NCShareCells.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //
  2. // NCShareCells.swift
  3. // Nextcloud
  4. //
  5. // Created by Henrik Storch on 18.03.22.
  6. // Copyright © 2022 Henrik Storch. All rights reserved.
  7. //
  8. // Author Henrik Storch <henrik.storch@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import UIKit
  24. protocol NCShareCellConfig {
  25. var title: String { get }
  26. func getCell(for share: NCTableShareable) -> UITableViewCell
  27. func didSelect(for share: NCTableShareable)
  28. }
  29. protocol NCToggleCellConfig: NCShareCellConfig {
  30. func isOn(for share: NCTableShareable) -> Bool
  31. func didChange(_ share: NCTableShareable, to newValue: Bool)
  32. }
  33. extension NCToggleCellConfig {
  34. func getCell(for share: NCTableShareable) -> UITableViewCell {
  35. return NCShareToggleCell(isOn: isOn(for: share))
  36. }
  37. func didSelect(for share: NCTableShareable) {
  38. didChange(share, to: !isOn(for: share))
  39. }
  40. }
  41. protocol NCPermission: NCToggleCellConfig {
  42. static var forDirectory: [Self] { get }
  43. static var forFile: [Self] { get }
  44. func hasResharePermission(for parentPermission: Int) -> Bool
  45. }
  46. enum NCUserPermission: CaseIterable, NCPermission {
  47. func hasResharePermission(for parentPermission: Int) -> Bool {
  48. return ((permissionBitFlag & parentPermission) != 0)
  49. }
  50. var permissionBitFlag: Int {
  51. switch self {
  52. case .reshare: return NCGlobal.shared.permissionShareShare
  53. case .edit: return NCGlobal.shared.permissionUpdateShare
  54. case .create: return NCGlobal.shared.permissionCreateShare
  55. case .delete: return NCGlobal.shared.permissionDeleteShare
  56. }
  57. }
  58. func didChange(_ share: NCTableShareable, to newValue: Bool) {
  59. share.permissions ^= permissionBitFlag
  60. }
  61. func isOn(for share: NCTableShareable) -> Bool {
  62. return (share.permissions & permissionBitFlag) != 0
  63. }
  64. case reshare, edit, create, delete
  65. static let forDirectory: [NCUserPermission] = NCUserPermission.allCases
  66. static let forFile: [NCUserPermission] = [.reshare, .edit]
  67. var title: String {
  68. switch self {
  69. case .reshare: return NSLocalizedString("_share_can_reshare_", comment: "")
  70. case .edit: return NSLocalizedString("_share_can_change_", comment: "")
  71. case .create: return NSLocalizedString("_share_can_create_", comment: "")
  72. case .delete: return NSLocalizedString("_share_can_delete_", comment: "")
  73. }
  74. }
  75. }
  76. enum NCLinkPermission: NCPermission {
  77. func didChange(_ share: NCTableShareable, to newValue: Bool) {
  78. guard self != .allowEdit || newValue else {
  79. share.permissions = NCGlobal.shared.permissionReadShare
  80. return
  81. }
  82. share.permissions = permissionValue
  83. }
  84. func hasResharePermission(for parentPermission: Int) -> Bool {
  85. permissionValue & parentPermission == permissionValue
  86. }
  87. var permissionValue: Int {
  88. switch self {
  89. case .allowEdit:
  90. return CCUtility.getPermissionsValue(
  91. byCanEdit: true,
  92. andCanCreate: true,
  93. andCanChange: true,
  94. andCanDelete: true,
  95. andCanShare: false,
  96. andIsFolder: false)
  97. case .viewOnly:
  98. return CCUtility.getPermissionsValue(
  99. byCanEdit: false,
  100. andCanCreate: false,
  101. andCanChange: false,
  102. andCanDelete: false,
  103. andCanShare: false,
  104. andIsFolder: true)
  105. case .uploadEdit:
  106. return CCUtility.getPermissionsValue(
  107. byCanEdit: true,
  108. andCanCreate: true,
  109. andCanChange: true,
  110. andCanDelete: true,
  111. andCanShare: false,
  112. andIsFolder: true)
  113. case .fileDrop:
  114. return NCGlobal.shared.permissionCreateShare
  115. }
  116. }
  117. func isOn(for share: NCTableShareable) -> Bool {
  118. switch self {
  119. case .allowEdit: return CCUtility.isAnyPermission(toEdit: share.permissions)
  120. case .viewOnly: return !CCUtility.isAnyPermission(toEdit: share.permissions) && share.permissions != NCGlobal.shared.permissionCreateShare
  121. case .uploadEdit: return CCUtility.isAnyPermission(toEdit: share.permissions) && share.permissions != NCGlobal.shared.permissionCreateShare
  122. case .fileDrop: return share.permissions == NCGlobal.shared.permissionCreateShare
  123. }
  124. }
  125. var title: String {
  126. switch self {
  127. case .allowEdit: return NSLocalizedString("_share_can_change_", comment: "")
  128. case .viewOnly: return NSLocalizedString("_share_read_only_", comment: "")
  129. case .uploadEdit: return NSLocalizedString("_share_allow_upload_", comment: "")
  130. case .fileDrop: return NSLocalizedString("_share_file_drop_", comment: "")
  131. }
  132. }
  133. case allowEdit, viewOnly, uploadEdit, fileDrop
  134. static let forDirectory: [NCLinkPermission] = [.viewOnly, .uploadEdit, .fileDrop]
  135. static let forFile: [NCLinkPermission] = [.allowEdit]
  136. }
  137. enum NCShareDetails: CaseIterable, NCShareCellConfig {
  138. func didSelect(for share: NCTableShareable) {
  139. switch self {
  140. case .hideDownload: share.hideDownload.toggle()
  141. case .expirationDate: return
  142. case .password: return
  143. case .note: return
  144. case .label: return
  145. }
  146. }
  147. func getCell(for share: NCTableShareable) -> UITableViewCell {
  148. switch self {
  149. case .hideDownload:
  150. return NCShareToggleCell(isOn: share.hideDownload)
  151. case .expirationDate:
  152. return NCShareDateCell(share: share)
  153. case .password: return NCShareToggleCell(isOn: !share.password.isEmpty, customIcons: ("lock", "lock.open"))
  154. case .note:
  155. let cell = UITableViewCell(style: .value1, reuseIdentifier: "shareNote")
  156. cell.detailTextLabel?.text = share.note
  157. cell.accessoryType = .disclosureIndicator
  158. return cell
  159. case .label:
  160. let cell = UITableViewCell(style: .value1, reuseIdentifier: "shareLabel")
  161. cell.detailTextLabel?.text = share.label
  162. return cell
  163. }
  164. }
  165. var title: String {
  166. switch self {
  167. case .hideDownload: return NSLocalizedString("_share_hide_download_", comment: "")
  168. case .expirationDate: return NSLocalizedString("_share_expiration_date_", comment: "")
  169. case .password: return NSLocalizedString("_share_password_protect_", comment: "")
  170. case .note: return NSLocalizedString("_share_note_recipient_", comment: "")
  171. case .label: return NSLocalizedString("_share_link_name_", comment: "")
  172. }
  173. }
  174. case label, hideDownload, expirationDate, password, note
  175. static let forLink: [NCShareDetails] = NCShareDetails.allCases
  176. static let forUser: [NCShareDetails] = [.expirationDate, .note]
  177. }
  178. struct NCShareConfig {
  179. let permissions: [NCPermission]
  180. let advanced: [NCShareDetails]
  181. let share: NCTableShareable
  182. let parentPermission: Int
  183. init(parentMetadata: tableMetadata, share: NCTableShareable) {
  184. self.share = share
  185. self.parentPermission = parentMetadata.sharePermissionsCollaborationServices
  186. let type: NCPermission.Type = share.shareType == NCShareCommon.shared.SHARE_TYPE_LINK ? NCLinkPermission.self : NCUserPermission.self
  187. self.permissions = parentMetadata.directory ? type.forDirectory : type.forFile
  188. self.advanced = share.shareType == NCShareCommon.shared.SHARE_TYPE_LINK ? NCShareDetails.forLink : NCShareDetails.forUser
  189. }
  190. func cellFor(indexPath: IndexPath) -> UITableViewCell? {
  191. let cellConfig = config(for: indexPath)
  192. let cell = cellConfig?.getCell(for: share)
  193. cell?.textLabel?.text = cellConfig?.title
  194. if let cellConfig = cellConfig as? NCPermission, !cellConfig.hasResharePermission(for: parentPermission) {
  195. cell?.isUserInteractionEnabled = false
  196. cell?.textLabel?.isEnabled = false
  197. }
  198. return cell
  199. }
  200. func didSelectRow(at indexPath: IndexPath) {
  201. let cellConfig = config(for: indexPath)
  202. cellConfig?.didSelect(for: share)
  203. }
  204. func config(for indexPath: IndexPath) -> NCShareCellConfig? {
  205. if indexPath.section == 0, indexPath.row < permissions.count {
  206. return permissions[indexPath.row]
  207. } else if indexPath.section == 1, indexPath.row < advanced.count {
  208. return advanced[indexPath.row]
  209. } else { return nil }
  210. }
  211. }
  212. class NCShareToggleCell: UITableViewCell {
  213. typealias CustomToggleIcon = (onIconName: String?, offIconName: String?)
  214. init(isOn: Bool, customIcons: CustomToggleIcon? = nil) {
  215. super.init(style: .default, reuseIdentifier: "toggleCell")
  216. guard let customIcons = customIcons,
  217. let iconName = isOn ? customIcons.onIconName : customIcons.offIconName else {
  218. self.accessoryType = isOn ? .checkmark : .none
  219. return
  220. }
  221. let image = NCUtility.shared.loadImage(named: iconName, color: NCBrandColor.shared.brandElement)
  222. self.accessoryView = UIImageView(image: image)
  223. }
  224. required init?(coder: NSCoder) {
  225. fatalError("init(coder:) has not been implemented")
  226. }
  227. }
  228. open class NCShareDateCell: UITableViewCell {
  229. let picker = UIDatePicker()
  230. let textField = UITextField()
  231. var onReload: (() -> Void)?
  232. init(share: NCTableShareable) {
  233. super.init(style: .value1, reuseIdentifier: "shareExpDate")
  234. picker.datePickerMode = .date
  235. picker.minimumDate = Date()
  236. if #available(iOS 13.4, *) {
  237. picker.preferredDatePickerStyle = .wheels
  238. }
  239. picker.action(for: .valueChanged) { datePicker in
  240. guard let datePicker = datePicker as? UIDatePicker else { return }
  241. self.detailTextLabel?.text = DateFormatter.shareExpDate.string(from: datePicker.date)
  242. }
  243. accessoryView = textField
  244. let toolbar = UIToolbar.toolbar {
  245. self.resignFirstResponder()
  246. share.expirationDate = nil
  247. self.onReload?()
  248. } completion: {
  249. self.resignFirstResponder()
  250. share.expirationDate = self.picker.date as NSDate
  251. self.onReload?()
  252. }
  253. textField.inputAccessoryView = toolbar
  254. textField.inputView = picker
  255. if let expDate = share.expirationDate {
  256. detailTextLabel?.text = DateFormatter.shareExpDate.string(from: expDate as Date)
  257. }
  258. }
  259. required public init?(coder: NSCoder) {
  260. fatalError("init(coder:) has not been implemented")
  261. }
  262. }