NCDataSource.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. //
  2. // NCDataSource.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 06/09/2020.
  6. // Copyright © 2020 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@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. import NCCommunication
  25. class NCDataSource: NSObject {
  26. public var metadatasSource: [tableMetadata] = []
  27. public var metadatasForSection: [NCMetadatasForSection] = []
  28. private var sectionsValue: [String] = []
  29. private var providers: [NCCSearchProvider]?
  30. private var ascending: Bool = true
  31. private var sort: String = ""
  32. private var directoryOnTop: Bool = true
  33. private var favoriteOnTop: Bool = true
  34. private var filterLivePhoto: Bool = true
  35. private var groupByField: String = ""
  36. override init() {
  37. super.init()
  38. }
  39. init(metadatasSource: [tableMetadata], sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true, groupByField: String = "name", providers: [NCCSearchProvider]? = nil) {
  40. super.init()
  41. self.metadatasSource = metadatasSource
  42. self.sort = sort ?? "none"
  43. self.ascending = ascending ?? false
  44. self.directoryOnTop = directoryOnTop ?? true
  45. self.favoriteOnTop = favoriteOnTop ?? true
  46. self.filterLivePhoto = filterLivePhoto ?? true
  47. self.groupByField = groupByField
  48. self.providers = providers
  49. createSections()
  50. for sectionValue in self.sectionsValue {
  51. createMetadataForSection(sectionValue: sectionValue)
  52. }
  53. }
  54. // MARK: -
  55. func clearDataSource() {
  56. self.metadatasSource.removeAll()
  57. self.metadatasForSection.removeAll()
  58. self.sectionsValue.removeAll()
  59. }
  60. internal func createSections() {
  61. self.sectionsValue = metadatasSource.map { getSectionValue(metadata: $0) }
  62. self.sectionsValue = Array(Set(self.sectionsValue))
  63. if let providers = self.providers {
  64. } else {
  65. self.sectionsValue = self.sectionsValue.sorted {
  66. if self.ascending {
  67. return $0 < $1
  68. } else {
  69. return $0 > $1
  70. }
  71. }
  72. }
  73. }
  74. internal func createMetadataForSection(sectionValue: String) {
  75. let metadatas = metadatasSource.filter({ getSectionValue(metadata: $0) == sectionValue})
  76. let metadataForSection = NCMetadatasForSection.init(sectionValue: sectionValue, metadatas: metadatas, sort: self.sort, ascending: self.ascending, directoryOnTop: self.directoryOnTop, favoriteOnTop: self.favoriteOnTop, filterLivePhoto: self.filterLivePhoto)
  77. metadatasForSection.append(metadataForSection)
  78. }
  79. // MARK: -
  80. @discardableResult
  81. func addMetadata(_ metadata: tableMetadata) -> IndexPath? {
  82. // ADD metadatasSource
  83. if let rowIndex = self.metadatasSource.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
  84. self.metadatasSource[rowIndex] = metadata
  85. } else {
  86. self.metadatasSource.append(metadata)
  87. }
  88. // ADD metadataForSection
  89. if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == self.getSectionValue(metadata: metadata) }) {
  90. let metadataForSection = metadatasForSection[sectionIndex]
  91. if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
  92. metadataForSection.metadatas[rowIndex] = metadata
  93. return IndexPath(row: rowIndex, section: sectionIndex)
  94. } else {
  95. metadataForSection.metadatas.append(metadata)
  96. metadataForSection.createMetadatasForSection()
  97. if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
  98. return IndexPath(row: rowIndex, section: sectionIndex)
  99. }
  100. return nil
  101. }
  102. } else {
  103. // NEW section
  104. createSections()
  105. let sectionValue = getSectionValue(metadata: metadata)
  106. createMetadataForSection(sectionValue: sectionValue)
  107. // get IndexPath of new section
  108. if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == sectionValue }) {
  109. let metadataForSection = metadatasForSection[sectionIndex]
  110. if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
  111. return IndexPath(row: rowIndex, section: sectionIndex)
  112. }
  113. }
  114. }
  115. return nil
  116. }
  117. func deleteMetadata(ocId: String) -> IndexPath? {
  118. var indexPathReturn: IndexPath?
  119. var removeMetadataForSection = false
  120. var sectionValue = ""
  121. // DELETE metadataForSection (IMPORTANT FIRST)
  122. let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId)
  123. if let indexPath = indexPath, let metadataForSection = metadataForSection {
  124. metadataForSection.metadatas.remove(at: indexPath.row)
  125. if metadataForSection.metadatas.count == 0 {
  126. sectionValue = metadataForSection.sectionValue
  127. removeMetadataForSection = true
  128. } else {
  129. metadataForSection.createMetadatasForSection()
  130. }
  131. indexPathReturn = indexPath
  132. }
  133. // DELETE metadatasSource (IMPORTANT LAST)
  134. if let rowIndex = self.metadatasSource.firstIndex(where: {$0.ocId == ocId}) {
  135. self.metadatasSource.remove(at: rowIndex)
  136. }
  137. // REMOVE sectionsValue / metadatasForSection
  138. if removeMetadataForSection {
  139. if let index = self.sectionsValue.firstIndex(where: {$0 == sectionValue }) {
  140. self.sectionsValue.remove(at: index)
  141. }
  142. if let index = self.metadatasForSection.firstIndex(where: {$0.sectionValue == sectionValue }) {
  143. self.metadatasForSection.remove(at: index)
  144. }
  145. }
  146. return indexPathReturn
  147. }
  148. @discardableResult
  149. func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> IndexPath? {
  150. var ocIdSearch = ocId
  151. var indexPath: IndexPath?
  152. var metadataForSection: NCMetadatasForSection?
  153. guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return nil }
  154. if let ocIdTemp = ocIdTemp {
  155. ocIdSearch = ocIdTemp
  156. }
  157. // UPDATE metadataForSection (IMPORTANT FIRST)
  158. (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocIdSearch)
  159. if let indexPath = indexPath, let metadataForSection = metadataForSection {
  160. metadataForSection.metadatas[indexPath.row] = metadata
  161. metadataForSection.createMetadatasForSection()
  162. }
  163. // UPDATE metadatasSource (IMPORTANT LAST)
  164. if let rowIndex = self.metadatasSource.firstIndex(where: {$0.ocId == ocIdSearch}) {
  165. self.metadatasSource[rowIndex] = metadata
  166. }
  167. return indexPath
  168. }
  169. // MARK: -
  170. func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadatasForSection?) {
  171. if let metadata = metadatasSource.filter({ $0.ocId == ocId}).first {
  172. let sectionValue = getSectionValue(metadata: metadata)
  173. if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == sectionValue}) {
  174. for metadataForSection in self.metadatasForSection {
  175. if metadataForSection.sectionValue == sectionValue {
  176. if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == ocId}) {
  177. return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
  178. }
  179. }
  180. }
  181. }
  182. }
  183. return (nil, nil)
  184. }
  185. func numberOfSections() -> Int {
  186. if self.metadatasForSection.count == 0 {
  187. return 1
  188. } else {
  189. return self.metadatasForSection.count
  190. }
  191. }
  192. func numberOfItemsInSection(_ section: Int) -> Int {
  193. if self.metadatasForSection.count == 0 || self.metadatasSource.count == 0 { return 0 }
  194. return self.metadatasForSection[section].metadatas.count
  195. }
  196. func cellForItemAt(indexPath: IndexPath) -> tableMetadata? {
  197. let metadatasForSection = self.metadatasForSection[indexPath.section]
  198. return metadatasForSection.metadatas[indexPath.row]
  199. }
  200. func getSectionValue(indexPath: IndexPath) -> String {
  201. if metadatasForSection.count == 0 { return "" }
  202. let metadataForSection = self.metadatasForSection[indexPath.section]
  203. return metadataForSection.sectionValue
  204. }
  205. func getFooterInformation() -> (directories: Int, files: Int, size: Int64) {
  206. var directories: Int = 0
  207. var files: Int = 0
  208. var size: Int64 = 0
  209. for metadataForSection in metadatasForSection {
  210. directories += metadataForSection.numDirectory
  211. files += metadataForSection.numFile
  212. size += metadataForSection.totalSize
  213. }
  214. return (directories, files, size)
  215. }
  216. internal func getSectionValue(metadata: tableMetadata) -> String {
  217. switch self.groupByField {
  218. case "name":
  219. return metadata.name
  220. default:
  221. return metadata.name
  222. }
  223. }
  224. }
  225. class NCMetadatasForSection: NSObject {
  226. var sectionValue: String
  227. var metadatas: [tableMetadata]
  228. private var sort : String
  229. private var ascending: Bool
  230. private var directoryOnTop: Bool
  231. private var favoriteOnTop: Bool
  232. private var filterLivePhoto: Bool
  233. private var metadatasSourceSorted: [tableMetadata] = []
  234. private var metadatasFavoriteDirectory: [tableMetadata] = []
  235. private var metadatasFavoriteFile: [tableMetadata] = []
  236. private var metadatasDirectory: [tableMetadata] = []
  237. private var metadatasFile: [tableMetadata] = []
  238. public var numDirectory: Int = 0
  239. public var numFile: Int = 0
  240. public var totalSize: Int64 = 0
  241. public var metadataShare: [String: tableShare] = [:]
  242. public var metadataOffLine: [String] = []
  243. init(sectionValue: String, metadatas: [tableMetadata], sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
  244. self.sectionValue = sectionValue
  245. self.metadatas = metadatas
  246. self.sort = sort
  247. self.ascending = ascending
  248. self.directoryOnTop = directoryOnTop
  249. self.favoriteOnTop = favoriteOnTop
  250. self.filterLivePhoto = filterLivePhoto
  251. super.init()
  252. createMetadatasForSection()
  253. }
  254. func createMetadatasForSection() {
  255. // Clear
  256. //
  257. metadatasSourceSorted.removeAll()
  258. metadatasFavoriteDirectory.removeAll()
  259. metadatasFavoriteFile.removeAll()
  260. metadatasDirectory.removeAll()
  261. metadatasFile.removeAll()
  262. metadataShare.removeAll()
  263. metadataOffLine.removeAll()
  264. numDirectory = 0
  265. numFile = 0
  266. totalSize = 0
  267. // Metadata order
  268. //
  269. if sort != "none" && sort != "" {
  270. metadatasSourceSorted = metadatas.sorted {
  271. switch sort {
  272. case "date":
  273. if ascending {
  274. return ($0.date as Date) < ($1.date as Date)
  275. } else {
  276. return ($0.date as Date) > ($1.date as Date)
  277. }
  278. case "size":
  279. if ascending {
  280. return $0.size < $1.size
  281. } else {
  282. return $0.size > $1.size
  283. }
  284. default:
  285. if ascending {
  286. return $0.fileNameView.lowercased() < $1.fileNameView.lowercased()
  287. } else {
  288. return $0.fileNameView.lowercased() > $1.fileNameView.lowercased()
  289. }
  290. }
  291. }
  292. } else {
  293. metadatasSourceSorted = metadatas
  294. }
  295. // Initialize datasource
  296. //
  297. for metadata in metadatasSourceSorted {
  298. // skipped the root file
  299. if metadata.fileName == "." || metadata.serverUrl == ".." {
  300. continue
  301. }
  302. // skipped livePhoto
  303. if metadata.ext == "mov" && metadata.livePhoto && filterLivePhoto {
  304. continue
  305. }
  306. // share
  307. let shares = NCManageDatabase.shared.getTableShares(account: metadata.account, serverUrl: metadata.serverUrl, fileName: metadata.fileName)
  308. if shares.count > 0 {
  309. metadataShare[metadata.ocId] = shares.first
  310. }
  311. // is Local / offline
  312. if !metadata.directory, CCUtility.fileProviderStorageExists(metadata) {
  313. let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
  314. if tableLocalFile == nil {
  315. NCManageDatabase.shared.addLocalFile(metadata: metadata)
  316. }
  317. if tableLocalFile?.offline ?? false {
  318. metadataOffLine.append(metadata.ocId)
  319. }
  320. }
  321. // Organized the metadata
  322. if metadata.favorite && favoriteOnTop {
  323. if metadata.directory {
  324. metadatasFavoriteDirectory.append(metadata)
  325. } else {
  326. metadatasFavoriteFile.append(metadata)
  327. }
  328. } else if metadata.directory && directoryOnTop {
  329. metadatasDirectory.append(metadata)
  330. } else {
  331. metadatasFile.append(metadata)
  332. }
  333. //Info
  334. if metadata.directory {
  335. numDirectory += 1
  336. } else {
  337. numFile += 1
  338. totalSize += metadata.size
  339. }
  340. }
  341. metadatas.removeAll()
  342. // Struct view : favorite dir -> favorite file -> directory -> files
  343. metadatas += metadatasFavoriteDirectory
  344. metadatas += metadatasFavoriteFile
  345. metadatas += metadatasDirectory
  346. metadatas += metadatasFile
  347. }
  348. }