NCMenuAction.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //
  2. // NCMenuAction.swift
  3. // Nextcloud
  4. //
  5. // Created by Henrik Storch on 17.02.22.
  6. // Copyright © 2022 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Henrik Storch <henrik.storch@nextcloud.com>
  9. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  10. //
  11. // This program is free software: you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published by
  13. // the Free Software Foundation, either version 3 of the License, or
  14. // (at your option) any later version.
  15. //
  16. // This program is distributed in the hope that it will be useful,
  17. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. // GNU General Public License for more details.
  20. //
  21. // You should have received a copy of the GNU General Public License
  22. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. //
  24. import Foundation
  25. import UIKit
  26. import NextcloudKit
  27. import JGProgressHUD
  28. class NCMenuAction {
  29. let title: String
  30. let boldTitle: Bool
  31. let details: String?
  32. let icon: UIImage
  33. let selectable: Bool
  34. var onTitle: String?
  35. var onIcon: UIImage?
  36. let destructive: Bool
  37. var selected: Bool = false
  38. var isOn: Bool = false
  39. var action: ((_ menuAction: NCMenuAction) -> Void)?
  40. var rowHeight: CGFloat { self.title == NCMenuAction.seperatorIdentifier ? NCMenuAction.seperatorHeight : self.details != nil ? 76 : 56 }
  41. var order: Int = 0
  42. init(title: String, boldTitle: Bool = false, destructive: Bool = false, details: String? = nil, icon: UIImage, order: Int = 0, action: ((_ menuAction: NCMenuAction) -> Void)?) {
  43. self.title = title
  44. self.boldTitle = boldTitle
  45. self.destructive = destructive
  46. self.details = details
  47. self.icon = icon
  48. self.action = action
  49. self.selectable = false
  50. self.order = order
  51. }
  52. init(title: String, boldTitle: Bool = false, destructive: Bool = false, details: String? = nil, icon: UIImage, onTitle: String? = nil, onIcon: UIImage? = nil, selected: Bool, on: Bool, order: Int = 0, action: ((_ menuAction: NCMenuAction) -> Void)?) {
  53. self.title = title
  54. self.boldTitle = boldTitle
  55. self.destructive = destructive
  56. self.details = details
  57. self.icon = icon
  58. self.onTitle = onTitle ?? title
  59. self.onIcon = onIcon ?? icon
  60. self.action = action
  61. self.selected = selected
  62. self.isOn = on
  63. self.selectable = true
  64. self.order = order
  65. }
  66. }
  67. // MARK: - Actions
  68. extension NCMenuAction {
  69. static let seperatorIdentifier = "NCMenuAction.SEPARATOR"
  70. static let seperatorHeight: CGFloat = 0.5
  71. /// A static seperator, with no actions, text, or icons
  72. static func seperator(order: Int = 0) -> NCMenuAction {
  73. return NCMenuAction(title: seperatorIdentifier, icon: UIImage(), order: order, action: nil)
  74. }
  75. /// Select all items
  76. static func selectAllAction(action: @escaping () -> Void) -> NCMenuAction {
  77. NCMenuAction(
  78. title: NSLocalizedString("_select_all_", comment: ""),
  79. icon: NCUtility().loadImage(named: "checkmark.circle.fill"),
  80. action: { _ in action() }
  81. )
  82. }
  83. /// Cancel
  84. static func cancelAction(action: @escaping () -> Void) -> NCMenuAction {
  85. NCMenuAction(
  86. title: NSLocalizedString("_cancel_", comment: ""),
  87. icon: NCUtility().loadImage(named: "xmark"),
  88. action: { _ in action() }
  89. )
  90. }
  91. /// Delete files either from cache or from Nextcloud
  92. static func deleteAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], metadataFolder: tableMetadata? = nil, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  93. var titleDelete = NSLocalizedString("_delete_", comment: "")
  94. var icon = "trash"
  95. var destructive = false
  96. if selectedMetadatas.count > 1 {
  97. titleDelete = NSLocalizedString("_delete_selected_files_", comment: "")
  98. destructive = true
  99. } else if let metadata = selectedMetadatas.first {
  100. if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
  101. titleDelete = NSLocalizedString("_leave_share_", comment: "")
  102. icon = "person.2.slash"
  103. } else if metadata.directory {
  104. titleDelete = NSLocalizedString("_delete_folder_", comment: "")
  105. destructive = true
  106. } else {
  107. titleDelete = NSLocalizedString("_delete_file_", comment: "")
  108. destructive = true
  109. }
  110. if let metadataFolder = metadataFolder {
  111. let isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
  112. let isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
  113. if isShare || isMounted {
  114. titleDelete = NSLocalizedString("_leave_share_", comment: "")
  115. icon = "person.2.slash"
  116. }
  117. }
  118. } // else: no metadata selected
  119. let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock }
  120. var fileList = ""
  121. for (ix, metadata) in selectedMetadatas.enumerated() {
  122. guard ix < 3 else { fileList += "\n - ..."; break }
  123. fileList += "\n - " + metadata.fileNameView
  124. }
  125. return NCMenuAction(
  126. title: titleDelete,
  127. destructive: destructive,
  128. icon: NCUtility().loadImage(named: icon),
  129. order: order,
  130. action: { _ in
  131. let alertController = UIAlertController(
  132. title: titleDelete,
  133. message: NSLocalizedString("_want_delete_", comment: "") + fileList,
  134. preferredStyle: .alert)
  135. if canDeleteServer {
  136. alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
  137. Task {
  138. var error = NKError()
  139. var ocId: [String] = []
  140. for metadata in selectedMetadatas where error == .success {
  141. error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
  142. if error == .success {
  143. ocId.append(metadata.ocId)
  144. }
  145. }
  146. NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": false, "error": error])
  147. }
  148. completion?()
  149. })
  150. }
  151. // NCMedia removes image from collection view if removed from cache
  152. if !(viewController is NCMedia) {
  153. alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
  154. Task {
  155. var error = NKError()
  156. var ocId: [String] = []
  157. for metadata in selectedMetadatas where error == .success {
  158. error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
  159. if error == .success {
  160. ocId.append(metadata.ocId)
  161. }
  162. }
  163. if error != .success {
  164. NCContentPresenter().showError(error: error)
  165. }
  166. NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": true, "error": error])
  167. }
  168. completion?()
  169. })
  170. }
  171. alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
  172. viewController.present(alertController, animated: true, completion: nil)
  173. }
  174. )
  175. }
  176. /// Open "share view" (activity VC) to open files in another app
  177. static func share(selectedMetadatas: [tableMetadata], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  178. NCMenuAction(
  179. title: NSLocalizedString("_share_", comment: ""),
  180. icon: NCUtility().loadImage(named: "square.and.arrow.up"),
  181. order: order,
  182. action: { _ in
  183. NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
  184. completion?()
  185. }
  186. )
  187. }
  188. /// Save selected files to user's photo library
  189. static func saveMediaAction(selectedMediaMetadatas: [tableMetadata], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  190. var title: String = NSLocalizedString("_save_selected_files_", comment: "")
  191. var icon = NCUtility().loadImage(named: "square.and.arrow.down")
  192. if selectedMediaMetadatas.allSatisfy({ NCManageDatabase.shared.getMetadataLivePhoto(metadata: $0) != nil }) {
  193. title = NSLocalizedString("_livephoto_save_", comment: "")
  194. icon = NCUtility().loadImage(named: "livephoto")
  195. }
  196. return NCMenuAction(
  197. title: title,
  198. icon: icon,
  199. order: order,
  200. action: { _ in
  201. for metadata in selectedMediaMetadatas {
  202. if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
  203. NCNetworking.shared.saveLivePhotoQueue.addOperation(NCOperationSaveLivePhoto(metadata: metadata, metadataMOV: metadataMOV))
  204. } else {
  205. if NCUtilityFileSystem().fileProviderStorageExists(metadata) {
  206. NCActionCenter.shared.saveAlbum(metadata: metadata)
  207. } else {
  208. if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty {
  209. NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum))
  210. }
  211. }
  212. }
  213. }
  214. completion?()
  215. }
  216. )
  217. }
  218. /// Set (or remove) a file as *available offline*. Downloads the file if not downloaded already
  219. static func setAvailableOfflineAction(selectedMetadatas: [tableMetadata], isAnyOffline: Bool, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  220. NCMenuAction(
  221. title: isAnyOffline ? NSLocalizedString("_remove_available_offline_", comment: "") : NSLocalizedString("_set_available_offline_", comment: ""),
  222. icon: NCUtility().loadImage(named: "icloud.and.arrow.down"),
  223. order: order,
  224. action: { _ in
  225. if !isAnyOffline, selectedMetadatas.count > 3 {
  226. let alert = UIAlertController(
  227. title: NSLocalizedString("_set_available_offline_", comment: ""),
  228. message: NSLocalizedString("_select_offline_warning_", comment: ""),
  229. preferredStyle: .alert)
  230. alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
  231. selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
  232. completion?()
  233. }))
  234. alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
  235. viewController.present(alert, animated: true)
  236. } else {
  237. selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
  238. completion?()
  239. }
  240. }
  241. )
  242. }
  243. /// Open view that lets the user move or copy the files within Nextcloud
  244. static func moveOrCopyAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  245. NCMenuAction(
  246. title: NSLocalizedString("_move_or_copy_", comment: ""),
  247. icon: NCUtility().loadImage(named: "rectangle.portrait.and.arrow.right"),
  248. order: order,
  249. action: { _ in
  250. NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: indexPath)
  251. completion?()
  252. }
  253. )
  254. }
  255. /// Lock or unlock a file using *files_lock*
  256. static func lockUnlockFiles(shouldLock: Bool, metadatas: [tableMetadata], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  257. let titleKey: String
  258. if metadatas.count == 1 {
  259. titleKey = shouldLock ? "_lock_file_" : "_unlock_file_"
  260. } else {
  261. titleKey = shouldLock ? "_lock_selected_files_" : "_unlock_selected_files_"
  262. }
  263. let imageName = !shouldLock ? "lock_open" : "lock"
  264. return NCMenuAction(
  265. title: NSLocalizedString(titleKey, comment: ""),
  266. icon: NCUtility().loadImage(named: imageName),
  267. order: order,
  268. action: { _ in
  269. for metadata in metadatas where metadata.lock != shouldLock {
  270. NCNetworking.shared.lockUnlockFile(metadata, shoulLock: shouldLock)
  271. }
  272. completion?()
  273. }
  274. )
  275. }
  276. }