NCUploadAssetsView.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. let utilityFileSystem = NCUtilityFileSystem()
  24. @Environment(\.presentationMode) var presentationMode
  25. var body: some View {
  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, account: model.controller?.account) {
  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.getElement(account: metadata?.account))))
  118. if model.useAutoUploadFolder {
  119. Toggle(isOn: $model.useAutoUploadSubFolder, label: {
  120. Text(NSLocalizedString("_autoupload_create_subfolder_", comment: ""))
  121. .font(.system(size: 15))
  122. })
  123. .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.getElement(account: metadata?.account))))
  124. }
  125. if !model.useAutoUploadFolder {
  126. HStack {
  127. Label {
  128. if utilityFileSystem.getHomeServer(session: model.session) == 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.getElement(account: metadata?.account)))
  143. }
  144. }
  145. .contentShape(Rectangle())
  146. .onTapGesture {
  147. showSelect = true
  148. }
  149. }
  150. }
  151. Section {
  152. Button(NSLocalizedString("_save_", comment: "")) {
  153. if model.useAutoUploadFolder, model.useAutoUploadSubFolder {
  154. model.showHUD = true
  155. }
  156. model.uploadInProgress.toggle()
  157. model.save { metadatasNOConflict, metadatasUploadInConflict in
  158. if metadatasUploadInConflict.isEmpty {
  159. model.dismissCreateFormUploadConflict(metadatas: metadatasNOConflict)
  160. } else {
  161. model.metadatasNOConflict = metadatasNOConflict
  162. model.metadatasUploadInConflict = metadatasUploadInConflict
  163. showUploadConflict = true
  164. }
  165. }
  166. }
  167. .frame(maxWidth: .infinity)
  168. .buttonStyle(ButtonRounded(disabled: model.uploadInProgress, account: model.session.account))
  169. .listRowBackground(Color(UIColor.systemGroupedBackground))
  170. .disabled(model.uploadInProgress)
  171. .hiddenConditionally(isHidden: model.hiddenSave)
  172. }
  173. }
  174. }
  175. .navigationTitle(NSLocalizedString("_upload_photos_videos_", comment: ""))
  176. .navigationBarTitleDisplayMode(.inline)
  177. .navigationBarItems(trailing: Button(action: {
  178. model.dismissView = true
  179. }) {
  180. Image(systemName: "xmark")
  181. .font(Font.system(.body).weight(.light))
  182. .foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
  183. })
  184. NCHUDView(showHUD: $model.showHUD, textLabel: NSLocalizedString("_wait_", comment: ""), image: "doc.badge.arrow.up", color: NCBrandColor.shared.getElement(account: model.session.account))
  185. .offset(y: model.showHUD ? 5 : -200)
  186. .animation(.easeOut, value: model.showHUD)
  187. }
  188. .navigationViewStyle(StackNavigationViewStyle())
  189. .sheet(isPresented: $showSelect) {
  190. SelectView(serverUrl: $model.serverUrl, session: model.session)
  191. }
  192. .sheet(isPresented: $showUploadConflict) {
  193. UploadConflictView(delegate: model, serverUrl: model.serverUrl, metadatasUploadInConflict: model.metadatasUploadInConflict, metadatasNOConflict: model.metadatasNOConflict)
  194. }
  195. .fullScreenCover(isPresented: $showQuickLook) {
  196. NCViewerQuickLookView(url: URL(fileURLWithPath: fileNamePath), index: $index, isPresentedQuickLook: $showQuickLook, model: model)
  197. .ignoresSafeArea()
  198. }
  199. .onReceive(model.$dismissView) { newValue in
  200. if newValue {
  201. presentationMode.wrappedValue.dismiss()
  202. }
  203. }
  204. .onDisappear {
  205. model.dismissView = true
  206. }
  207. }
  208. struct ImageAsset: View {
  209. @ObservedObject var model: NCUploadAssetsModel
  210. @State var index: Int
  211. var body: some View {
  212. ZStack(alignment: .bottomTrailing) {
  213. if index < model.previewStore.count {
  214. let item = model.previewStore[index]
  215. Image(uiImage: item.image)
  216. .resizable()
  217. .aspectRatio(contentMode: .fill)
  218. .frame(width: 80, height: 80, alignment: .center)
  219. .cornerRadius(10)
  220. if item.assetType == .livePhoto && item.data == nil {
  221. Image(systemName: "livephoto")
  222. .resizable()
  223. .scaledToFit()
  224. .frame(width: 15, height: 15)
  225. .foregroundColor(.white)
  226. .padding(.horizontal, 5)
  227. .padding(.vertical, 5)
  228. } else if item.assetType == .video {
  229. Image(systemName: "video.fill")
  230. .resizable()
  231. .scaledToFit()
  232. .frame(width: 15, height: 15)
  233. .foregroundColor(.white)
  234. .padding(.horizontal, 5)
  235. .padding(.vertical, 5)
  236. }
  237. }
  238. }
  239. }
  240. }
  241. }
  242. #Preview {
  243. NCUploadAssetsView(model: NCUploadAssetsModel(assets: [], serverUrl: "/", controller: nil))
  244. }