NCMedia.swift 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. //
  2. // NCMedia.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 12/02/2019.
  6. // Copyright © 2019 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 NextcloudKit
  25. import RealmSwift
  26. class NCMedia: UIViewController {
  27. @IBOutlet weak var collectionView: UICollectionView!
  28. @IBOutlet weak var titleDate: UILabel!
  29. @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
  30. @IBOutlet weak var activityIndicatorTrailing: NSLayoutConstraint!
  31. @IBOutlet weak var selectOrCancelButton: UIButton!
  32. @IBOutlet weak var selectOrCancelButtonTrailing: NSLayoutConstraint!
  33. @IBOutlet weak var menuButton: UIButton!
  34. @IBOutlet weak var gradientView: UIView!
  35. let layout = NCMediaLayout()
  36. var activeAccount = tableAccount()
  37. var documentPickerViewController: NCDocumentPickerViewController?
  38. var tabBarSelect: NCMediaSelectTabBar!
  39. let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
  40. let utilityFileSystem = NCUtilityFileSystem()
  41. let utility = NCUtility()
  42. let imageCache = NCImageCache.shared
  43. var metadatas: ThreadSafeArray<tableMetadata>?
  44. var serverUrl = ""
  45. let refreshControl = UIRefreshControl()
  46. var loadingTask: Task<Void, any Error>?
  47. let taskDescriptionRetrievesProperties = "retrievesProperties"
  48. var isTop: Bool = true
  49. var isEditMode = false
  50. var selectOcId: [String] = []
  51. var attributesZoomIn: UIMenuElement.Attributes = []
  52. var attributesZoomOut: UIMenuElement.Attributes = []
  53. let gradient: CAGradientLayer = CAGradientLayer()
  54. var showOnlyImages = false
  55. var showOnlyVideos = false
  56. var lastContentOffsetY: CGFloat = 0
  57. var timeIntervalSearchNewMedia: TimeInterval = 2.0
  58. var timerSearchNewMedia: Timer?
  59. let insetsTop: CGFloat = 75
  60. let maxImageGrid: CGFloat = 7
  61. var livePhotoImage = UIImage()
  62. var playImage = UIImage()
  63. var photoImage = UIImage()
  64. var videoImage = UIImage()
  65. // MARK: - View Life Cycle
  66. override func viewDidLoad() {
  67. super.viewDidLoad()
  68. view.backgroundColor = .systemBackground
  69. collectionView.register(UINib(nibName: "NCSectionHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: collectionViewMediaElementKindSectionHeader, withReuseIdentifier: "sectionHeaderEmptyData")
  70. collectionView.register(UINib(nibName: "NCGridMediaCell", bundle: nil), forCellWithReuseIdentifier: "gridCell")
  71. collectionView.alwaysBounceVertical = true
  72. collectionView.contentInset = UIEdgeInsets(top: insetsTop, left: 0, bottom: 50, right: 0)
  73. collectionView.backgroundColor = .systemBackground
  74. collectionView.prefetchDataSource = self
  75. // Drag & Drop
  76. // Drag & Drop
  77. collectionView.dragInteractionEnabled = true
  78. collectionView.dragDelegate = self
  79. collectionView.dropDelegate = self
  80. layout.sectionInset = UIEdgeInsets(top: 0, left: 2, bottom: 0, right: 2)
  81. layout.mediaViewController = self
  82. collectionView.collectionViewLayout = layout
  83. tabBarSelect = NCMediaSelectTabBar(tabBarController: self.tabBarController, delegate: self)
  84. livePhotoImage = utility.loadImage(named: "livephoto", colors: [.white])
  85. playImage = utility.loadImage(named: "play.fill", colors: [.white])
  86. titleDate.text = ""
  87. selectOrCancelButton.backgroundColor = .clear
  88. selectOrCancelButton.layer.cornerRadius = 15
  89. selectOrCancelButton.layer.masksToBounds = true
  90. selectOrCancelButton.setTitle( NSLocalizedString("_select_", comment: ""), for: .normal)
  91. selectOrCancelButton.addBlur(style: .systemThinMaterial)
  92. menuButton.backgroundColor = .clear
  93. menuButton.layer.cornerRadius = 15
  94. menuButton.layer.masksToBounds = true
  95. menuButton.showsMenuAsPrimaryAction = true
  96. menuButton.configuration = UIButton.Configuration.plain()
  97. menuButton.setImage(NCUtility().loadImage(named: "ellipsis"), for: .normal)
  98. menuButton.changesSelectionAsPrimaryAction = false
  99. menuButton.addBlur(style: .systemThinMaterial)
  100. gradient.startPoint = CGPoint(x: 0, y: 0.1)
  101. gradient.endPoint = CGPoint(x: 0, y: 1)
  102. gradient.colors = [UIColor.black.withAlphaComponent(UIAccessibility.isReduceTransparencyEnabled ? 0.8 : 0.4).cgColor, UIColor.clear.cgColor]
  103. gradientView.layer.insertSublayer(gradient, at: 0)
  104. activeAccount = NCManageDatabase.shared.getActiveAccount() ?? tableAccount()
  105. collectionView.refreshControl = refreshControl
  106. refreshControl.action(for: .valueChanged) { _ in
  107. DispatchQueue.global(qos: .userInteractive).async {
  108. self.reloadDataSource()
  109. }
  110. self.refreshControl.endRefreshing()
  111. }
  112. NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { _ in
  113. self.activeAccount = NCManageDatabase.shared.getActiveAccount() ?? tableAccount()
  114. if let metadatas = self.metadatas,
  115. let metadata = metadatas.first {
  116. if metadata.account != self.activeAccount.account {
  117. self.metadatas = nil
  118. self.collectionViewReloadData()
  119. }
  120. }
  121. }
  122. NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateMediaCacheEnded), object: nil, queue: nil) { _ in
  123. if let metadatas = self.imageCache.initialMetadatas() {
  124. self.metadatas = metadatas
  125. }
  126. self.collectionViewReloadData()
  127. }
  128. }
  129. override func viewWillAppear(_ animated: Bool) {
  130. super.viewWillAppear(animated)
  131. navigationController?.setMediaAppreance()
  132. }
  133. override func viewDidAppear(_ animated: Bool) {
  134. super.viewDidAppear(animated)
  135. NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
  136. NotificationCenter.default.addObserver(self, selector: #selector(moveFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
  137. NotificationCenter.default.addObserver(self, selector: #selector(copyFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil)
  138. NotificationCenter.default.addObserver(self, selector: #selector(enterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
  139. NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
  140. startTimer()
  141. createMenu()
  142. if imageCache.createMediaCacheInProgress {
  143. self.metadatas = nil
  144. self.collectionViewReloadData()
  145. } else if let metadatas = imageCache.initialMetadatas() {
  146. self.metadatas = metadatas
  147. self.collectionViewReloadData()
  148. } else {
  149. DispatchQueue.global(qos: .userInteractive).async {
  150. self.reloadDataSource()
  151. }
  152. }
  153. }
  154. override func viewDidDisappear(_ animated: Bool) {
  155. super.viewDidDisappear(animated)
  156. NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
  157. NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil)
  158. NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
  159. NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
  160. NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
  161. // Cancel Queue & Retrieves Properties
  162. NCNetworking.shared.downloadThumbnailQueue.cancelAll()
  163. NextcloudKit.shared.sessionManager.session.getTasksWithCompletionHandler { dataTasks, _, _ in
  164. dataTasks.forEach {
  165. if $0.taskDescription == self.taskDescriptionRetrievesProperties {
  166. $0.cancel()
  167. }
  168. }
  169. }
  170. }
  171. override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
  172. super.viewWillTransition(to: size, with: coordinator)
  173. coordinator.animate(alongsideTransition: nil) { _ in
  174. self.setTitleDate()
  175. }
  176. }
  177. override var preferredStatusBarStyle: UIStatusBarStyle {
  178. if self.traitCollection.userInterfaceStyle == .dark {
  179. return .lightContent
  180. } else if isTop {
  181. return .darkContent
  182. } else {
  183. return .lightContent
  184. }
  185. }
  186. override func viewWillLayoutSubviews() {
  187. super.viewWillLayoutSubviews()
  188. if let frame = tabBarController?.tabBar.frame {
  189. tabBarSelect.hostingController.view.frame = frame
  190. }
  191. gradient.frame = gradientView.bounds
  192. }
  193. func startTimer() {
  194. // don't start if media chage is in progress
  195. if imageCache.createMediaCacheInProgress {
  196. return
  197. }
  198. timerSearchNewMedia?.invalidate()
  199. timerSearchNewMedia = Timer.scheduledTimer(timeInterval: timeIntervalSearchNewMedia, target: self, selector: #selector(searchMediaUI), userInfo: nil, repeats: false)
  200. }
  201. // MARK: - NotificationCenter
  202. @objc func deleteFile(_ notification: NSNotification) {
  203. guard let userInfo = notification.userInfo as NSDictionary?,
  204. let error = userInfo["error"] as? NKError else { return }
  205. self.reloadDataSource()
  206. if error != .success {
  207. NCContentPresenter().showError(error: error)
  208. }
  209. }
  210. @objc func enterForeground(_ notification: NSNotification) {
  211. startTimer()
  212. }
  213. @objc func uploadedFile(_ notification: NSNotification) {
  214. guard let userInfo = notification.userInfo as NSDictionary?,
  215. let ocId = userInfo["ocId"] as? String else { return }
  216. if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
  217. (metadata.isVideo || metadata.isImage) {
  218. self.reloadDataSource()
  219. }
  220. }
  221. @objc func moveFile(_ notification: NSNotification) {
  222. guard let userInfo = notification.userInfo as NSDictionary?,
  223. let dragDrop = userInfo["dragdrop"] as? Bool, dragDrop else { return }
  224. setEditMode(false)
  225. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  226. self.reloadDataSource()
  227. self.searchMediaUI()
  228. }
  229. }
  230. @objc func copyFile(_ notification: NSNotification) {
  231. guard let userInfo = notification.userInfo as NSDictionary?,
  232. let dragDrop = userInfo["dragdrop"] as? Bool, dragDrop else { return }
  233. setEditMode(false)
  234. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  235. self.reloadDataSource()
  236. self.searchMediaUI()
  237. }
  238. }
  239. // MARK: - Image
  240. func getImage(metadata: tableMetadata) -> UIImage? {
  241. let fileNamePathPreview = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)
  242. if let image = imageCache.getMediaImage(ocId: metadata.ocId, etag: metadata.etag) {
  243. return image
  244. } else if FileManager().fileExists(atPath: fileNamePathPreview), let image = UIImage(contentsOfFile: fileNamePathPreview) {
  245. return image
  246. } else if metadata.hasPreview && metadata.status == NCGlobal.shared.metadataStatusNormal,
  247. (!utilityFileSystem.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag)),
  248. NCNetworking.shared.downloadThumbnailQueue.operations.filter({ ($0 as? NCMediaDownloadThumbnaill)?.metadata.ocId == metadata.ocId }).isEmpty {
  249. NCNetworking.shared.downloadThumbnailQueue.addOperation(NCMediaDownloadThumbnaill(metadata: metadata, media: self))
  250. }
  251. return nil
  252. }
  253. func buildMediaPhotoVideo(columnCount: Int) {
  254. var pointSize: CGFloat = 0
  255. switch columnCount {
  256. case 0...1: pointSize = 60
  257. case 2...3: pointSize = 30
  258. case 4...5: pointSize = 25
  259. case 6...Int(maxImageGrid): pointSize = 20
  260. default: pointSize = 20
  261. }
  262. if let image = UIImage(systemName: "photo.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: pointSize))?.withTintColor(.systemGray4, renderingMode: .alwaysOriginal) {
  263. photoImage = image
  264. }
  265. if let image = UIImage(systemName: "video.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: pointSize))?.withTintColor(.systemGray4, renderingMode: .alwaysOriginal) {
  266. videoImage = image
  267. }
  268. }
  269. }
  270. // MARK: - Collection View
  271. extension NCMedia: UICollectionViewDelegate {
  272. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  273. var mediaCell: NCGridMediaCell?
  274. if let metadata = self.metadatas?[indexPath.row] {
  275. if let visibleCells = self.collectionView?.indexPathsForVisibleItems.compactMap({ self.collectionView?.cellForItem(at: $0) }) {
  276. for case let cell as NCGridMediaCell in visibleCells {
  277. if cell.ocId == metadata.ocId {
  278. mediaCell = cell
  279. }
  280. }
  281. }
  282. if isEditMode {
  283. if let index = selectOcId.firstIndex(of: metadata.ocId) {
  284. selectOcId.remove(at: index)
  285. mediaCell?.selected(false)
  286. } else {
  287. selectOcId.append(metadata.ocId)
  288. mediaCell?.selected(true)
  289. }
  290. tabBarSelect.selectCount = selectOcId.count
  291. } else {
  292. // ACTIVE SERVERURL
  293. serverUrl = metadata.serverUrl
  294. if let metadatas = self.metadatas?.getArray() {
  295. NCViewer().view(viewController: self, metadata: metadata, metadatas: metadatas, imageIcon: getImage(metadata: metadata))
  296. }
  297. }
  298. }
  299. }
  300. func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
  301. guard let cell = collectionView.cellForItem(at: indexPath) as? NCGridMediaCell,
  302. let metadata = self.metadatas?[indexPath.row] else { return nil }
  303. let identifier = indexPath as NSCopying
  304. let image = cell.imageItem.image
  305. self.serverUrl = metadata.serverUrl
  306. return UIContextMenuConfiguration(identifier: identifier, previewProvider: {
  307. return NCViewerProviderContextMenu(metadata: metadata, image: image)
  308. }, actionProvider: { _ in
  309. return NCContextMenu().viewMenu(ocId: metadata.ocId, viewController: self, image: image)
  310. })
  311. }
  312. func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
  313. animator.addCompletion {
  314. if let indexPath = configuration.identifier as? IndexPath {
  315. self.collectionView(collectionView, didSelectItemAt: indexPath)
  316. }
  317. }
  318. }
  319. }
  320. extension NCMedia: UICollectionViewDataSourcePrefetching {
  321. func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
  322. // print("[INFO] n. " + String(indexPaths.count))
  323. }
  324. }
  325. extension NCMedia: UICollectionViewDataSource {
  326. func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
  327. if kind == "collectionViewMediaElementKindSectionHeader" {
  328. guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderEmptyData", for: indexPath) as? NCSectionHeaderEmptyData else { return NCSectionHeaderEmptyData() }
  329. header.emptyImage.image = utility.loadImage(named: "photo", colors: [NCBrandColor.shared.brandElement])
  330. if loadingTask != nil || imageCache.createMediaCacheInProgress {
  331. header.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
  332. } else {
  333. header.emptyTitle.text = NSLocalizedString("_tutorial_photo_view_", comment: "")
  334. }
  335. header.emptyDescription.text = ""
  336. return header
  337. }
  338. return UICollectionReusableView()
  339. }
  340. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  341. var numberOfItemsInSection = 0
  342. if let metadatas { numberOfItemsInSection = metadatas.count }
  343. if numberOfItemsInSection == 0 {
  344. selectOrCancelButton.isHidden = true
  345. menuButton.isHidden = false
  346. gradientView.isHidden = true
  347. activityIndicatorTrailing.constant = 50
  348. } else if isEditMode {
  349. selectOrCancelButton.isHidden = false
  350. menuButton.isHidden = true
  351. activityIndicatorTrailing.constant = 150
  352. } else {
  353. selectOrCancelButton.isHidden = false
  354. menuButton.isHidden = false
  355. activityIndicatorTrailing.constant = 150
  356. }
  357. DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { self.setTitleDate() }
  358. return numberOfItemsInSection
  359. }
  360. func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
  361. guard let metadatas else { return }
  362. if !collectionView.indexPathsForVisibleItems.contains(indexPath) && indexPath.row < metadatas.count {
  363. guard let metadata = metadatas[indexPath.row] else { return }
  364. for case let operation as NCMediaDownloadThumbnaill in NCNetworking.shared.downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId {
  365. operation.cancel()
  366. }
  367. for case let operation as NCOperationConvertLivePhoto in NCNetworking.shared.convertLivePhotoQueue.operations where operation.ocId == metadata.ocId {
  368. operation.cancel()
  369. }
  370. }
  371. }
  372. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  373. guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridMediaCell,
  374. let metadatas = self.metadatas,
  375. let metadata = metadatas[indexPath.row]
  376. else {
  377. return NCGridMediaCell()
  378. }
  379. cell.date = metadata.date as Date
  380. cell.ocId = metadata.ocId
  381. cell.indexPath = indexPath
  382. cell.user = metadata.ownerId
  383. cell.imageStatus.image = nil
  384. cell.imageItem.contentMode = .scaleAspectFill
  385. if let image = getImage(metadata: metadata) {
  386. cell.imageItem.image = image
  387. } else if !metadata.hasPreview {
  388. cell.imageItem.backgroundColor = .clear
  389. cell.imageItem.contentMode = .center
  390. if metadata.isImage {
  391. cell.imageItem.image = photoImage
  392. } else {
  393. cell.imageItem.image = videoImage
  394. }
  395. }
  396. // Convert OLD Live Photo
  397. if NCGlobal.shared.isLivePhotoServerAvailable, metadata.isLivePhoto, metadata.isNotFlaggedAsLivePhotoByServer {
  398. NCNetworking.shared.convertLivePhoto(metadata: metadata)
  399. }
  400. if metadata.isAudioOrVideo {
  401. cell.imageStatus.image = playImage
  402. } else if metadata.isLivePhoto {
  403. cell.imageStatus.image = livePhotoImage
  404. } else {
  405. cell.imageStatus.image = nil
  406. }
  407. if isEditMode, selectOcId.contains(metadata.ocId) {
  408. cell.selected(true)
  409. } else {
  410. cell.selected(false)
  411. }
  412. return cell
  413. }
  414. }
  415. // MARK: -
  416. extension NCMedia: NCMediaLayoutDelegate {
  417. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, heightForHeaderInSection section: Int) -> Float {
  418. var height: Double = 0
  419. if metadatas?.count ?? 0 == 0 {
  420. height = NCGlobal.shared.getHeightHeaderEmptyData(view: view, portraitOffset: 0, landscapeOffset: -20)
  421. }
  422. return Float(height)
  423. }
  424. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, heightForFooterInSection section: Int) -> Float {
  425. return .zero
  426. }
  427. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForSection section: Int) -> UIEdgeInsets {
  428. return .zero
  429. }
  430. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForHeaderInSection section: Int) -> UIEdgeInsets {
  431. return .zero
  432. }
  433. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForFooterInSection section: Int) -> UIEdgeInsets {
  434. return .zero
  435. }
  436. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, minimumInteritemSpacingForSection section: Int) -> Float {
  437. return .zero
  438. }
  439. func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath, columnCount: Int, mediaLayout: String) -> CGSize {
  440. let size = CGSize(width: collectionView.frame.width / CGFloat(columnCount), height: collectionView.frame.width / CGFloat(columnCount))
  441. if mediaLayout == NCGlobal.shared.mediaLayoutRatio {
  442. guard let metadatas = self.metadatas,
  443. let metadata = metadatas[indexPath.row] else { return size }
  444. if metadata.imageSize != CGSize.zero {
  445. return metadata.imageSize
  446. } else if let size = imageCache.getMediaSize(ocId: metadata.ocId, etag: metadata.etag) {
  447. return size
  448. }
  449. }
  450. return size
  451. }
  452. }
  453. // MARK: -
  454. extension NCMedia: UIScrollViewDelegate {
  455. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  456. if let metadatas, !metadatas.isEmpty {
  457. isTop = scrollView.contentOffset.y <= -(insetsTop + view.safeAreaInsets.top - 25)
  458. setColor()
  459. setNeedsStatusBarAppearanceUpdate()
  460. if lastContentOffsetY == 0 || lastContentOffsetY / 2 <= scrollView.contentOffset.y || lastContentOffsetY / 2 >= scrollView.contentOffset.y {
  461. setTitleDate()
  462. lastContentOffsetY = scrollView.contentOffset.y
  463. }
  464. } else {
  465. setColor()
  466. }
  467. }
  468. func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
  469. }
  470. func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  471. if !decelerate {
  472. if !decelerate {
  473. startTimer()
  474. }
  475. }
  476. }
  477. func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  478. startTimer()
  479. }
  480. func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
  481. let y = view.safeAreaInsets.top
  482. scrollView.contentOffset.y = -(insetsTop + y)
  483. }
  484. }
  485. // MARK: -
  486. extension NCMedia: NCSelectDelegate {
  487. func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
  488. guard let serverUrl = serverUrl else { return }
  489. let home = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
  490. let mediaPath = serverUrl.replacingOccurrences(of: home, with: "")
  491. NCManageDatabase.shared.setAccountMediaPath(mediaPath, account: activeAccount.account)
  492. activeAccount = NCManageDatabase.shared.getActiveAccount() ?? tableAccount()
  493. reloadDataSource()
  494. startTimer()
  495. }
  496. }