NCMenuAction.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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 details: String?
  31. let icon: UIImage
  32. let selectable: Bool
  33. var onTitle: String?
  34. var onIcon: UIImage?
  35. var selected: Bool = false
  36. var isOn: Bool = false
  37. var action: ((_ menuAction: NCMenuAction) -> Void)?
  38. var rowHeight: CGFloat { self.title == NCMenuAction.seperatorIdentifier ? NCMenuAction.seperatorHeight : self.details != nil ? 80 : 60 }
  39. var order: Int = 0
  40. init(title: String, details: String? = nil, icon: UIImage, order: Int = 0, action: ((_ menuAction: NCMenuAction) -> Void)?) {
  41. self.title = title
  42. self.details = details
  43. self.icon = icon
  44. self.action = action
  45. self.selectable = false
  46. self.order = order
  47. }
  48. init(title: String, details: String? = nil, icon: UIImage, onTitle: String? = nil, onIcon: UIImage? = nil, selected: Bool, on: Bool, order: Int = 0, action: ((_ menuAction: NCMenuAction) -> Void)?) {
  49. self.title = title
  50. self.details = details
  51. self.icon = icon
  52. self.onTitle = onTitle ?? title
  53. self.onIcon = onIcon ?? icon
  54. self.action = action
  55. self.selected = selected
  56. self.isOn = on
  57. self.selectable = true
  58. self.order = order
  59. }
  60. }
  61. // MARK: - Actions
  62. extension NCMenuAction {
  63. static let seperatorIdentifier = "NCMenuAction.SEPARATOR"
  64. static let seperatorHeight: CGFloat = 0.5
  65. /// A static seperator, with no actions, text, or icons
  66. static func seperator(order: Int = 0) -> NCMenuAction {
  67. return NCMenuAction(title: seperatorIdentifier, icon: UIImage(), order: order, action: nil)
  68. }
  69. /// Select all items
  70. static func selectAllAction(action: @escaping () -> Void) -> NCMenuAction {
  71. NCMenuAction(
  72. title: NSLocalizedString("_select_all_", comment: ""),
  73. icon: NCUtility.shared.loadImage(named: "checkmark.circle.fill"),
  74. action: { _ in action() }
  75. )
  76. }
  77. /// Cancel
  78. static func cancelAction(action: @escaping () -> Void) -> NCMenuAction {
  79. NCMenuAction(
  80. title: NSLocalizedString("_cancel_", comment: ""),
  81. icon: NCUtility.shared.loadImage(named: "xmark"),
  82. action: { _ in action() }
  83. )
  84. }
  85. /// Copy files to pasteboard
  86. static func copyAction(selectOcId: [String], hudView: UIView, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  87. NCMenuAction(
  88. title: NSLocalizedString("_copy_file_", comment: ""),
  89. icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
  90. order: order,
  91. action: { _ in
  92. NCActionCenter.shared.copyPasteboard(pasteboardOcIds: selectOcId, hudView: hudView)
  93. completion?()
  94. }
  95. )
  96. }
  97. /// Delete files either from cache or from Nextcloud
  98. static func deleteAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], metadataFolder: tableMetadata? = nil, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  99. var titleDelete = NSLocalizedString("_delete_", comment: "")
  100. if selectedMetadatas.count > 1 {
  101. titleDelete = NSLocalizedString("_delete_selected_files_", comment: "")
  102. } else if let metadata = selectedMetadatas.first {
  103. if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
  104. titleDelete = NSLocalizedString("_leave_share_", comment: "")
  105. } else if metadata.directory {
  106. titleDelete = NSLocalizedString("_delete_folder_", comment: "")
  107. } else {
  108. titleDelete = NSLocalizedString("_delete_file_", comment: "")
  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. }
  116. }
  117. } // else: no metadata selected
  118. let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock }
  119. var fileList = ""
  120. for (ix, metadata) in selectedMetadatas.enumerated() {
  121. guard ix < 3 else { fileList += "\n - ..."; break }
  122. fileList += "\n - " + metadata.fileNameView
  123. }
  124. return NCMenuAction(
  125. title: titleDelete,
  126. icon: NCUtility.shared.loadImage(named: "trash"),
  127. order: order,
  128. action: { _ in
  129. let alertController = UIAlertController(
  130. title: titleDelete,
  131. message: NSLocalizedString("_want_delete_", comment: "") + fileList,
  132. preferredStyle: .alert)
  133. if canDeleteServer {
  134. alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
  135. let hud = JGProgressHUD()
  136. hud.textLabel.text = NSLocalizedString("_deletion_progess_", comment: "")
  137. if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
  138. let view = appDelegate.window?.rootViewController?.view {
  139. hud.show(in: view)
  140. }
  141. Task {
  142. var error = NKError()
  143. var ocId: [String] = []
  144. let account = selectedMetadatas.first?.account ?? ""
  145. for metadata in selectedMetadatas where error == .success {
  146. error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
  147. if error == .success {
  148. ocId.append(metadata.ocId)
  149. }
  150. }
  151. NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": false, "error": error, "hud": hud])
  152. }
  153. completion?()
  154. })
  155. }
  156. // NCMedia removes image from collection view if removed from cache
  157. if !(viewController is NCMedia) {
  158. alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
  159. Task {
  160. var error = NKError()
  161. var ocId: [String] = []
  162. let account = selectedMetadatas.first?.account ?? ""
  163. for metadata in selectedMetadatas where error == .success {
  164. error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
  165. if error == .success {
  166. ocId.append(metadata.ocId)
  167. }
  168. }
  169. if error != .success {
  170. NCContentPresenter.shared.showError(error: error)
  171. }
  172. NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": true, "error": error])
  173. }
  174. completion?()
  175. })
  176. }
  177. alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
  178. viewController.present(alertController, animated: true, completion: nil)
  179. }
  180. )
  181. }
  182. /// Open "share view" (activity VC) to open files in another app
  183. static func openInAction(selectedMetadatas: [tableMetadata], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  184. NCMenuAction(
  185. title: NSLocalizedString("_open_in_", comment: ""),
  186. icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
  187. order: order,
  188. action: { _ in
  189. NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
  190. completion?()
  191. }
  192. )
  193. }
  194. /// Save selected files to user's photo library
  195. static func saveMediaAction(selectedMediaMetadatas: [tableMetadata], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  196. var title: String = NSLocalizedString("_save_selected_files_", comment: "")
  197. var icon = NCUtility.shared.loadImage(named: "square.and.arrow.down")
  198. if selectedMediaMetadatas.allSatisfy({ NCManageDatabase.shared.getMetadataLivePhoto(metadata: $0) != nil }) {
  199. title = NSLocalizedString("_livephoto_save_", comment: "")
  200. icon = NCUtility.shared.loadImage(named: "livephoto")
  201. }
  202. return NCMenuAction(
  203. title: title,
  204. icon: icon,
  205. order: order,
  206. action: { _ in
  207. for metadata in selectedMediaMetadatas {
  208. if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
  209. NCActionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
  210. } else {
  211. if CCUtility.fileProviderStorageExists(metadata) {
  212. NCActionCenter.shared.saveAlbum(metadata: metadata)
  213. } else {
  214. NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
  215. }
  216. }
  217. }
  218. completion?()
  219. }
  220. )
  221. }
  222. /// Set (or remove) a file as *available offline*. Downloads the file if not downloaded already
  223. static func setAvailableOfflineAction(selectedMetadatas: [tableMetadata], isAnyOffline: Bool, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  224. NCMenuAction(
  225. title: isAnyOffline ? NSLocalizedString("_remove_available_offline_", comment: "") : NSLocalizedString("_set_available_offline_", comment: ""),
  226. icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
  227. order: order,
  228. action: { _ in
  229. if !isAnyOffline, selectedMetadatas.count > 3 {
  230. let alert = UIAlertController(
  231. title: NSLocalizedString("_set_available_offline_", comment: ""),
  232. message: NSLocalizedString("_select_offline_warning_", comment: ""),
  233. preferredStyle: .alert)
  234. alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
  235. selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
  236. completion?()
  237. }))
  238. alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
  239. viewController.present(alert, animated: true)
  240. } else {
  241. selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
  242. completion?()
  243. }
  244. }
  245. )
  246. }
  247. /// Open view that lets the user move or copy the files within Nextcloud
  248. static func moveOrCopyAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  249. NCMenuAction(
  250. title: NSLocalizedString("_move_or_copy_selected_files_", comment: ""),
  251. icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
  252. order: order,
  253. action: { _ in
  254. NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: indexPath)
  255. completion?()
  256. }
  257. )
  258. }
  259. /// Open AirPrint view to print a single file
  260. static func printAction(metadata: tableMetadata, order: Int = 0) -> NCMenuAction {
  261. NCMenuAction(
  262. title: NSLocalizedString("_print_", comment: ""),
  263. icon: NCUtility.shared.loadImage(named: "printer"),
  264. order: order,
  265. action: { _ in
  266. if CCUtility.fileProviderStorageExists(metadata) {
  267. NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": NCGlobal.shared.selectorPrint, "error": NKError(), "account": metadata.account])
  268. } else {
  269. NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorPrint) { _, _ in }
  270. }
  271. }
  272. )
  273. }
  274. /// Lock or unlock a file using *files_lock*
  275. static func lockUnlockFiles(shouldLock: Bool, metadatas: [tableMetadata], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  276. let titleKey: String
  277. if metadatas.count == 1 {
  278. titleKey = shouldLock ? "_lock_file_" : "_unlock_file_"
  279. } else {
  280. titleKey = shouldLock ? "_lock_selected_files_" : "_unlock_selected_files_"
  281. }
  282. let imageName = !shouldLock ? "lock_open" : "lock"
  283. return NCMenuAction(
  284. title: NSLocalizedString(titleKey, comment: ""),
  285. icon: NCUtility.shared.loadImage(named: imageName),
  286. order: order,
  287. action: { _ in
  288. for metadata in metadatas where metadata.lock != shouldLock {
  289. NCNetworking.shared.lockUnlockFile(metadata, shoulLock: shouldLock)
  290. }
  291. completion?()
  292. }
  293. )
  294. }
  295. }