NCUploadAssetsView.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. //
  2. // NCUploadAssetsView.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 03/06/24.
  6. // Copyright © 2024 Marino Faggiana. All rights reserved.
  7. //
  8. import SwiftUI
  9. import NextcloudKit
  10. struct NCUploadAssetsView: View {
  11. @ObservedObject var model: NCUploadAssetsModel
  12. @State private var showSelect = false
  13. @State private var showUploadConflict = false
  14. @State private var showQuickLook = false
  15. @State private var showRenameAlert = false
  16. @State private var renameError = ""
  17. @State private var renameFileName: String = ""
  18. @State private var renameIndex: Int = 0
  19. @State private var index: Int = 0
  20. var metadata: tableMetadata?
  21. let gridItems: [GridItem] = [GridItem()]
  22. let fileNamePath = NSTemporaryDirectory() + "Photo.jpg"
  23. @Environment(\.presentationMode) var presentationMode
  24. var body: some View {
  25. let utilityFileSystem = NCUtilityFileSystem()
  26. NavigationView {
  27. ZStack(alignment: .top) {
  28. List {
  29. Section(footer: Text(NSLocalizedString("_modify_image_desc_", comment: ""))) {
  30. ScrollView(.horizontal) {
  31. LazyHGrid(rows: gridItems, alignment: .center, spacing: 10) {
  32. ForEach(0..<model.previewStore.count, id: \.self) { index in
  33. let item = model.previewStore[index]
  34. Menu {
  35. Button(action: {
  36. renameFileName = model.previewStore[index].fileName
  37. renameIndex = index
  38. showRenameAlert = true
  39. }) {
  40. Label(NSLocalizedString("_rename_", comment: ""), systemImage: "pencil")
  41. }
  42. if item.asset.type == .photo || item.asset.type == .livePhoto {
  43. Button(action: {
  44. if model.presentedQuickLook(index: index, fileNamePath: fileNamePath) {
  45. self.index = index
  46. showQuickLook = true
  47. }
  48. }) {
  49. Label(NSLocalizedString("_modify_", comment: ""), systemImage: "pencil.tip.crop.circle")
  50. }
  51. }
  52. if item.data != nil {
  53. Button(action: {
  54. if let image = model.previewStore[index].asset.fullResolutionImage?.resizeImage(size: CGSize(width: 300, height: 300), isAspectRation: true) {
  55. model.previewStore[index].image = image
  56. model.previewStore[index].data = nil
  57. model.previewStore[index].assetType = model.previewStore[index].asset.type
  58. }
  59. }) {
  60. Label(NSLocalizedString("_undo_modify_", comment: ""), systemImage: "arrow.uturn.backward.circle")
  61. }
  62. }
  63. if item.data == nil && item.asset.type == .livePhoto && item.assetType == .livePhoto {
  64. Button(action: {
  65. model.previewStore[index].assetType = .photo
  66. }) {
  67. Label(NSLocalizedString("_disable_livephoto_", comment: ""), systemImage: "livephoto.slash")
  68. }
  69. } else if item.data == nil && item.asset.type == .livePhoto && item.assetType == .photo {
  70. Button(action: {
  71. model.previewStore[index].assetType = .livePhoto
  72. }) {
  73. Label(NSLocalizedString("_enable_livephoto_", comment: ""), systemImage: "livephoto")
  74. }
  75. }
  76. Button(role: .destructive, action: {
  77. model.deleteAsset(index: index)
  78. }) {
  79. Label(NSLocalizedString("_remove_", comment: ""), systemImage: "trash")
  80. }
  81. } label: {
  82. ImageAsset(model: model, index: index)
  83. .alert(NSLocalizedString("_rename_", comment: ""), isPresented: $showRenameAlert) {
  84. TextField("", text: $renameFileName)
  85. .autocapitalization(.none)
  86. .autocorrectionDisabled()
  87. Button(NSLocalizedString("_rename_", comment: ""), action: {
  88. if !renameError.isEmpty {
  89. DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
  90. showRenameAlert = true
  91. }
  92. } else {
  93. model.previewStore[renameIndex].fileName = renameFileName.trimmingCharacters(in: .whitespacesAndNewlines)
  94. }
  95. })
  96. Button(NSLocalizedString("_cancel_", comment: ""), role: .cancel, action: {})
  97. } message: {
  98. Text(renameError)
  99. }
  100. }
  101. .onChange(of: renameFileName) { newValue in
  102. if let error = FileNameValidator.shared.checkFileName(newValue) {
  103. renameError = error.errorDescription
  104. } else {
  105. renameError = ""
  106. }
  107. }
  108. }
  109. }
  110. }
  111. }
  112. Section {
  113. Toggle(isOn: $model.useAutoUploadFolder, label: {
  114. Text(NSLocalizedString("_use_folder_auto_upload_", comment: ""))
  115. .font(.system(size: 15))
  116. })
  117. .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brandElement)))
  118. if model.useAutoUploadFolder {
  119. Toggle(isOn: $model.useAutoUploadFolder, label: {
  120. Text(NSLocalizedString("_autoupload_create_subfolder_", comment: ""))
  121. .font(.system(size: 15))
  122. })
  123. .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brandElement)))
  124. }
  125. if !model.useAutoUploadFolder {
  126. HStack {
  127. Label {
  128. if utilityFileSystem.getHomeServer(urlBase: model.userBaseUrl.urlBase, userId: model.userBaseUrl.userId) == model.serverUrl {
  129. Text("/")
  130. .font(.system(size: 15))
  131. .frame(maxWidth: .infinity, alignment: .trailing)
  132. } else {
  133. Text(model.getTextServerUrl())
  134. .font(.system(size: 15))
  135. .frame(maxWidth: .infinity, alignment: .trailing)
  136. }
  137. } icon: {
  138. Image("folder")
  139. .renderingMode(.template)
  140. .resizable()
  141. .scaledToFit()
  142. .foregroundColor(Color(NCBrandColor.shared.brandElement))
  143. }
  144. }
  145. .contentShape(Rectangle())
  146. .onTapGesture {
  147. showSelect = true
  148. }
  149. }
  150. }
  151. Button(NSLocalizedString("_save_", comment: "")) {
  152. if model.useAutoUploadFolder, model.useAutoUploadSubFolder {
  153. model.showHUD = true
  154. }
  155. model.uploadInProgress.toggle()
  156. model.save { metadatasNOConflict, metadatasUploadInConflict in
  157. if metadatasUploadInConflict.isEmpty {
  158. model.dismissCreateFormUploadConflict(metadatas: metadatasNOConflict)
  159. } else {
  160. model.metadatasNOConflict = metadatasNOConflict
  161. model.metadatasUploadInConflict = metadatasUploadInConflict
  162. showUploadConflict = true
  163. }
  164. }
  165. }
  166. .frame(maxWidth: .infinity)
  167. .buttonStyle(ButtonRounded(disabled: model.uploadInProgress))
  168. .listRowBackground(Color(UIColor.systemGroupedBackground))
  169. .disabled(model.uploadInProgress)
  170. .hiddenConditionally(isHidden: model.hiddenSave)
  171. }
  172. .navigationTitle(NSLocalizedString("_upload_photos_videos_", comment: ""))
  173. .navigationBarTitleDisplayMode(.inline)
  174. .navigationBarItems(trailing: Button(action: {
  175. presentationMode.wrappedValue.dismiss()
  176. }) {
  177. Image(systemName: "xmark")
  178. .font(Font.system(.body).weight(.light))
  179. .foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
  180. })
  181. HUDView(showHUD: $model.showHUD, textLabel: NSLocalizedString("_wait_", comment: ""), image: "doc.badge.arrow.up")
  182. .offset(y: model.showHUD ? 5 : -200)
  183. .animation(.easeOut, value: model.showHUD)
  184. }
  185. }
  186. .navigationViewStyle(StackNavigationViewStyle())
  187. .sheet(isPresented: $showSelect) {
  188. SelectView(serverUrl: $model.serverUrl)
  189. }
  190. .sheet(isPresented: $showUploadConflict) {
  191. UploadConflictView(delegate: model, serverUrl: model.serverUrl, metadatasUploadInConflict: model.metadatasUploadInConflict, metadatasNOConflict: model.metadatasNOConflict)
  192. }
  193. .fullScreenCover(isPresented: $showQuickLook) {
  194. NCViewerQuickLookView(url: URL(fileURLWithPath: fileNamePath), index: $index, isPresentedQuickLook: $showQuickLook, model: model)
  195. .ignoresSafeArea()
  196. }
  197. .onReceive(model.$dismissView) { newValue in
  198. if newValue {
  199. presentationMode.wrappedValue.dismiss()
  200. }
  201. }
  202. .onTapGesture {
  203. SceneManager.shared.getWindow(controller: model.controller)?.endEditing(true)
  204. }
  205. .onDisappear {
  206. model.dismissView = true
  207. }
  208. }
  209. struct ImageAsset: View {
  210. @ObservedObject var model: NCUploadAssetsModel
  211. @State var index: Int
  212. var body: some View {
  213. ZStack(alignment: .bottomTrailing) {
  214. if index < model.previewStore.count {
  215. let item = model.previewStore[index]
  216. Image(uiImage: item.image)
  217. .resizable()
  218. .aspectRatio(contentMode: .fill)
  219. .frame(width: 80, height: 80, alignment: .center)
  220. .cornerRadius(10)
  221. if item.assetType == .livePhoto && item.data == nil {
  222. Image(systemName: "livephoto")
  223. .resizable()
  224. .scaledToFit()
  225. .frame(width: 15, height: 15)
  226. .foregroundColor(.white)
  227. .padding(.horizontal, 5)
  228. .padding(.vertical, 5)
  229. } else if item.assetType == .video {
  230. Image(systemName: "video.fill")
  231. .resizable()
  232. .scaledToFit()
  233. .frame(width: 15, height: 15)
  234. .foregroundColor(.white)
  235. .padding(.horizontal, 5)
  236. .padding(.vertical, 5)
  237. }
  238. }
  239. }
  240. }
  241. }
  242. }
  243. #Preview {
  244. NCUploadAssetsView(model: NCUploadAssetsModel(assets: [], serverUrl: "/", userBaseUrl: (UIApplication.shared.delegate as? AppDelegate)!, controller: nil))
  245. }