NCMenuAction.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. class NCMenuAction {
  28. let title: String
  29. let boldTitle: Bool
  30. let details: String?
  31. let icon: UIImage
  32. let selectable: Bool
  33. var onTitle: String?
  34. var onIcon: UIImage?
  35. let destructive: Bool
  36. var selected: Bool = false
  37. var isOn: Bool = false
  38. var action: ((_ menuAction: NCMenuAction) -> Void)?
  39. var rowHeight: CGFloat { self.title == NCMenuAction.seperatorIdentifier ? NCMenuAction.seperatorHeight : self.details != nil ? 76 : 56 }
  40. var order: Int = 0
  41. init(title: String, boldTitle: Bool = false, destructive: Bool = false, details: String? = nil, icon: UIImage, order: Int = 0, action: ((_ menuAction: NCMenuAction) -> Void)?) {
  42. self.title = title
  43. self.boldTitle = boldTitle
  44. self.destructive = destructive
  45. self.details = details
  46. self.icon = icon
  47. self.action = action
  48. self.selectable = false
  49. self.order = order
  50. }
  51. 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)?) {
  52. self.title = title
  53. self.boldTitle = boldTitle
  54. self.destructive = destructive
  55. self.details = details
  56. self.icon = icon
  57. self.onTitle = onTitle ?? title
  58. self.onIcon = onIcon ?? icon
  59. self.action = action
  60. self.selected = selected
  61. self.isOn = on
  62. self.selectable = true
  63. self.order = order
  64. }
  65. }
  66. // MARK: - Actions
  67. extension NCMenuAction {
  68. static let seperatorIdentifier = "NCMenuAction.SEPARATOR"
  69. static let seperatorHeight: CGFloat = 0.5
  70. /// A static seperator, with no actions, text, or icons
  71. static func seperator(order: Int = 0) -> NCMenuAction {
  72. return NCMenuAction(title: seperatorIdentifier, icon: UIImage(), order: order, action: nil)
  73. }
  74. /// Select all items
  75. static func selectAllAction(action: @escaping () -> Void) -> NCMenuAction {
  76. NCMenuAction(
  77. title: NSLocalizedString("_select_all_", comment: ""),
  78. icon: NCUtility().loadImage(named: "checkmark.circle.fill", colors: [NCBrandColor.shared.iconImageColor]),
  79. action: { _ in action() }
  80. )
  81. }
  82. /// Cancel
  83. static func cancelAction(action: @escaping () -> Void) -> NCMenuAction {
  84. NCMenuAction(
  85. title: NSLocalizedString("_cancel_", comment: ""),
  86. icon: NCUtility().loadImage(named: "xmark", colors: [NCBrandColor.shared.iconImageColor]),
  87. action: { _ in action() }
  88. )
  89. }
  90. /// Delete files either from cache or from Nextcloud
  91. static func deleteAction(selectedMetadatas: [tableMetadata], metadataFolder: tableMetadata? = nil, controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  92. var titleDelete = NSLocalizedString("_delete_", comment: "")
  93. var message = NSLocalizedString("_want_delete_", comment: "")
  94. var icon = "trash"
  95. var destructive = false
  96. var color = NCBrandColor.shared.iconImageColor
  97. let permissions = NCPermissions()
  98. if selectedMetadatas.count > 1 {
  99. titleDelete = NSLocalizedString("_delete_selected_files_", comment: "")
  100. destructive = true
  101. } else if let metadata = selectedMetadatas.first {
  102. if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
  103. titleDelete = NSLocalizedString("_leave_share_", comment: "")
  104. message = NSLocalizedString("_want_leave_share_", comment: "")
  105. icon = "person.2.slash"
  106. } else if metadata.directory {
  107. titleDelete = NSLocalizedString("_delete_folder_", comment: "")
  108. destructive = true
  109. } else {
  110. titleDelete = NSLocalizedString("_delete_file_", comment: "")
  111. destructive = true
  112. }
  113. if let metadataFolder = metadataFolder {
  114. let isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder.permissions.contains(permissions.permissionShared)
  115. let isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder.permissions.contains(permissions.permissionMounted)
  116. if isShare || isMounted {
  117. titleDelete = NSLocalizedString("_leave_share_", comment: "")
  118. icon = "person.2.slash"
  119. }
  120. }
  121. } // else: no metadata selected
  122. let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock }
  123. var fileList = ""
  124. for (ix, metadata) in selectedMetadatas.enumerated() {
  125. guard ix < 3 else { fileList += "\n - ..."; break }
  126. fileList += "\n - " + metadata.fileNameView
  127. }
  128. if destructive { color = .red }
  129. return NCMenuAction(
  130. title: titleDelete,
  131. destructive: destructive,
  132. icon: NCUtility().loadImage(named: icon, colors: [color]),
  133. order: order,
  134. action: { _ in
  135. let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message + fileList, canDeleteServer: canDeleteServer, selectedMetadatas: selectedMetadatas, sceneIdentifier: controller?.sceneIdentifier) { _ in
  136. completion?()
  137. }
  138. controller?.present(alertController, animated: true, completion: nil)
  139. })
  140. }
  141. /// Open "share view" (activity VC) to open files in another app
  142. static func share(selectedMetadatas: [tableMetadata], controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  143. NCMenuAction(
  144. title: NSLocalizedString("_share_", comment: ""),
  145. icon: NCUtility().loadImage(named: "square.and.arrow.up", colors: [NCBrandColor.shared.iconImageColor]),
  146. order: order,
  147. action: { _ in
  148. NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas, controller: controller)
  149. completion?()
  150. }
  151. )
  152. }
  153. /// Set (or remove) a file as *available offline*. Downloads the file if not downloaded already
  154. static func setAvailableOfflineAction(selectedMetadatas: [tableMetadata], isAnyOffline: Bool, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  155. NCMenuAction(
  156. title: isAnyOffline ? NSLocalizedString("_remove_available_offline_", comment: "") : NSLocalizedString("_set_available_offline_", comment: ""),
  157. icon: NCUtility().loadImage(named: "icloud.and.arrow.down", colors: [NCBrandColor.shared.iconImageColor]),
  158. order: order,
  159. action: { _ in
  160. if !isAnyOffline, selectedMetadatas.count > 3 {
  161. let alert = UIAlertController(
  162. title: NSLocalizedString("_set_available_offline_", comment: ""),
  163. message: NSLocalizedString("_select_offline_warning_", comment: ""),
  164. preferredStyle: .alert)
  165. alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
  166. selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
  167. completion?()
  168. }))
  169. alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
  170. viewController.present(alert, animated: true)
  171. } else {
  172. selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
  173. completion?()
  174. }
  175. }
  176. )
  177. }
  178. /// Open view that lets the user move or copy the files within Nextcloud
  179. static func moveOrCopyAction(selectedMetadatas: [tableMetadata], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  180. NCMenuAction(
  181. title: NSLocalizedString("_move_or_copy_", comment: ""),
  182. icon: NCUtility().loadImage(named: "rectangle.portrait.and.arrow.right", colors: [NCBrandColor.shared.iconImageColor]),
  183. order: order,
  184. action: { _ in
  185. var fileNameError: NKError?
  186. for metadata in selectedMetadatas {
  187. if let sceneIdentifier = metadata.sceneIdentifier,
  188. let controller = SceneManager.shared.getController(sceneIdentifier: sceneIdentifier),
  189. let checkError = FileNameValidator.shared.checkFileName(metadata.fileNameView, account: controller.account) {
  190. fileNameError = checkError
  191. break
  192. }
  193. }
  194. if let fileNameError {
  195. viewController.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true, completion: nil)
  196. } else {
  197. let controller = viewController.tabBarController as? NCMainTabBarController
  198. NCActionCenter.shared.openSelectView(items: selectedMetadatas, controller: controller)
  199. completion?()
  200. }
  201. }
  202. )
  203. }
  204. /// Lock or unlock a file using *files_lock*
  205. static func lockUnlockFiles(shouldLock: Bool, metadatas: [tableMetadata], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
  206. let titleKey: String
  207. if metadatas.count == 1 {
  208. titleKey = shouldLock ? "_lock_file_" : "_unlock_file_"
  209. } else {
  210. titleKey = shouldLock ? "_lock_selected_files_" : "_unlock_selected_files_"
  211. }
  212. let imageName = !shouldLock ? "lock_open" : "lock"
  213. return NCMenuAction(
  214. title: NSLocalizedString(titleKey, comment: ""),
  215. icon: NCUtility().loadImage(named: imageName, colors: [NCBrandColor.shared.iconImageColor]),
  216. order: order,
  217. action: { _ in
  218. for metadata in metadatas where metadata.lock != shouldLock {
  219. NCNetworking.shared.lockUnlockFile(metadata, shoulLock: shouldLock)
  220. }
  221. completion?()
  222. }
  223. )
  224. }
  225. }