Browse Source

Version 5.5.0 (#2994)

Marino Faggiana 9 months ago
parent
commit
ca03c1d3a7
100 changed files with 2582 additions and 2834 deletions
  1. 1 1
      Brand/Database.swift
  2. 4 2
      Brand/NCBrand.swift
  3. 1 1
      Cartfile.resolved
  4. 19 74
      File Provider Extension/FileProviderData.swift
  5. 8 16
      File Provider Extension/FileProviderDomain.swift
  6. 59 83
      File Provider Extension/FileProviderEnumerator.swift
  7. 44 125
      File Provider Extension/FileProviderExtension+Actions.swift
  8. 72 0
      File Provider Extension/FileProviderExtension+NetworkingDelegate.swift
  9. 12 32
      File Provider Extension/FileProviderExtension+Thumbnail.swift
  10. 65 187
      File Provider Extension/FileProviderExtension.swift
  11. 60 69
      File Provider Extension/FileProviderItem.swift
  12. 6 60
      File Provider Extension/FileProviderUtility.swift
  13. 114 48
      Nextcloud.xcodeproj/project.pbxproj
  14. 96 0
      Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension UI.xcscheme
  15. 96 0
      Nextcloud.xcodeproj/xcshareddata/xcschemes/WidgetDashboardIntentHandler.xcscheme
  16. 1 1
      Notification Service Extension/NotificationService.swift
  17. 3 5
      Share/NCShareCell.swift
  18. 2 9
      Share/NCShareExtension+DataSource.swift
  19. 1 21
      Share/NCShareExtension+Files.swift
  20. 9 1
      Share/NCShareExtension+NCDelegate.swift
  21. 1 2
      Share/NCShareExtension.swift
  22. 1 1
      Tests/Common/BaseXCTestCase.swift
  23. 1 1
      Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift
  24. 4 5
      Widget/Dashboard/DashboardData.swift
  25. 1 1
      Widget/Dashboard/DashboardWidgetProvider.swift
  26. 13 15
      Widget/Files/FilesData.swift
  27. 1 1
      Widget/Files/FilesWidgetProvider.swift
  28. 1 1
      Widget/Lockscreen/LockscreenData.swift
  29. 1 1
      Widget/Lockscreen/LockscreenWidgetProvider.swift
  30. 1 1
      Widget/Toolbar/ToolbarData.swift
  31. 1 1
      Widget/Toolbar/ToolbarWidgetProvider.swift
  32. 0 10
      WidgetDashboardIntentHandler/IntentHandler.swift
  33. 9 7
      iOSClient/Account Settings/NCAccountSettingsModel.swift
  34. 32 28
      iOSClient/Account Settings/NCAccountSettingsView.swift
  35. 20 60
      iOSClient/Activity/NCActivity.storyboard
  36. 40 49
      iOSClient/Activity/NCActivity.swift
  37. 9 8
      iOSClient/Activity/NCActivityCommentView.swift
  38. 26 25
      iOSClient/Activity/NCActivityCommentView.xib
  39. 5 30
      iOSClient/Activity/NCActivityTableViewCell.swift
  40. 34 59
      iOSClient/AppDelegate.swift
  41. 29 33
      iOSClient/Data/NCDataSource.swift
  42. 21 57
      iOSClient/Data/NCManageDatabase+Account.swift
  43. 1 19
      iOSClient/Data/NCManageDatabase+Activity.swift
  44. 0 2
      iOSClient/Data/NCManageDatabase+Avatar.swift
  45. 1 7
      iOSClient/Data/NCManageDatabase+Capabilities.swift
  46. 0 10
      iOSClient/Data/NCManageDatabase+Chunk.swift
  47. 0 4
      iOSClient/Data/NCManageDatabase+Comments.swift
  48. 0 10
      iOSClient/Data/NCManageDatabase+DashboardWidget.swift
  49. 0 16
      iOSClient/Data/NCManageDatabase+DirectEditing.swift
  50. 1 5
      iOSClient/Data/NCManageDatabase+Directory.swift
  51. 0 36
      iOSClient/Data/NCManageDatabase+E2EE.swift
  52. 0 6
      iOSClient/Data/NCManageDatabase+ExternalSites.swift
  53. 2 5
      iOSClient/Data/NCManageDatabase+GPS.swift
  54. 0 4
      iOSClient/Data/NCManageDatabase+Groupfolders.swift
  55. 25 18
      iOSClient/Data/NCManageDatabase+LayoutForView.swift
  56. 1 21
      iOSClient/Data/NCManageDatabase+LocalFile.swift
  57. 1 2
      iOSClient/Data/NCManageDatabase+Metadata+Session.swift
  58. 69 63
      iOSClient/Data/NCManageDatabase+Metadata.swift
  59. 0 7
      iOSClient/Data/NCManageDatabase+PhotoLibrary.swift
  60. 0 8
      iOSClient/Data/NCManageDatabase+SecurityGuard.swift
  61. 2 18
      iOSClient/Data/NCManageDatabase+Share.swift
  62. 0 8
      iOSClient/Data/NCManageDatabase+Tag.swift
  63. 0 1
      iOSClient/Data/NCManageDatabase+Tip.swift
  64. 2 10
      iOSClient/Data/NCManageDatabase+Trash.swift
  65. 1 4
      iOSClient/Data/NCManageDatabase+UserStatus.swift
  66. 0 7
      iOSClient/Data/NCManageDatabase+Video.swift
  67. 56 98
      iOSClient/Data/NCManageDatabase.swift
  68. 1 1
      iOSClient/Extensions/UIAlertController+Extension.swift
  69. 9 0
      iOSClient/Extensions/UIView+Extension.swift
  70. 1 9
      iOSClient/Favorites/NCFavorite.swift
  71. 5 14
      iOSClient/Files/NCFiles.swift
  72. 2 11
      iOSClient/Groupfolders/NCGroupfolders.swift
  73. 0 15
      iOSClient/Images.xcassets/qrcode.imageset/Contents.json
  74. BIN
      iOSClient/Images.xcassets/qrcode.imageset/qrcode.pdf
  75. 38 152
      iOSClient/Login/NCLogin.swift
  76. 6 10
      iOSClient/Login/NCLoginPoll.swift
  77. 1 8
      iOSClient/Login/NCLoginProvider.swift
  78. 0 5
      iOSClient/Login/NCLoginQRCode.swift
  79. 7 10
      iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift
  80. 18 66
      iOSClient/Main/Collection Common/Cell/NCGridCell.swift
  81. 43 69
      iOSClient/Main/Collection Common/Cell/NCGridCell.xib
  82. 6 50
      iOSClient/Main/Collection Common/Cell/NCListCell.swift
  83. 9 9
      iOSClient/Main/Collection Common/Cell/NCListCell.xib
  84. 145 0
      iOSClient/Main/Collection Common/Cell/NCPhotoCell.swift
  85. 82 0
      iOSClient/Main/Collection Common/Cell/NCPhotoCell.xib
  86. 528 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift
  87. 115 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift
  88. 35 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
  89. 8 7
      iOSClient/Main/Collection Common/NCCollectionViewCommon+DragDrop.swift
  90. 70 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+EasyTipView.swift
  91. 33 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+EndToEndInitialize.swift
  92. 78 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+MediaLayout.swift
  93. 1 42
      iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift
  94. 235 673
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  95. 1 8
      iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift
  96. 1 3
      iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift
  97. 10 142
      iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift
  98. 1 1
      iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib
  99. 5 7
      iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift
  100. 2 2
      iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.xib

+ 1 - 1
Brand/Database.swift

@@ -26,4 +26,4 @@ import Foundation
 // Database Realm
 //
 let databaseName                    = "nextcloud.realm"
-let databaseSchemaVersion: UInt64   = 348
+let databaseSchemaVersion: UInt64   = 354

+ 4 - 2
Brand/NCBrand.swift

@@ -52,7 +52,7 @@ let userAgent: String = {
     var folderDefaultAutoUpload: String = "Photos"
 
     // Capabilities Group
-    var capabilitiesGroups: String = "group.it.twsweb.Crypto-Cloud"
+    var capabilitiesGroup: String = "group.it.twsweb.Crypto-Cloud"
     var capabilitiesGroupApps: String = "group.com.nextcloud.apps"
 
     // BRAND ONLY
@@ -257,6 +257,7 @@ class NCBrandColor: NSObject {
 
     private func stepCalc(steps: Int, color1: CGColor, color2: CGColor) -> [CGFloat] {
         var step = [CGFloat](repeating: 0, count: 3)
+
         step[0] = (color2.components![0] - color1.components![0]) / CGFloat(steps)
         step[1] = (color2.components![1] - color1.components![1]) / CGFloat(steps)
         step[2] = (color2.components![2] - color1.components![2]) / CGFloat(steps)
@@ -266,8 +267,8 @@ class NCBrandColor: NSObject {
     private func mixPalette(steps: Int, color1: CGColor, color2: CGColor) -> [CGColor] {
         var palette = [color1]
         let step = stepCalc(steps: steps, color1: color1, color2: color2)
-
         let c1Components = color1.components!
+
         for i in 1 ..< steps {
             let r = c1Components[0] + step[0] * CGFloat(i)
             let g = c1Components[1] + step[1] * CGFloat(i)
@@ -292,6 +293,7 @@ class NCBrandColor: NSObject {
         let palette1 = mixPalette(steps: steps, color1: red, color2: yellow)
         let palette2 = mixPalette(steps: steps, color1: yellow, color2: blue)
         let palette3 = mixPalette(steps: steps, color1: blue, color2: red)
+
         return palette1 + palette2 + palette3
     }
 }

+ 1 - 1
Cartfile.resolved

@@ -1,2 +1,2 @@
-binary "https://code.videolan.org/videolan/VLCKit/raw/master/Packaging/MobileVLCKit.json" "3.5.1"
+binary "https://code.videolan.org/videolan/VLCKit/raw/master/Packaging/MobileVLCKit.json" "3.6.0"
 github "marinofaggiana/TOPasscodeViewController" "ed795637acd2b1ef154e011a04ebab4686d0523c"

+ 19 - 74
File Provider Extension/FileProviderData.swift

@@ -40,36 +40,30 @@ class fileProviderData: NSObject {
     var accountUrlBase = ""
     var homeServerUrl = ""
 
-    // Max item for page
-    let itemForPage = 100
-
-    // Anchor
-    var currentAnchor: UInt64 = 0
-
-    // Rank favorite
     var listFavoriteIdentifierRank: [String: NSNumber] = [:]
 
-    // Item for signalEnumerator
     var fileProviderSignalDeleteContainerItemIdentifier: [NSFileProviderItemIdentifier: NSFileProviderItemIdentifier] = [:]
     var fileProviderSignalUpdateContainerItem: [NSFileProviderItemIdentifier: FileProviderItem] = [:]
     var fileProviderSignalDeleteWorkingSetItemIdentifier: [NSFileProviderItemIdentifier: NSFileProviderItemIdentifier] = [:]
     var fileProviderSignalUpdateWorkingSetItem: [NSFileProviderItemIdentifier: FileProviderItem] = [:]
 
-    // Error
     enum FileProviderError: Error {
         case downloadError
         case uploadError
     }
 
+    enum TypeSignal: String {
+        case delete
+        case update
+        case workingSet
+    }
+
     // MARK: - 
 
     func setupAccount(domain: NSFileProviderDomain?, providerExtension: NSFileProviderExtension) -> tableAccount? {
-
         self.domain = domain
-        if domain != nil {
-            if let fileProviderManager = NSFileProviderManager(for: domain!) {
-                self.fileProviderManager = fileProviderManager
-            }
+        if let domain, let fileProviderManager = NSFileProviderManager(for: domain) {
+            self.fileProviderManager = fileProviderManager
         }
 
         // LOG
@@ -81,9 +75,7 @@ class fileProviderData: NSObject {
 
         // NO DOMAIN -> Set default account
         if domain == nil {
-
             guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return nil }
-
             account = activeAccount.account
             user = activeAccount.user
             userId = activeAccount.userId
@@ -91,8 +83,8 @@ class fileProviderData: NSObject {
             homeServerUrl = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId)
 
             NCManageDatabase.shared.setCapabilities(account: account)
-
-            NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
+            NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup, delegate: NCNetworking.shared)
+            NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
 
             return tableAccount.init(value: activeAccount)
         }
@@ -106,7 +98,6 @@ class fileProviderData: NSObject {
             guard let host = url.host else { continue }
             let accountDomain = activeAccount.userId + " (" + host + ")"
             if accountDomain == domain!.identifier.rawValue {
-
                 account = activeAccount.account
                 user = activeAccount.user
                 userId = activeAccount.userId
@@ -115,84 +106,38 @@ class fileProviderData: NSObject {
 
                 NCManageDatabase.shared.setCapabilities(account: account)
 
-                NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
+                NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup, delegate: NCNetworking.shared)
+                NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
 
                 return tableAccount.init(value: activeAccount)
             }
         }
-
         return nil
     }
 
     // MARK: -
 
     @discardableResult
-    func signalEnumerator(ocId: String, delete: Bool = false, update: Bool = false) -> FileProviderItem? {
-
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return nil }
-
-        guard let parentItemIdentifier = fileProviderUtility().getParentItemIdentifier(metadata: metadata) else { return nil }
-
+    func signalEnumerator(ocId: String, type: TypeSignal) -> FileProviderItem? {
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              let parentItemIdentifier = fileProviderUtility().getParentItemIdentifier(metadata: metadata) else { return nil }
         let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
 
-        if delete {
+        if type == .delete {
             fileProviderData.shared.fileProviderSignalDeleteContainerItemIdentifier[item.itemIdentifier] = item.itemIdentifier
             fileProviderData.shared.fileProviderSignalDeleteWorkingSetItemIdentifier[item.itemIdentifier] = item.itemIdentifier
         }
-
-        if update {
+        if type == .update {
             fileProviderData.shared.fileProviderSignalUpdateContainerItem[item.itemIdentifier] = item
             fileProviderData.shared.fileProviderSignalUpdateWorkingSetItem[item.itemIdentifier] = item
         }
-
-        if !update && !delete {
+        if type == .workingSet {
             fileProviderData.shared.fileProviderSignalUpdateWorkingSetItem[item.itemIdentifier] = item
         }
-
-        if update || delete {
-            currentAnchor += 1
+        if type == .delete || type == .update {
             fileProviderManager.signalEnumerator(for: parentItemIdentifier) { _ in }
         }
-
         fileProviderManager.signalEnumerator(for: .workingSet) { _ in }
-
         return item
     }
-
-    /*
-     func updateFavoriteForWorkingSet() {
-         
-         var updateWorkingSet = false
-         let oldListFavoriteIdentifierRank = listFavoriteIdentifierRank
-         listFavoriteIdentifierRank = NCManageDatabase.shared.getTableMetadatasDirectoryFavoriteIdentifierRank(account: account)
-         
-         // (ADD)
-         for (identifier, _) in listFavoriteIdentifierRank {
-             
-             guard let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "ocId == %@", identifier)) else { continue }
-             guard let parentItemIdentifier = fileProviderUtility.sharedInstance.getParentItemIdentifier(metadata: metadata, homeServerUrl: homeServerUrl) else { continue }
-             let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
-                 
-             fileProviderSignalUpdateWorkingSetItem[item.itemIdentifier] = item
-             updateWorkingSet = true
-         }
-         
-         // (REMOVE)
-         for (identifier, _) in oldListFavoriteIdentifierRank {
-             
-             if !listFavoriteIdentifierRank.keys.contains(identifier) {
-                 
-                 guard let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "ocId == %@", identifier)) else { continue }
-                 let itemIdentifier = fileProviderUtility.sharedInstance.getItemIdentifier(metadata: metadata)
-                 
-                 fileProviderSignalDeleteWorkingSetItemIdentifier[itemIdentifier] = itemIdentifier
-                 updateWorkingSet = true
-             }
-         }
-         
-         if updateWorkingSet {
-             signalEnumerator(for: [.workingSet])
-         }
-     }
-     */
 }

+ 8 - 16
File Provider Extension/FileProviderDomain.swift

@@ -24,11 +24,8 @@
 import UIKit
 
 class FileProviderDomain: NSObject {
-
     func registerDomains() {
-
         NSFileProviderManager.getDomainsWithCompletionHandler { fileProviderDomain, error in
-
             var domains: [String] = []
             let pathRelativeToDocumentStorage = NSFileProviderManager.default.documentStorageURL.absoluteString
             let accounts = NCManageDatabase.shared.getAllAccount()
@@ -50,10 +47,10 @@ class FileProviderDomain: NSObject {
                     }
                 }
                 if !domainFound {
-                    let domainRawValue = NSFileProviderDomain(identifier: NSFileProviderDomainIdentifier(rawValue: domain), displayName: domain, pathRelativeToDocumentStorage: pathRelativeToDocumentStorage)
-                    NSFileProviderManager.remove(domainRawValue, completionHandler: { error in
-                        if error != nil {
-                            print("Error  domain: \(domainRawValue) error: \(String(describing: error))")
+                    let fileProviderDomain = NSFileProviderDomain(identifier: NSFileProviderDomainIdentifier(rawValue: domain), displayName: domain, pathRelativeToDocumentStorage: pathRelativeToDocumentStorage)
+                    NSFileProviderManager.remove(fileProviderDomain, completionHandler: { error in
+                        if let error {
+                            print("Error  domain: \(fileProviderDomain) error: \(String(describing: error))")
                         }
                     })
                 }
@@ -72,19 +69,14 @@ class FileProviderDomain: NSObject {
                     }
                 }
                 if !domainFound {
-                    let domainRawValue = NSFileProviderDomain(identifier: NSFileProviderDomainIdentifier(rawValue: accountDomain), displayName: accountDomain, pathRelativeToDocumentStorage: pathRelativeToDocumentStorage)
-                    NSFileProviderManager.add(domainRawValue, completionHandler: { error in
-                        if error != nil {
-                            print("Error  domain: \(domainRawValue) error: \(String(describing: error))")
+                    let fileProviderDomain = NSFileProviderDomain(identifier: NSFileProviderDomainIdentifier(rawValue: accountDomain), displayName: accountDomain, pathRelativeToDocumentStorage: pathRelativeToDocumentStorage)
+                    NSFileProviderManager.add(fileProviderDomain, completionHandler: { error in
+                        if let error {
+                            print("Error  domain: \(fileProviderDomain) error: \(String(describing: error))")
                         }
                     })
                 }
             }
         }
     }
-
-    func removeAllDomains() {
-
-        NSFileProviderManager.removeAllDomains { _ in }
-    }
 }

+ 59 - 83
File Provider Extension/FileProviderEnumerator.swift

@@ -23,87 +23,93 @@
 
 import UIKit
 import FileProvider
+import RealmSwift
 import NextcloudKit
 
 class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
     var enumeratedItemIdentifier: NSFileProviderItemIdentifier
     var serverUrl: String?
-    let fpUtility = fileProviderUtility()
+    let providerUtility = fileProviderUtility()
+    var recordsPerPage: Int = 20
+    var anchor: UInt64 = 0
 
     init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) {
         self.enumeratedItemIdentifier = enumeratedItemIdentifier
-
-        // Select ServerUrl
         if enumeratedItemIdentifier == .rootContainer {
             serverUrl = fileProviderData.shared.homeServerUrl
         } else {
-            let metadata = fpUtility.getTableMetadataFromItemIdentifier(enumeratedItemIdentifier)
-            if metadata != nil {
-                if let directorySource = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata!.account, metadata!.serverUrl)) {
-                    serverUrl = directorySource.serverUrl + "/" + metadata!.fileName
-                }
+            if let metadata = providerUtility.getTableMetadataFromItemIdentifier(enumeratedItemIdentifier),
+               let directorySource = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+                serverUrl = directorySource.serverUrl + "/" + metadata.fileName
+
             }
         }
-
         super.init()
     }
 
-    func invalidate() {
-
-    }
+    func invalidate() { }
 
     func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) {
         var items: [NSFileProviderItemProtocol] = []
-
-        /*** WorkingSet ***/
+        /// WorkingSet
         if enumeratedItemIdentifier == .workingSet {
             var itemIdentifierMetadata: [NSFileProviderItemIdentifier: tableMetadata] = [:]
-
-            // ***** Tags *****
+            /// Tags
             let tags = NCManageDatabase.shared.getTags(predicate: NSPredicate(format: "account == %@", fileProviderData.shared.account))
             for tag in tags {
-
                 guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(tag.ocId)  else { continue }
-                fpUtility.createocIdentifierOnFileSystem(metadata: metadata)
-                itemIdentifierMetadata[fpUtility.getItemIdentifier(metadata: metadata)] = metadata
+                itemIdentifierMetadata[providerUtility.getItemIdentifier(metadata: metadata)] = metadata
             }
-
-            // ***** Favorite *****
+            /// Favorite
             fileProviderData.shared.listFavoriteIdentifierRank = NCManageDatabase.shared.getTableMetadatasDirectoryFavoriteIdentifierRank(account: fileProviderData.shared.account)
             for (identifier, _) in fileProviderData.shared.listFavoriteIdentifierRank {
-
                 guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(identifier) else { continue }
-                itemIdentifierMetadata[fpUtility.getItemIdentifier(metadata: metadata)] = metadata
+                itemIdentifierMetadata[providerUtility.getItemIdentifier(metadata: metadata)] = metadata
             }
-
-            // create items
+            /// Create items
             for (_, metadata) in itemIdentifierMetadata {
-                let parentItemIdentifier = fpUtility.getParentItemIdentifier(metadata: metadata)
-                if parentItemIdentifier != nil {
-                    let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier!)
+                if let parentItemIdentifier = providerUtility.getParentItemIdentifier(metadata: metadata) {
+                    let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
                     items.append(item)
                 }
             }
-
             observer.didEnumerate(items)
             observer.finishEnumerating(upTo: nil)
-
         } else {
-
-        /*** ServerUrl ***/
-
+            /// ServerUrl
             guard let serverUrl = serverUrl else {
                 observer.finishEnumerating(upTo: nil)
                 return
             }
+            var pageNumber = 1
+            if let stringPage = String(data: page.rawValue, encoding: .utf8),
+               let intPage = Int(stringPage) {
+                pageNumber = intPage
+            }
 
-            if page == NSFileProviderPage.initialPageSortedByDate as NSFileProviderPage || page == NSFileProviderPage.initialPageSortedByName as NSFileProviderPage {
-                self.readFileOrFolder(serverUrl: serverUrl) { metadatas in
-                    self.completeObserver(observer, numPage: 1, metadatas: metadatas)
+            self.fetchItemsForPage(serverUrl: serverUrl, pageNumber: pageNumber) { metadatas in
+                if let metadatas {
+                    for metadata in metadatas {
+                        if metadata.e2eEncrypted || (!metadata.session.isEmpty && metadata.session != NCNetworking.shared.sessionUploadBackgroundExtension) {
+                            continue
+                        }
+                        if let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadata) {
+                            let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
+                            items.append(item)
+                        }
+                    }
+                }
+
+                observer.didEnumerate(items)
+
+                if let metadatas,
+                    metadatas.count == self.recordsPerPage {
+                    pageNumber += 1
+                    let providerPage = NSFileProviderPage("\(pageNumber)".data(using: .utf8)!)
+                    observer.finishEnumerating(upTo: providerPage)
+                } else {
+                    observer.finishEnumerating(upTo: nil)
                 }
-            } else {
-                let numPage = Int(String(data: page.rawValue, encoding: .utf8)!)!
-                completeObserver(observer, numPage: numPage, metadatas: nil)
             }
         }
     }
@@ -111,7 +117,6 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
     func enumerateChanges(for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor) {
         var itemsDelete: [NSFileProviderItemIdentifier] = []
         var itemsUpdate: [FileProviderItem] = []
-
         // Report the deleted items
         //
         if self.enumeratedItemIdentifier == .workingSet {
@@ -125,7 +130,6 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
             }
             fileProviderData.shared.fileProviderSignalDeleteContainerItemIdentifier.removeAll()
         }
-
         // Report the updated items
         //
         if self.enumeratedItemIdentifier == .workingSet {
@@ -143,64 +147,36 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
         observer.didDeleteItems(withIdentifiers: itemsDelete)
         observer.didUpdate(itemsUpdate)
 
-        let data = "\(fileProviderData.shared.currentAnchor)".data(using: .utf8)
+        let data = "\(self.anchor)".data(using: .utf8)
         observer.finishEnumeratingChanges(upTo: NSFileProviderSyncAnchor(data!), moreComing: false)
     }
 
     func currentSyncAnchor(completionHandler: @escaping (NSFileProviderSyncAnchor?) -> Void) {
-        let data = "\(fileProviderData.shared.currentAnchor)".data(using: .utf8)
+        let data = "\(self.anchor)".data(using: .utf8)
         completionHandler(NSFileProviderSyncAnchor(data!))
     }
 
-    // --------------------------------------------------------------------------------------------
-    // MARK: - User Function + Network
-    // --------------------------------------------------------------------------------------------
-
-    func completeObserver(_ observer: NSFileProviderEnumerationObserver, numPage: Int, metadatas: [tableMetadata]?) {
-        var numPage = numPage
-        var items: [NSFileProviderItemProtocol] = []
-
-        if metadatas != nil {
-            for metadata in metadatas! {
-                if metadata.e2eEncrypted || (!metadata.session.isEmpty && metadata.session != NCNetworking.shared.sessionUploadBackgroundExtension) { continue }
-                fpUtility.createocIdentifierOnFileSystem(metadata: metadata)
-                let parentItemIdentifier = fpUtility.getParentItemIdentifier(metadata: metadata)
-                if parentItemIdentifier != nil {
-                    let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier!)
-                    items.append(item)
-                }
-            }
-            observer.didEnumerate(items)
-        }
-
-        if items.count == fileProviderData.shared.itemForPage {
-            numPage += 1
-            let providerPage = NSFileProviderPage("\(numPage)".data(using: .utf8)!)
-            observer.finishEnumerating(upTo: providerPage)
-        } else {
-            observer.finishEnumerating(upTo: nil)
-        }
-    }
+    func fetchItemsForPage(serverUrl: String, pageNumber: Int, completionHandler: @escaping (_ metadatas: Results<tableMetadata>?) -> Void) {
+        let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl)
 
-    func readFileOrFolder(serverUrl: String, completionHandler: @escaping (_ metadatas: [tableMetadata]?) -> Void) {
-        NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: NCKeychain().showHiddenFiles) { account, files, _, error in
-            if error == .success {
-                DispatchQueue.global().async {
+        if pageNumber == 1 {
+            NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: NCKeychain().showHiddenFiles) { _, files, _, error in
+                if error == .success {
                     NCManageDatabase.shared.convertFilesToMetadatas(files, useFirstAsMetadataFolder: true) { metadataFolder, metadatas in
                         /// FOLDER
                         NCManageDatabase.shared.addMetadata(metadataFolder)
                         NCManageDatabase.shared.addDirectory(e2eEncrypted: metadataFolder.e2eEncrypted, favorite: metadataFolder.favorite, ocId: metadataFolder.ocId, fileId: metadataFolder.fileId, etag: metadataFolder.etag, permissions: metadataFolder.permissions, richWorkspace: metadataFolder.richWorkspace, serverUrl: serverUrl, account: metadataFolder.account)
                         /// FILES
-                        let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal)
-                        NCManageDatabase.shared.updateMetadatas(metadatas, predicate: predicate)
-                        let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
-                        completionHandler(metadatas)
+                        NCManageDatabase.shared.deleteMetadata(predicate: predicate)
+                        NCManageDatabase.shared.addMetadatas(metadatas)
                     }
                 }
-            } else {
-                let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
-                completionHandler(metadatas)
+                let resultsMetadata = NCManageDatabase.shared.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: self.recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
+                completionHandler(resultsMetadata)
             }
+        } else {
+            let resultsMetadata = NCManageDatabase.shared.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
+            completionHandler(resultsMetadata)
         }
     }
 }

+ 44 - 125
File Provider Extension/FileProviderExtension+Actions.swift

@@ -26,49 +26,33 @@ import FileProvider
 import NextcloudKit
 
 extension FileProviderExtension {
-
     override func createDirectory(withName directoryName: String, inParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
-        guard let tableDirectory = fpUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let tableDirectory = providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
-
         let directoryName = utilityFileSystem.createFileName(directoryName, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.account)
         let serverUrlFileName = tableDirectory.serverUrl + "/" + directoryName
 
         NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName) { _, ocId, _, error in
-
             if error == .success {
-
                 NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles) { _, files, _, error in
-
                     if error == .success, let file = files.first {
-
                         let isDirectoryEncrypted = self.utilityFileSystem.isDirectoryE2EE(file: file)
                         let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryEncrypted)
 
                         NCManageDatabase.shared.addDirectory(e2eEncrypted: false, favorite: false, ocId: ocId!, fileId: metadata.fileId, etag: metadata.etag, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
                         NCManageDatabase.shared.addMetadata(metadata)
 
-                        guard let metadataInsert = NCManageDatabase.shared.getMetadataFromOcId(ocId!) else {
-                            completionHandler(nil, NSFileProviderError(.noSuchItem))
-                            return
-                        }
-
-                        guard let parentItemIdentifier = self.fpUtility.getParentItemIdentifier(metadata: metadataInsert) else {
-                            completionHandler(nil, NSFileProviderError(.noSuchItem))
-                            return
+                        guard let metadataInsert = NCManageDatabase.shared.getMetadataFromOcId(ocId!),
+                              let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadataInsert) else {
+                            return completionHandler(nil, NSFileProviderError(.noSuchItem))
                         }
-
                         let item = FileProviderItem(metadata: metadataInsert, parentItemIdentifier: parentItemIdentifier)
                         completionHandler(item, nil)
-
                     } else {
                         completionHandler(nil, NSFileProviderError(.serverUnreachable))
                     }
                 }
-
             } else {
                 completionHandler(nil, NSFileProviderError(.serverUnreachable))
             }
@@ -76,12 +60,9 @@ extension FileProviderExtension {
     }
 
     override func deleteItem(withIdentifier itemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (Error?) -> Void) {
-
-        guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
-            completionHandler(NSFileProviderError(.noSuchItem))
-            return
+        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
+            return completionHandler(NSFileProviderError(.noSuchItem))
         }
-
         let ocId = metadata.ocId
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
         let isDirectory = metadata.directory
@@ -89,12 +70,11 @@ extension FileProviderExtension {
         let fileName = metadata.fileName
 
         NextcloudKit.shared.deleteFileOrFolder(serverUrlFileName: serverUrlFileName) { account, error in
-
             if error == .success { // || error == kOCErrorServerPathNotFound {
-
                 let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue)
+
                 do {
-                    try self.fpUtility.fileManager.removeItem(atPath: fileNamePath)
+                    try self.providerUtility.fileManager.removeItem(atPath: fileNamePath)
                 } catch let error {
                     print("error: \(error)")
                 }
@@ -106,9 +86,7 @@ extension FileProviderExtension {
 
                 NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocId))
                 NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", ocId))
-
                 completionHandler(nil)
-
             } else {
                 completionHandler(NSFileProviderError(.serverUnreachable))
             }
@@ -116,47 +94,34 @@ extension FileProviderExtension {
     }
 
     override func reparentItem(withIdentifier itemIdentifier: NSFileProviderItemIdentifier, toParentItemWithIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, newName: String?, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
-        guard let itemFrom = try? item(for: itemIdentifier) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
-        }
-
-        guard let metadataFrom = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let itemFrom = try? item(for: itemIdentifier),
+              let metadataFrom = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
-
         let ocIdFrom = metadataFrom.ocId
         let serverUrlFrom = metadataFrom.serverUrl
         let fileNameFrom = serverUrlFrom + "/" + itemFrom.filename
-
-        guard let tableDirectoryTo = fpUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let tableDirectoryTo = providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
         let serverUrlTo = tableDirectoryTo.serverUrl
         let fileNameTo = serverUrlTo + "/" + itemFrom.filename
 
         NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNameFrom, serverUrlFileNameDestination: fileNameTo, overwrite: false) { account, error in
-
             if error == .success {
-
                 if metadataFrom.directory {
                     NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: serverUrlFrom, account: account)
                     NCManageDatabase.shared.renameDirectory(ocId: ocIdFrom, serverUrl: serverUrlTo)
                 }
-
                 NCManageDatabase.shared.moveMetadata(ocId: ocIdFrom, serverUrlTo: serverUrlTo)
 
                 guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocIdFrom) else {
-                    completionHandler(nil, NSFileProviderError(.noSuchItem))
-                    return
-                }
+                    return completionHandler(nil, NSFileProviderError(.noSuchItem))
 
+                }
                 let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
-                completionHandler(item, nil)
 
+                completionHandler(item, nil)
             } else {
                 completionHandler(nil, NSFileProviderError(.serverUnreachable))
             }
@@ -164,60 +129,38 @@ extension FileProviderExtension {
     }
 
     override func renameItem(withIdentifier itemIdentifier: NSFileProviderItemIdentifier, toName itemName: String, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
-        guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier),
+              let directoryTable = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
-
-        guard let directoryTable = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
-        }
-
         let fileNameFrom = metadata.fileNameView
         let fileNamePathFrom = metadata.serverUrl + "/" + fileNameFrom
         let fileNamePathTo = metadata.serverUrl + "/" + itemName
         let ocId = metadata.ocId
 
         NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNamePathFrom, serverUrlFileNameDestination: fileNamePathTo, overwrite: false) { account, error in
-
             if error == .success {
-
                 // Rename metadata
                 NCManageDatabase.shared.renameMetadata(fileNameTo: itemName, ocId: ocId)
 
                 guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
-                    completionHandler(nil, NSFileProviderError(.noSuchItem))
-                    return
+                    return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
-
                 if metadata.directory {
-
                     NCManageDatabase.shared.setDirectory(serverUrl: fileNamePathFrom, serverUrlTo: fileNamePathTo, encrypted: directoryTable.e2eEncrypted, account: account)
-
                 } else {
-
-                    let itemIdentifier = self.fpUtility.getItemIdentifier(metadata: metadata)
-
-                    // rename file
-                    _ = self.fpUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: fileNameFrom), toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: itemName))
-
-                    _ = self.fpUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(itemIdentifier.rawValue, etag: metadata.etag), toPath: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(itemIdentifier.rawValue, etag: metadata.etag))
-
-                    _ = self.fpUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStorageIconOcId(itemIdentifier.rawValue, etag: metadata.etag), toPath: self.utilityFileSystem.getDirectoryProviderStorageIconOcId(itemIdentifier.rawValue, etag: metadata.etag))
-
+                    let itemIdentifier = self.providerUtility.getItemIdentifier(metadata: metadata)
+                    self.providerUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: fileNameFrom), toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: itemName))
+                    self.providerUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(itemIdentifier.rawValue, etag: metadata.etag), toPath: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(itemIdentifier.rawValue, etag: metadata.etag))
+                    self.providerUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStorageIconOcId(itemIdentifier.rawValue, etag: metadata.etag), toPath: self.utilityFileSystem.getDirectoryProviderStorageIconOcId(itemIdentifier.rawValue, etag: metadata.etag))
                     NCManageDatabase.shared.setLocalFile(ocId: ocId, fileName: itemName)
                 }
 
-                guard let parentItemIdentifier = self.fpUtility.getParentItemIdentifier(metadata: metadata) else {
-                    completionHandler(nil, NSFileProviderError(.noSuchItem))
-                    return
+                guard let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadata) else {
+                    return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
-
                 let item = FileProviderItem(metadata: tableMetadata.init(value: metadata), parentItemIdentifier: parentItemIdentifier)
                 completionHandler(item, nil)
-
             } else {
                 completionHandler(nil, NSFileProviderError(.serverUnreachable))
             }
@@ -225,20 +168,16 @@ extension FileProviderExtension {
     }
 
     override func setFavoriteRank(_ favoriteRank: NSNumber?, forItemIdentifier itemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
-        guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
-
         var favorite = false
         let ocId = metadata.ocId
 
         if favoriteRank == nil {
             fileProviderData.shared.listFavoriteIdentifierRank.removeValue(forKey: itemIdentifier.rawValue)
         } else {
-            let rank = fileProviderData.shared.listFavoriteIdentifierRank[itemIdentifier.rawValue]
-            if rank == nil {
+            if fileProviderData.shared.listFavoriteIdentifierRank[itemIdentifier.rawValue] == nil {
                 fileProviderData.shared.listFavoriteIdentifierRank[itemIdentifier.rawValue] = favoriteRank
             }
             favorite = true
@@ -246,35 +185,25 @@ extension FileProviderExtension {
 
         if (favorite == true && metadata.favorite == false) || (favorite == false && metadata.favorite == true) {
             let fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
-
             NextcloudKit.shared.setFavorite(fileName: fileNamePath, favorite: favorite) { _, error in
-
                 if error == .success {
-
-                    guard let metadataTemp = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
-                        completionHandler(nil, NSFileProviderError(.noSuchItem))
-                        return
+                    guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
+                        return completionHandler(nil, NSFileProviderError(.noSuchItem))
                     }
-                    let metadata = tableMetadata.init(value: metadataTemp)
-
                     // Change DB
                     metadata.favorite = favorite
                     NCManageDatabase.shared.addMetadata(metadata)
-
-                    let item = fileProviderData.shared.signalEnumerator(ocId: metadata.ocId)
+                    /// SIGNAL
+                    let item = fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .workingSet)
                     completionHandler(item, nil)
-
                 } else {
-
                     guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
-                        completionHandler(nil, NSFileProviderError(.noSuchItem))
-                        return
+                        return completionHandler(nil, NSFileProviderError(.noSuchItem))
                     }
-
                     // Errore, remove from listFavoriteIdentifierRank
                     fileProviderData.shared.listFavoriteIdentifierRank.removeValue(forKey: itemIdentifier.rawValue)
-
-                    let item = fileProviderData.shared.signalEnumerator(ocId: metadata.ocId)
+                    /// SIGNAL
+                    let item = fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .workingSet)
                     completionHandler(item, NSFileProviderError(.serverUnreachable))
                 }
             }
@@ -282,33 +211,23 @@ extension FileProviderExtension {
     }
 
     override func setTagData(_ tagData: Data?, forItemIdentifier itemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
-        guard let metadataForTag = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let metadataForTag = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
         let ocId = metadataForTag.ocId
         let account = metadataForTag.account
 
-        // Add, Remove (nil)
         NCManageDatabase.shared.addTag(ocId, tagIOS: tagData, account: account)
-
-        let item = fileProviderData.shared.signalEnumerator(ocId: ocId)
+        /// SIGNAL WORKINGSET
+        let item = fileProviderData.shared.signalEnumerator(ocId: ocId, type: .workingSet)
         completionHandler(item, nil)
     }
 
     override func setLastUsedDate(_ lastUsedDate: Date?, forItemIdentifier itemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
-        guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
-        }
-
-        guard let parentItemIdentifier = fpUtility.getParentItemIdentifier(metadata: metadata) else {
-            completionHandler(nil, NSFileProviderError(.noSuchItem))
-            return
+        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier),
+              let parentItemIdentifier = providerUtility.getParentItemIdentifier(metadata: metadata) else {
+            return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
-
         let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
         completionHandler(item, nil)
     }

+ 72 - 0
File Provider Extension/FileProviderExtension+NetworkingDelegate.swift

@@ -0,0 +1,72 @@
+//
+//  FileProviderExtension+x.swift
+//  File Provider Extension
+//
+//  Created by Marino Faggiana on 11/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+import UniformTypeIdentifiers
+import FileProvider
+import NextcloudKit
+import Alamofire
+
+extension FileProviderExtension: NCNetworkingDelegate {
+    func downloadComplete(fileName: String, serverUrl: String, etag: String?, date: Date?, dateLastModified: Date?, length: Int64, task: URLSessionTask, error: NKError) { }
+    func downloadProgress(_ progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String, session: URLSession, task: URLSessionTask) { }
+    func uploadProgress(_ progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String, session: URLSession, task: URLSessionTask) { }
+
+    func uploadComplete(fileName: String, serverUrl: String, ocId: String?, etag: String?, date: Date?, size: Int64, task: URLSessionTask, error: NKError) {
+        guard let url = task.currentRequest?.url,
+              let metadata = NCManageDatabase.shared.getMetadata(from: url, sessionTaskIdentifier: task.taskIdentifier) else { return }
+        if error == .success, let ocId {
+            /// SIGNAL
+            fileProviderData.shared.signalEnumerator(ocId: metadata.ocIdTemp, type: .delete)
+            metadata.fileName = fileName
+            metadata.serverUrl = serverUrl
+            metadata.uploadDate = (date as? NSDate) ?? NSDate()
+            metadata.etag = etag ?? ""
+            metadata.ocId = ocId
+            metadata.size = size
+            if let fileId = NCUtility().ocIdToFileId(ocId: ocId) {
+                metadata.fileId = fileId
+            }
+            metadata.session = ""
+            metadata.sessionError = ""
+            metadata.status = NCGlobal.shared.metadataStatusNormal
+
+            NCManageDatabase.shared.addMetadata(metadata)
+            NCManageDatabase.shared.addLocalFile(metadata: metadata)
+            /// NEW File
+            if ocId != metadata.ocIdTemp {
+                let atPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocIdTemp)
+                let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(ocId)
+                utilityFileSystem.copyFile(atPath: atPath, toPath: toPath)
+                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocIdTemp))
+            }
+            /// SIGNAL
+            fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .update)
+        } else {
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocIdTemp))
+            /// SIGNAL
+            fileProviderData.shared.signalEnumerator(ocId: metadata.ocIdTemp, type: .delete)
+        }
+    }
+}

+ 12 - 32
File Provider Extension/FileProviderExtension+Thumbnail.swift

@@ -26,54 +26,34 @@ import FileProvider
 import NextcloudKit
 
 extension FileProviderExtension {
-
     override func fetchThumbnails(for itemIdentifiers: [NSFileProviderItemIdentifier], requestedSize size: CGSize, perThumbnailCompletionHandler: @escaping (NSFileProviderItemIdentifier, Data?, Error?) -> Void, completionHandler: @escaping (Error?) -> Void) -> Progress {
-
         let progress = Progress(totalUnitCount: Int64(itemIdentifiers.count))
         var counterProgress: Int64 = 0
 
         for itemIdentifier in itemIdentifiers {
-
-            guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
+            guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier), metadata.hasPreview else {
                 counterProgress += 1
                 if counterProgress == progress.totalUnitCount { completionHandler(nil) }
                 continue
             }
-
-            if metadata.hasPreview {
-
-                let fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
-                let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)
-
-                if let urlBase = metadata.urlBase.urlEncoded,
-                   let fileNamePath = fileNamePath.urlEncoded,
-                   let url = URL(string: "\(urlBase)/index.php/core/preview.png?file=\(fileNamePath)&x=\(size.width)&y=\(size.height)&a=1&mode=cover") {
-
-                    NextcloudKit.shared.getPreview(url: url) { _, data, error in
-                        if error == .success && data != nil {
-                            do {
-                                try data!.write(to: URL(fileURLWithPath: fileNameIconLocalPath), options: .atomic)
-                            } catch { }
-                            perThumbnailCompletionHandler(itemIdentifier, data, nil)
-                        } else {
-                            perThumbnailCompletionHandler(itemIdentifier, nil, NSFileProviderError(.serverUnreachable))
-                        }
-                        counterProgress += 1
-                        if counterProgress == progress.totalUnitCount {
-                            completionHandler(nil)
-                        }
-                    }
+            let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)
+
+            NextcloudKit.shared.downloadPreview(fileId: metadata.fileId, widthPreview: Int(size.width), heightPreview: Int(size.height), etag: metadata.etag) { _ in
+            } completion: { _, data, error in
+                if error == .success, let data {
+                    do {
+                        try data.write(to: URL(fileURLWithPath: fileNameIconLocalPath), options: .atomic)
+                    } catch { }
+                    perThumbnailCompletionHandler(itemIdentifier, data, nil)
+                } else {
+                    perThumbnailCompletionHandler(itemIdentifier, nil, NSFileProviderError(.serverUnreachable))
                 }
-
-            } else {
                 counterProgress += 1
                 if counterProgress == progress.totalUnitCount {
                     completionHandler(nil)
                 }
             }
         }
-
         return progress
     }
-
 }

+ 65 - 187
File Provider Extension/FileProviderExtension.swift

@@ -53,10 +53,7 @@ import Alamofire
    -------------------------------------------------------------------------------------------------------------------------------------------- */
 
 class FileProviderExtension: NSFileProviderExtension {
-
-    var outstandingSessionTasks: [URL: URLSessionTask] = [:]
-    var outstandingOcIdTemp: [String: String] = [:]
-    var fpUtility = fileProviderUtility()
+    let providerUtility = fileProviderUtility()
     let utilityFileSystem = NCUtilityFileSystem()
 
     override init() {
@@ -66,6 +63,8 @@ class FileProviderExtension: NSFileProviderExtension {
         _ = utilityFileSystem.directoryProviderStorage
         // Configure URLSession
         _ = NCNetworking.shared.sessionManagerUploadBackgroundExtension
+        // Domains
+        // FileProviderDomain().registerDomains()
     }
 
     deinit {
@@ -75,7 +74,6 @@ class FileProviderExtension: NSFileProviderExtension {
     // MARK: - Enumeration
 
     override func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier) throws -> NSFileProviderEnumerator {
-
         var maybeEnumerator: NSFileProviderEnumerator?
 
         if containerItemIdentifier != NSFileProviderItemIdentifier.workingSet {
@@ -114,11 +112,8 @@ class FileProviderExtension: NSFileProviderExtension {
     // MARK: - Item
 
     override func item(for identifier: NSFileProviderItemIdentifier) throws -> NSFileProviderItem {
-
         if identifier == .rootContainer {
-
             let metadata = tableMetadata()
-
             metadata.account = fileProviderData.shared.account
             metadata.directory = true
             metadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
@@ -126,15 +121,10 @@ class FileProviderExtension: NSFileProviderExtension {
             metadata.fileNameView = "root"
             metadata.serverUrl = fileProviderData.shared.homeServerUrl
             metadata.classFile = NKCommon.TypeClassFile.directory.rawValue
-
             return FileProviderItem(metadata: metadata, parentItemIdentifier: NSFileProviderItemIdentifier(NSFileProviderItemIdentifier.rootContainer.rawValue))
-
         } else {
-
-            guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(identifier) else {
-                throw NSFileProviderError(.noSuchItem)
-            }
-            guard let parentItemIdentifier = fpUtility.getParentItemIdentifier(metadata: metadata) else {
+            guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(identifier),
+                  let parentItemIdentifier = providerUtility.getParentItemIdentifier(metadata: metadata) else {
                 throw NSFileProviderError(.noSuchItem)
             }
             let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
@@ -143,40 +133,26 @@ class FileProviderExtension: NSFileProviderExtension {
     }
 
     override func urlForItem(withPersistentIdentifier identifier: NSFileProviderItemIdentifier) -> URL? {
-
-        // resolve the given identifier to a file on disk
-        guard let item = try? item(for: identifier) else {
-            return nil
-        }
-
+        guard let item = try? item(for: identifier) else { return nil }
         var url = fileProviderData.shared.fileProviderManager.documentStorageURL.appendingPathComponent(identifier.rawValue, isDirectory: true)
-
         // (fix copy/paste directory -> isDirectory = false)
         url = url.appendingPathComponent(item.filename, isDirectory: false)
-
         return url
     }
 
     override func persistentIdentifierForItem(at url: URL) -> NSFileProviderItemIdentifier? {
-
-        // resolve the given URL to a persistent identifier using a database
         let pathComponents = url.pathComponents
-
         // exploit the fact that the path structure has been defined as
         // <base storage directory>/<item identifier>/<item file name> above
         assert(pathComponents.count > 2)
-
         let itemIdentifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
         return itemIdentifier
     }
 
     override func providePlaceholder(at url: URL, completionHandler: @escaping (Error?) -> Void) {
-
         guard let identifier = persistentIdentifierForItem(at: url) else {
-            completionHandler(NSFileProviderError(.noSuchItem))
-            return
+            return completionHandler(NSFileProviderError(.noSuchItem))
         }
-
         do {
             let fileProviderItem = try item(for: identifier)
             let placeholderURL = NSFileProviderManager.placeholderURL(for: url)
@@ -188,169 +164,127 @@ class FileProviderExtension: NSFileProviderExtension {
     }
 
     override func startProvidingItem(at url: URL, completionHandler: @escaping ((_ error: Error?) -> Void)) {
-
         let pathComponents = url.pathComponents
-        let identifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
-
-        if outstandingSessionTasks[url] != nil {
-            return completionHandler(nil)
-        }
-
-        guard let metadata = fpUtility.getTableMetadataFromItemIdentifier(identifier) else {
-            return completionHandler(NSFileProviderError(.noSuchItem))
-        }
-
-        // Document VIEW ONLY
-        if metadata.isDocumentViewableOnly {
+        let itemIdentifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
+        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
             return completionHandler(NSFileProviderError(.noSuchItem))
         }
-
-        let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-        if tableLocalFile != nil && utilityFileSystem.fileProviderStorageExists(metadata) && tableLocalFile?.etag == metadata.etag {
+        if metadata.session == NCNetworking.shared.sessionUploadBackgroundExtension {
             return completionHandler(nil)
         }
-
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
         let fileNameLocalPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)
-
-        // Update status
-        NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusDownloading)
-        fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, update: true)
+        // Exists ? return
+        if let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)),
+           utilityFileSystem.fileProviderStorageExists(metadata),
+           tableLocalFile.etag == metadata.etag {
+            return completionHandler(nil)
+        } else {
+            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                       session: NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload,
+                                                       sessionError: "",
+                                                       selector: "",
+                                                       status: NCGlobal.shared.metadataStatusDownloading)
+        }
+        /// SIGNAL
+        fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .update)
 
         NextcloudKit.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
-
         }, taskHandler: { task in
-
-            self.outstandingSessionTasks[url] = task
-            fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(identifier.rawValue)) { _ in }
-
+            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                       taskIdentifier: task.taskIdentifier)
+            fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(itemIdentifier.rawValue)) { _ in }
         }, progressHandler: { _ in
-
         }) { _, etag, date, _, _, _, error in
-
-            self.outstandingSessionTasks.removeValue(forKey: url)
-            guard var metadata = self.fpUtility.getTableMetadataFromItemIdentifier(identifier) else {
-                completionHandler(NSFileProviderError(.noSuchItem))
-                return
+            guard let metadata = self.providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
+                return completionHandler(NSFileProviderError(.noSuchItem))
             }
-            metadata = tableMetadata.init(value: metadata)
-
             if error == .success {
-
                 metadata.status = NCGlobal.shared.metadataStatusNormal
-                metadata.date = date ?? NSDate()
+                metadata.date = (date as? NSDate) ?? NSDate()
                 metadata.etag = etag ?? ""
-
                 NCManageDatabase.shared.addLocalFile(metadata: metadata)
                 NCManageDatabase.shared.addMetadata(metadata)
-
                 completionHandler(nil)
-
             } else if error.errorCode == 200 {
-
                 NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusNormal)
-
                 completionHandler(nil)
-
             } else {
-
                 metadata.status = NCGlobal.shared.metadataStatusDownloadError
                 metadata.sessionError = error.errorDescription
                 NCManageDatabase.shared.addMetadata(metadata)
-
                 completionHandler(NSFileProviderError(.noSuchItem))
             }
-
-            fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, update: true)
+            /// SIGNAL
+            fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .update)
         }
     }
 
+    /// Upload the changed file
     override func itemChanged(at url: URL) {
-
         let pathComponents = url.pathComponents
         assert(pathComponents.count > 2)
         let itemIdentifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
         let fileName = pathComponents[pathComponents.count - 1]
-        var ocId = itemIdentifier.rawValue
-
-        // Temp ocId ?
-        if outstandingOcIdTemp[ocId] != nil && outstandingOcIdTemp[ocId] != ocId {
-            ocId = outstandingOcIdTemp[ocId]!
-            let atPath = utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: fileName)
-            let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)
-            utilityFileSystem.copyFile(atPath: atPath, toPath: toPath)
-        }
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return }
-
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcIdAndOcIdTemp(itemIdentifier.rawValue) else { return }
         let serverUrlFileName = metadata.serverUrl + "/" + fileName
-        let fileNameLocalPath = url.path
-
+        let fileNameLocalPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)
+        utilityFileSystem.copyFile(atPath: url.path, toPath: fileNameLocalPath)
+        NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                   session: NCNetworking.shared.sessionUploadBackgroundExtension,
+                                                   sessionError: "",
+                                                   selector: "",
+                                                   status: NCGlobal.shared.metadataStatusUploading)
         if let task = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance).upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, session: NCNetworking.shared.sessionManagerUploadBackgroundExtension) {
-
+            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                       status: NCGlobal.shared.metadataStatusUploading,
+                                                       taskIdentifier: task.taskIdentifier)
             fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(metadata.fileId)) { _ in }
         }
     }
 
     override func stopProvidingItem(at url: URL) {
-
-        let fileHasLocalChanges = false
-
-        if !fileHasLocalChanges {
-            // remove the existing file to free up space
-            do {
-                _ = try fpUtility.fileManager.removeItem(at: url)
-            } catch let error {
-                print("error: \(error)")
+        let pathComponents = url.pathComponents
+        assert(pathComponents.count > 2)
+        let itemIdentifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcIdAndOcIdTemp(itemIdentifier.rawValue) else { return }
+        if metadata.session == NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload {
+            NextcloudKit.shared.sessionManager.session.getTasksWithCompletionHandler { _, _, downloadTasks in
+                downloadTasks.forEach { task in
+                    if metadata.sessionTaskIdentifier == task.taskIdentifier {
+                        task.cancel()
+                    }
+                }
             }
-
-            // write out a placeholder to facilitate future property lookups
-            self.providePlaceholder(at: url, completionHandler: { _ in
-                // handle any error, do any necessary cleanup
-            })
-        }
-
-        // Download task
-        if let downloadTask = outstandingSessionTasks[url] {
-            downloadTask.cancel()
-            outstandingSessionTasks.removeValue(forKey: url)
         }
     }
 
     override func importDocument(at fileURL: URL, toParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-
         DispatchQueue.main.async {
-
             autoreleasepool {
-
+                guard let tableDirectory = self.providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
+                    return completionHandler(nil, NSFileProviderError(.noSuchItem))
+                }
                 var size = 0 as Int64
                 var error: NSError?
-
-                guard let tableDirectory = self.fpUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
-                    completionHandler(nil, NSFileProviderError(.noSuchItem))
-                    return
-                }
-
                 _ = fileURL.startAccessingSecurityScopedResource()
-
                 // typefile directory ? (NOT PERMITTED)
                 do {
-                    let attributes = try self.fpUtility.fileManager.attributesOfItem(atPath: fileURL.path)
+                    let attributes = try self.providerUtility.fileManager.attributesOfItem(atPath: fileURL.path)
                     size = attributes[FileAttributeKey.size] as? Int64 ?? 0
                     let typeFile = attributes[FileAttributeKey.type] as? FileAttributeType
                     if typeFile == FileAttributeType.typeDirectory {
-                        completionHandler(nil, NSFileProviderError(.noSuchItem))
-                        return
+                        return completionHandler(nil, NSFileProviderError(.noSuchItem))
                     }
                 } catch {
-                    completionHandler(nil, NSFileProviderError(.noSuchItem))
-                    return
+                    return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
 
                 let fileName = self.utilityFileSystem.createFileName(fileURL.lastPathComponent, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.account)
                 let ocIdTemp = NSUUID().uuidString.lowercased()
 
                 NSFileCoordinator().coordinate(readingItemAt: fileURL, options: .withoutChanges, error: &error) { url in
-                    _ = self.fpUtility.copyFile(url.path, toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName))
+                    self.providerUtility.copyFile(url.path, toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName))
                 }
 
                 fileURL.stopAccessingSecurityScopedResource()
@@ -366,71 +300,15 @@ class FileProviderExtension: NSFileProviderExtension {
                 let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName)
 
                 if let task = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance).upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, session: NCNetworking.shared.sessionManagerUploadBackgroundExtension) {
-
-                    self.outstandingSessionTasks[URL(fileURLWithPath: fileNameLocalPath)] = task as URLSessionTask
-
+                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                               status: NCGlobal.shared.metadataStatusUploading,
+                                                               taskIdentifier: task.taskIdentifier)
                     fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(ocIdTemp)) { _ in }
                 }
 
                 let item = FileProviderItem(metadata: tableMetadata.init(value: metadata), parentItemIdentifier: parentItemIdentifier)
-
                 completionHandler(item, nil)
             }
         }
     }
-
-    func uploadComplete(fileName: String, serverUrl: String, ocId: String?, etag: String?, date: NSDate?, size: Int64, fileNameLocalPath: String?, task: URLSessionTask, error: NKError) {
-
-        guard let metadataTemp = NCManageDatabase.shared.getMetadataFromFileNameLocalPath(fileNameLocalPath) else { return }
-        let ocIdTemp = metadataTemp.ocId
-        let metadata = tableMetadata.init(value: metadataTemp)
-
-        let url = URL(fileURLWithPath: utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName))
-        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-            self.outstandingSessionTasks.removeValue(forKey: url)
-        }
-        outstandingOcIdTemp[ocIdTemp] = ocId
-
-        if error == .success {
-
-            // New file
-            if ocId != ocIdTemp {
-                // Signal update
-                fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, delete: true)
-            }
-
-            metadata.fileName = fileName
-            metadata.serverUrl = serverUrl
-            if let etag = etag { metadata.etag = etag }
-            if let ocId = ocId { metadata.ocId = ocId }
-            if let date = date { metadata.date = date }
-            metadata.permissions = "RGDNVW"
-            metadata.session = ""
-            metadata.size = size
-            metadata.status = NCGlobal.shared.metadataStatusNormal
-
-            NCManageDatabase.shared.addMetadata(metadata)
-            NCManageDatabase.shared.addLocalFile(metadata: metadata)
-
-            // New file
-            if let ocId, ocId != ocIdTemp {
-
-                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
-
-                // File system
-                let atPath = utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp)
-                let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(ocId)
-                utilityFileSystem.copyFile(atPath: atPath, toPath: toPath)
-            }
-
-            fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, update: true)
-
-        } else {
-
-            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
-
-            fileProviderData.shared.signalEnumerator(ocId: ocIdTemp, delete: true)
-        }
-    }
-
 }

+ 60 - 69
File Provider Extension/FileProviderItem.swift

@@ -24,102 +24,101 @@
 import UIKit
 import FileProvider
 import NextcloudKit
+import UniformTypeIdentifiers
 
 class FileProviderItem: NSObject, NSFileProviderItem {
-
     var metadata: tableMetadata
-    var parentItemIdentifier: NSFileProviderItemIdentifier
-
+    /// Providing Required Properties
     var itemIdentifier: NSFileProviderItemIdentifier {
         return fileProviderUtility().getItemIdentifier(metadata: metadata)
     }
-
     var filename: String {
         return metadata.fileNameView
     }
-
-    var documentSize: NSNumber? {
-        return NSNumber(value: metadata.size)
-    }
-
     var typeIdentifier: String {
         let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: "", directory: metadata.directory)
         return results.typeIdentifier
     }
-
-    var contentModificationDate: Date? {
-        return metadata.date as Date
-    }
-
-    var creationDate: Date? {
-        return metadata.creationDate as Date
-    }
-
-    var lastUsedDate: Date? {
-        return metadata.date as Date
-    }
-
     var capabilities: NSFileProviderItemCapabilities {
-        guard !metadata.directory else {
+        if metadata.directory {
             return [ .allowsAddingSubItems, .allowsContentEnumerating, .allowsReading, .allowsDeleting, .allowsRenaming ]
-        }
-        guard !metadata.lock else {
+        } else if metadata.lock {
             return [ .allowsReading ]
         }
         return [ .allowsWriting, .allowsReading, .allowsDeleting, .allowsRenaming, .allowsReparenting ]
     }
-
+    /// Managing Content
+    var childItemCount: NSNumber? {
+        return metadata.directory ? nil : nil
+    }
+    var documentSize: NSNumber? {
+        return metadata.directory ? nil : NSNumber(value: metadata.size)
+    }
+    /// Specifying Content Location
+    var parentItemIdentifier: NSFileProviderItemIdentifier
     var isTrashed: Bool {
         return false
     }
-
-    var childItemCount: NSNumber? {
+    var symlinkTargetPath: String? {
         return nil
     }
-
+    /// Tracking Usage
+    var contentModificationDate: Date? {
+        return metadata.date as Date
+    }
+    var creationDate: Date? {
+        return metadata.creationDate as Date
+    }
+    var lastUsedDate: Date? {
+        return metadata.date as Date
+    }
+    /// Tracking Versions
     var versionIdentifier: Data? {
         return metadata.etag.data(using: .utf8)
     }
-
-    var tagData: Data? {
-        if let tableTag = NCManageDatabase.shared.getTag(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
-            return tableTag.tagIOS
+    var isMostRecentVersionDownloaded: Bool {
+        if NCManageDatabase.shared.getTableLocalFile(ocId: metadata.ocId) == nil {
+            return false
         } else {
-            return nil
+            return true
         }
     }
-
-    var favoriteRank: NSNumber? {
-        if let rank = fileProviderData.shared.listFavoriteIdentifierRank[metadata.ocId] {
-            return rank
+    /// Monitoring File Transfers
+    var isUploading: Bool {
+        if metadata.status == NCGlobal.shared.metadataStatusWaitUpload || metadata.status == NCGlobal.shared.metadataStatusUploading {
+            return true
         } else {
-            return nil
+            return false
         }
     }
-
-    var isMostRecentVersionDownloaded: Bool {
-        return true
-    }
-
-    var isDownloaded: Bool {
-        if metadata.directory {
+    var isUploaded: Bool {
+        if metadata.status == NCGlobal.shared.metadataStatusWaitUpload || metadata.status == NCGlobal.shared.metadataStatusUploading || metadata.status == NCGlobal.shared.metadataStatusUploadError {
+            return false
+        } else {
             return true
         }
-        if NCUtilityFileSystem().fileProviderStorageExists(metadata) {
+    }
+    var uploadingError: Error? {
+        if metadata.status == NCGlobal.shared.metadataStatusUploadError {
+            return fileProviderData.FileProviderError.uploadError
+        } else {
+            return nil
+        }
+    }
+    var isDownloading: Bool {
+        if metadata.status == NCGlobal.shared.metadataStatusWaitDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading {
             return true
         } else {
             return false
         }
     }
-
-    var isDownloading: Bool {
-        if metadata.status == NCGlobal.shared.metadataStatusDownloading {
+    var isDownloaded: Bool {
+        if NCUtilityFileSystem().fileProviderStorageExists(metadata) {
             return true
         } else {
             return false
         }
     }
-
     var downloadingError: Error? {
         if metadata.status == NCGlobal.shared.metadataStatusDownloadError {
             return fileProviderData.FileProviderError.downloadError
@@ -127,33 +126,25 @@ class FileProviderItem: NSObject, NSFileProviderItem {
             return nil
         }
     }
-
-    var isUploaded: Bool {
-        if NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) != nil {
-            return true
-        } else {
-            return false
-        }
-    }
-
-    var isUploading: Bool {
-        if metadata.status == NCGlobal.shared.metadataStatusUploading {
-            return true
+    /// Sharing
+    /// Managing Metadata
+    var tagData: Data? {
+        if let tableTag = NCManageDatabase.shared.getTag(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
+            return tableTag.tagIOS
         } else {
-            return false
+            return nil
         }
     }
-
-    var uploadingError: Error? {
-        if metadata.status == NCGlobal.shared.metadataStatusUploadError {
-            return fileProviderData.FileProviderError.uploadError
+    var favoriteRank: NSNumber? {
+        if let rank = fileProviderData.shared.listFavoriteIdentifierRank[metadata.ocId] {
+            return rank
         } else {
             return nil
         }
     }
 
     init(metadata: tableMetadata, parentItemIdentifier: NSFileProviderItemIdentifier) {
-        self.metadata = metadata
+        self.metadata = tableMetadata(value: metadata)
         self.parentItemIdentifier = parentItemIdentifier
     }
 }

+ 6 - 60
File Provider Extension/FileProviderUtility.swift

@@ -24,40 +24,24 @@
 import UIKit
 
 class fileProviderUtility: NSObject {
-
     let fileManager = FileManager()
     let utilityFileSystem = NCUtilityFileSystem()
 
     func getAccountFromItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) -> String? {
-
         let ocId = itemIdentifier.rawValue
         return NCManageDatabase.shared.getMetadataFromOcId(ocId)?.account
     }
 
     func getTableMetadataFromItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) -> tableMetadata? {
-
         let ocId = itemIdentifier.rawValue
         return NCManageDatabase.shared.getMetadataFromOcId(ocId)
     }
 
     func getItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier {
-
         return NSFileProviderItemIdentifier(metadata.ocId)
     }
 
-    func createocIdentifierOnFileSystem(metadata: tableMetadata) {
-
-        let itemIdentifier = getItemIdentifier(metadata: metadata)
-
-        if metadata.directory {
-            _ = utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue)
-        } else {
-            _ = utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: metadata.fileNameView)
-        }
-    }
-
     func getParentItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier? {
-
         let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: metadata.urlBase, userId: metadata.userId)
         if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
             if directory.serverUrl == homeServerUrl {
@@ -70,36 +54,23 @@ class fileProviderUtility: NSObject {
                 }
             }
         }
-
         return nil
     }
 
     func getTableDirectoryFromParentItemIdentifier(_ parentItemIdentifier: NSFileProviderItemIdentifier, account: String, homeServerUrl: String) -> tableDirectory? {
-
         var predicate: NSPredicate
-
         if parentItemIdentifier == .rootContainer {
-
             predicate = NSPredicate(format: "account == %@ AND serverUrl == %@", account, homeServerUrl)
-
         } else {
-
             guard let metadata = getTableMetadataFromItemIdentifier(parentItemIdentifier) else { return nil }
             predicate = NSPredicate(format: "ocId == %@", metadata.ocId)
         }
-
         guard let directory = NCManageDatabase.shared.getTableDirectory(predicate: predicate) else { return nil }
-
         return directory
     }
 
-    // MARK: -
-
-    func copyFile(_ atPath: String, toPath: String) -> Error? {
-
-        var errorResult: Error?
-
-        if !fileManager.fileExists(atPath: atPath) { return NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError, userInfo: [:]) }
+    func copyFile(_ atPath: String, toPath: String) {
+        if !fileManager.fileExists(atPath: atPath) { return }
 
         do {
             try fileManager.removeItem(atPath: toPath)
@@ -109,18 +80,12 @@ class fileProviderUtility: NSObject {
         do {
             try fileManager.copyItem(atPath: atPath, toPath: toPath)
         } catch let error {
-            errorResult = error
+            print("error: \(error)")
         }
-
-        return errorResult
     }
 
-    func moveFile(_ atPath: String, toPath: String) -> Error? {
-
-        var errorResult: Error?
-
-        if atPath == toPath { return nil }
-        if !fileManager.fileExists(atPath: atPath) { return NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError, userInfo: [:]) }
+    func moveFile(_ atPath: String, toPath: String) {
+        if !fileManager.fileExists(atPath: atPath) { return }
 
         do {
             try fileManager.removeItem(atPath: toPath)
@@ -130,26 +95,7 @@ class fileProviderUtility: NSObject {
         do {
             try fileManager.moveItem(atPath: atPath, toPath: toPath)
         } catch let error {
-            errorResult = error
-        }
-
-        return errorResult
-    }
-
-    func deleteFile(_ atPath: String) -> Error? {
-
-        var errorResult: Error?
-
-        do {
-            try fileManager.removeItem(atPath: atPath)
-        } catch let error {
-            errorResult = error
+            print("error: \(error)")
         }
-
-        return errorResult
-    }
-
-    func fileExists(atPath: String) -> Bool {
-        return fileManager.fileExists(atPath: atPath)
     }
 }

+ 114 - 48
Nextcloud.xcodeproj/project.pbxproj

@@ -53,9 +53,9 @@
 		AF8ED1FC2757821000B8DBC4 /* NextcloudUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */; };
 		AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471127E2341B002537EE /* NCShare+Menu.swift */; };
 		AF93471927E2361E002537EE /* NCShareAdvancePermissionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF93471427E2361E002537EE /* NCShareAdvancePermissionFooter.xib */; };
-		AF93471A27E2361E002537EE /* NCShareAdvancePermissionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471527E2361E002537EE /* NCShareAdvancePermissionHeader.swift */; };
+		AF93471A27E2361E002537EE /* NCShareHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471527E2361E002537EE /* NCShareHeader.swift */; };
 		AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471627E2361E002537EE /* NCShareAdvancePermission.swift */; };
-		AF93471C27E2361E002537EE /* NCShareAdvancePermissionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF93471727E2361E002537EE /* NCShareAdvancePermissionHeader.xib */; };
+		AF93471C27E2361E002537EE /* NCShareHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF93471727E2361E002537EE /* NCShareHeader.xib */; };
 		AF93471D27E2361E002537EE /* NCShareAdvancePermissionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471827E2361E002537EE /* NCShareAdvancePermissionFooter.swift */; };
 		AF93474C27E34120002537EE /* NCUtility+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93474B27E34120002537EE /* NCUtility+Image.swift */; };
 		AF93474E27E3F212002537EE /* NCShareNewUserAddComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93474D27E3F211002537EE /* NCShareNewUserAddComment.swift */; };
@@ -113,7 +113,7 @@
 		F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
 		F359D86C2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
 		F359D86D2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
-		F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */; };
+		F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBar.swift */; };
 		F36E64FA2B96236C0085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
 		F36E64FB2B9733F10085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
 		F36E64FC2B9733F20085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
@@ -218,12 +218,15 @@
 		F711A4EB2AF9327D00095DD8 /* UIImage+animatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */; };
 		F711A4EF2AF932B900095DD8 /* SVGKitSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F711A4EE2AF932B900095DD8 /* SVGKitSwift */; };
 		F711D63128F44801003F43C8 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C9739428F17131002C43E2 /* IntentHandler.swift */; };
+		F713FBE52C31645200F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
+		F713FBE62C31646400F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
+		F713FBE72C31646500F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
+		F713FBE82C31646600F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
 		F713FF002472764100214AF6 /* UIImage+animatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */; };
 		F7145610296433C80038D028 /* NCDocumentCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = F714560F296433C80038D028 /* NCDocumentCamera.swift */; };
 		F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; };
 		F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F714803A262EBE3900693E51 /* MainInterface.storyboard */; };
 		F7148041262EBE4000693E51 /* NCShareExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7148040262EBE4000693E51 /* NCShareExtension.swift */; };
-		F714804F262ED4F900693E51 /* NCGridCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4521903D010088454D /* NCGridCell.xib */; };
 		F7148054262ED51000693E51 /* NCListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4321903CF20088454D /* NCListCell.xib */; };
 		F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
 		F7160A7D2BE931DE0034DCB3 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F7160A7C2BE931DE0034DCB3 /* RealmSwift */; };
@@ -407,6 +410,7 @@
 		F746EC51273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */; };
 		F746EC52273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */; };
 		F746EC53273906C50052598D /* NCViewCertificateDetails.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */; };
+		F747EB0D2C4AC1FF00F959A8 /* NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F747EB0C2C4AC1FF00F959A8 /* NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift */; };
 		F7490E6B29882A92009DCE94 /* NCGlobal.swift in Sources */ = {isa = PBXBuildFile; fileRef = F702F2CE25EE5B5C008F8E80 /* NCGlobal.swift */; };
 		F7490E6C29882AEA009DCE94 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
 		F7490E6E29882B56009DCE94 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
@@ -463,6 +467,8 @@
 		F74DE14425135B6800917068 /* NCTransfers.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F74DE14225135B6800917068 /* NCTransfers.storyboard */; };
 		F7501C322212E57500FB1415 /* NCMedia.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7501C302212E57400FB1415 /* NCMedia.storyboard */; };
 		F7501C332212E57500FB1415 /* NCMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7501C312212E57400FB1415 /* NCMedia.swift */; };
+		F751247C2C42919C00E63DB8 /* NCPhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F751247A2C42919C00E63DB8 /* NCPhotoCell.swift */; };
+		F751247E2C42919C00E63DB8 /* NCPhotoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F751247B2C42919C00E63DB8 /* NCPhotoCell.xib */; };
 		F75379202AE2AD9400C0250E /* JGProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = F753791F2AE2AD9400C0250E /* JGProgressHUD */; };
 		F75379222AE2ADA100C0250E /* JGProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = F75379212AE2ADA100C0250E /* JGProgressHUD */; };
 		F753BA93281FD8020015BFB6 /* EasyTipView in Frameworks */ = {isa = PBXBuildFile; productRef = F753BA92281FD8020015BFB6 /* EasyTipView */; };
@@ -515,7 +521,6 @@
 		F765E9CD295C585800A09ED8 /* NCUploadScanDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765E9CC295C585800A09ED8 /* NCUploadScanDocument.swift */; };
 		F765F73125237E3F00391DBE /* NCRecent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765F72F25237E3F00391DBE /* NCRecent.swift */; };
 		F765F73225237E3F00391DBE /* NCRecent.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F765F73025237E3F00391DBE /* NCRecent.storyboard */; };
-		F76673ED22C901F6007ED366 /* FileProviderDomain.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76673EC22C901F5007ED366 /* FileProviderDomain.swift */; };
 		F76673F022C90434007ED366 /* FileProviderUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76673EF22C90433007ED366 /* FileProviderUtility.swift */; };
 		F76687072B7D067400779E3F /* NCAudioRecorderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76687052B7D067400779E3F /* NCAudioRecorderViewController.swift */; };
 		F76687082B7D067400779E3F /* NCAudioRecorderViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F76687062B7D067400779E3F /* NCAudioRecorderViewController.storyboard */; };
@@ -588,9 +593,12 @@
 		F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7725A5F251F33BB00D125E0 /* NCFiles.storyboard */; };
 		F77333882927A72100466E35 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = F77333872927A72100466E35 /* OpenSSL */; };
 		F774264A22EB4D0000B23912 /* NCSearchUserDropDownCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F774264822EB4D0000B23912 /* NCSearchUserDropDownCell.xib */; };
+		F7743A122C33F0A20034F670 /* NCCollectionViewCommon+CollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7743A112C33F0A20034F670 /* NCCollectionViewCommon+CollectionViewDelegate.swift */; };
+		F7743A142C33F13A0034F670 /* NCCollectionViewCommon+CollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7743A132C33F13A0034F670 /* NCCollectionViewCommon+CollectionViewDataSource.swift */; };
 		F77444F522281649000D5EB0 /* NCGridMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77444F322281649000D5EB0 /* NCGridMediaCell.swift */; };
 		F77444F622281649000D5EB0 /* NCGridMediaCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F77444F422281649000D5EB0 /* NCGridMediaCell.xib */; };
 		F77444F8222816D5000D5EB0 /* NCPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77444F7222816D5000D5EB0 /* NCPickerViewController.swift */; };
+		F778231E2C42C07C001BB94F /* NCCollectionViewCommon+MediaLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F778231D2C42C07C001BB94F /* NCCollectionViewCommon+MediaLayout.swift */; };
 		F77A697D250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */; };
 		F77B0F631D118A16002130FE /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; };
 		F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; };
@@ -664,7 +672,7 @@
 		F78ACD4621903D010088454D /* NCGridCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4521903D010088454D /* NCGridCell.xib */; };
 		F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; };
 		F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4921903F850088454D /* NCTrashListCell.xib */; };
-		F78ACD52219046DC0088454D /* NCSectionHeaderMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */; };
+		F78ACD52219046DC0088454D /* NCSectionFirstHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionFirstHeader.swift */; };
 		F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
 		F78AF1E72BE938C100F3F060 /* MobileVLCKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7792DE429EEE02D005930CE /* MobileVLCKit.xcframework */; };
 		F78AF1E82BE938C100F3F060 /* MobileVLCKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F7792DE429EEE02D005930CE /* MobileVLCKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -691,6 +699,12 @@
 		F798F0E225880608000DAFFD /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; };
 		F798F0E725880609000DAFFD /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; };
 		F798F0EC2588060A000DAFFD /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; };
+		F799DF822C4B7DCC003410B5 /* NCSectionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F799DF812C4B7DCC003410B5 /* NCSectionFooter.swift */; };
+		F799DF832C4B7DCC003410B5 /* NCSectionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F799DF812C4B7DCC003410B5 /* NCSectionFooter.swift */; };
+		F799DF852C4B7E56003410B5 /* NCSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F799DF842C4B7E56003410B5 /* NCSectionHeader.swift */; };
+		F799DF862C4B7E56003410B5 /* NCSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F799DF842C4B7E56003410B5 /* NCSectionHeader.swift */; };
+		F799DF882C4B83CC003410B5 /* NCCollectionViewCommon+EasyTipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F799DF872C4B83CC003410B5 /* NCCollectionViewCommon+EasyTipView.swift */; };
+		F799DF8B2C4B84EB003410B5 /* NCCollectionViewCommon+EndToEndInitialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F799DF8A2C4B84EB003410B5 /* NCCollectionViewCommon+EndToEndInitialize.swift */; };
 		F79A65C32191D90F00FF6DCC /* NCSelect.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F79A65C22191D90F00FF6DCC /* NCSelect.storyboard */; };
 		F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79A65C52191D95E00FF6DCC /* NCSelect.swift */; };
 		F79B646026CA661600838ACA /* UIControl+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79B645F26CA661600838ACA /* UIControl+Extension.swift */; };
@@ -716,6 +730,7 @@
 		F7A560442AE15D2900BE8FD6 /* Queuer in Frameworks */ = {isa = PBXBuildFile; productRef = F7A560432AE15D2900BE8FD6 /* Queuer */; };
 		F7A560462AE15D3D00BE8FD6 /* Queuer in Frameworks */ = {isa = PBXBuildFile; productRef = F7A560452AE15D3D00BE8FD6 /* Queuer */; };
 		F7A560482AE15D5000BE8FD6 /* Queuer in Frameworks */ = {isa = PBXBuildFile; productRef = F7A560472AE15D5000BE8FD6 /* Queuer */; };
+		F7A5DF052C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A5DF042C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift */; };
 		F7A60F86292D215000FCE1F2 /* NCShareAccounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A60F84292D215000FCE1F2 /* NCShareAccounts.swift */; };
 		F7A60F87292D215000FCE1F2 /* NCShareAccounts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7A60F85292D215000FCE1F2 /* NCShareAccounts.storyboard */; };
 		F7A76DC8256A71CD00119AB3 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extension.swift */; };
@@ -746,8 +761,8 @@
 		F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; };
 		F7AEEAA62C11DBAF00011412 /* NCAccountSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AEEAA52C11DBAF00011412 /* NCAccountSettingsView.swift */; };
 		F7AEEAA82C11DBFD00011412 /* NCAccountSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AEEAA72C11DBFD00011412 /* NCAccountSettingsModel.swift */; };
-		F7B398422A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */; };
-		F7B398432A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */; };
+		F7B398422A6A91D5007538D6 /* NCSectionFirstHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionFirstHeader.xib */; };
+		F7B398432A6A91D5007538D6 /* NCSectionFirstHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionFirstHeader.xib */; };
 		F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */; };
 		F7B7504B2397D38F004E13EC /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extension.swift */; };
 		F7B769A82B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B769A72B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift */; };
@@ -764,6 +779,9 @@
 		F7BB7E4727A18C56009B9F29 /* Parchment in Frameworks */ = {isa = PBXBuildFile; productRef = F7BB7E4627A18C56009B9F29 /* Parchment */; };
 		F7BC287E26663F6C004D46C5 /* NCViewCertificateDetails.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */; };
 		F7BC288026663F85004D46C5 /* NCViewCertificateDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BC287F26663F85004D46C5 /* NCViewCertificateDetails.swift */; };
+		F7BD0A002C468925003A4A6D /* NCMedia+CollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BD09FF2C468925003A4A6D /* NCMedia+CollectionViewDataSource.swift */; };
+		F7BD0A022C4689A4003A4A6D /* NCMedia+CollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BD0A012C4689A4003A4A6D /* NCMedia+CollectionViewDelegate.swift */; };
+		F7BD0A042C4689E9003A4A6D /* NCMedia+MediaLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BD0A032C4689E9003A4A6D /* NCMedia+MediaLayout.swift */; };
 		F7BD71E62636EAFC00643C34 /* NCNetworkingE2EE.swift in Sources */ = {isa = PBXBuildFile; fileRef = F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */; };
 		F7BF9D822934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BF9D812934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift */; };
 		F7BF9D832934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BF9D812934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift */; };
@@ -771,6 +789,7 @@
 		F7BF9D852934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BF9D812934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift */; };
 		F7BF9D862934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BF9D812934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift */; };
 		F7BF9D872934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BF9D812934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift */; };
+		F7C1DAEF2C3D1DF4000BDC69 /* FileProviderDomain.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76673EC22C901F5007ED366 /* FileProviderDomain.swift */; };
 		F7C1EEA525053A9C00866ACC /* NCDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C1EEA425053A9C00866ACC /* NCDataSource.swift */; };
 		F7C30DF6291BC0CA0017149B /* NCNetworkingE2EEUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */; };
 		F7C30DF7291BC0D30017149B /* NCNetworkingE2EEUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */; };
@@ -798,10 +817,10 @@
 		F7CA212D25F1333300826ABB /* NCAccountRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CA212B25F1333200826ABB /* NCAccountRequest.swift */; };
 		F7CA212E25F1333300826ABB /* NCAccountRequest.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7CA212C25F1333200826ABB /* NCAccountRequest.storyboard */; };
 		F7CB689A2541676B0050EC94 /* NCMore.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7CB68992541676B0050EC94 /* NCMore.storyboard */; };
-		F7CBC1232BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CBC1212BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib */; };
-		F7CBC1242BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CBC1212BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib */; };
-		F7CBC1252BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CBC1222BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift */; };
-		F7CBC1262BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CBC1222BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift */; };
+		F7CBC1232BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CBC1212BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib */; };
+		F7CBC1242BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CBC1212BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib */; };
+		F7CBC1252BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CBC1222BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift */; };
+		F7CBC1262BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CBC1222BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift */; };
 		F7CEE6002BA9A5C9003EFD89 /* NCTrashGridCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CEE5FE2BA9A5C9003EFD89 /* NCTrashGridCell.xib */; };
 		F7CEE6012BA9A5C9003EFD89 /* NCTrashGridCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CEE5FF2BA9A5C9003EFD89 /* NCTrashGridCell.swift */; };
 		F7D1612023CF19E30039EBBF /* NCViewerRichWorkspace.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7D1611F23CF19E30039EBBF /* NCViewerRichWorkspace.storyboard */; };
@@ -840,8 +859,7 @@
 		F7EDE4D1262D7B8400414FE6 /* NCDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C1EEA425053A9C00866ACC /* NCDataSource.swift */; };
 		F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4121903CE00088454D /* NCListCell.swift */; };
 		F7EDE4DB262D7BA200414FE6 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
-		F7EDE4E0262D7BAF00414FE6 /* NCGridCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD3F21903CC20088454D /* NCGridCell.swift */; };
-		F7EDE4E5262D7BBE00414FE6 /* NCSectionHeaderMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */; };
+		F7EDE4E5262D7BBE00414FE6 /* NCSectionFirstHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionFirstHeader.swift */; };
 		F7EDE509262DA9D600414FE6 /* NCSelectCommandViewSelect.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EDE508262DA9D600414FE6 /* NCSelectCommandViewSelect.xib */; };
 		F7EDE514262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EDE513262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib */; };
 		F7EDE51B262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EDE51A262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib */; };
@@ -1093,9 +1111,9 @@
 		AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudUnitTests.swift; sourceTree = "<group>"; };
 		AF93471127E2341B002537EE /* NCShare+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Menu.swift"; sourceTree = "<group>"; };
 		AF93471427E2361E002537EE /* NCShareAdvancePermissionFooter.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareAdvancePermissionFooter.xib; sourceTree = "<group>"; };
-		AF93471527E2361E002537EE /* NCShareAdvancePermissionHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAdvancePermissionHeader.swift; sourceTree = "<group>"; };
+		AF93471527E2361E002537EE /* NCShareHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareHeader.swift; sourceTree = "<group>"; };
 		AF93471627E2361E002537EE /* NCShareAdvancePermission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAdvancePermission.swift; sourceTree = "<group>"; };
-		AF93471727E2361E002537EE /* NCShareAdvancePermissionHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareAdvancePermissionHeader.xib; sourceTree = "<group>"; };
+		AF93471727E2361E002537EE /* NCShareHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareHeader.xib; sourceTree = "<group>"; };
 		AF93471827E2361E002537EE /* NCShareAdvancePermissionFooter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAdvancePermissionFooter.swift; sourceTree = "<group>"; };
 		AF93474B27E34120002537EE /* NCUtility+Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Image.swift"; sourceTree = "<group>"; };
 		AF93474D27E3F211002537EE /* NCShareNewUserAddComment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareNewUserAddComment.swift; sourceTree = "<group>"; };
@@ -1118,7 +1136,7 @@
 		F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAsset+Extension.swift"; sourceTree = "<group>"; };
 		F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
 		F359D8662A7D03420023F405 /* NCUtility+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Exif.swift"; sourceTree = "<group>"; };
-		F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SelectTabBarDelegate.swift"; sourceTree = "<group>"; };
+		F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SelectTabBar.swift"; sourceTree = "<group>"; };
 		F37208742BAB4AB0006B5430 /* TestConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestConstants.swift; sourceTree = "<group>"; };
 		F37208772BAB4B5D006B5430 /* BaseXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseXCTestCase.swift; sourceTree = "<group>"; };
 		F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewCommonSelectTabBar.swift; sourceTree = "<group>"; };
@@ -1208,6 +1226,7 @@
 		F710D1F42405770F00A6033D /* NCViewerPDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerPDF.swift; sourceTree = "<group>"; };
 		F710D2012405826100A6033D /* NCViewer+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCViewer+Menu.swift"; sourceTree = "<group>"; };
 		F711A4DB2AF92CAD00095DD8 /* NCUtility+Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCUtility+Date.swift"; sourceTree = "<group>"; };
+		F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCNetworking+AsyncAwait.swift"; sourceTree = "<group>"; };
 		F713FEFE2472764000214AF6 /* UIImage+animatedGIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+animatedGIF.h"; sourceTree = "<group>"; };
 		F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+animatedGIF.m"; sourceTree = "<group>"; };
 		F714560F296433C80038D028 /* NCDocumentCamera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCDocumentCamera.swift; sourceTree = "<group>"; };
@@ -1299,6 +1318,7 @@
 		F7421EAE2294044B00C4B7C1 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
 		F745B250222D871800346520 /* QRCodeReader.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QRCodeReader.framework; path = Carthage/Build/iOS/QRCodeReader.framework; sourceTree = "<group>"; };
 		F745B252222D88AE00346520 /* NCLoginQRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginQRCode.swift; sourceTree = "<group>"; };
+		F747EB0C2C4AC1FF00F959A8 /* NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift"; sourceTree = "<group>"; };
 		F749B649297B0CBB00087535 /* NCManageDatabase+Share.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Share.swift"; sourceTree = "<group>"; };
 		F749B650297B0F2400087535 /* NCManageDatabase+Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Avatar.swift"; sourceTree = "<group>"; };
 		F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUtilityFileSystem.swift; sourceTree = "<group>"; };
@@ -1311,6 +1331,8 @@
 		F74DE14225135B6800917068 /* NCTransfers.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCTransfers.storyboard; sourceTree = "<group>"; };
 		F7501C302212E57400FB1415 /* NCMedia.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCMedia.storyboard; sourceTree = "<group>"; };
 		F7501C312212E57400FB1415 /* NCMedia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCMedia.swift; sourceTree = "<group>"; };
+		F751247A2C42919C00E63DB8 /* NCPhotoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPhotoCell.swift; sourceTree = "<group>"; };
+		F751247B2C42919C00E63DB8 /* NCPhotoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCPhotoCell.xib; sourceTree = "<group>"; };
 		F75153232226920200323DDC /* FastScroll.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FastScroll.framework; path = Carthage/Build/iOS/FastScroll.framework; sourceTree = "<group>"; };
 		F753701822723D620041C76C /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/Localizable.strings; sourceTree = "<group>"; };
 		F753701922723E0D0041C76C /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -1420,9 +1442,12 @@
 		F77439541FCD6D6100662C46 /* es-PY */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PY"; path = "es-PY.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F774395B1FCD6D8200662C46 /* es-SV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-SV"; path = "es-SV.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F77439621FCD6D9C00662C46 /* es-UY */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-UY"; path = "es-UY.lproj/Localizable.strings"; sourceTree = "<group>"; };
+		F7743A112C33F0A20034F670 /* NCCollectionViewCommon+CollectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+CollectionViewDelegate.swift"; sourceTree = "<group>"; };
+		F7743A132C33F13A0034F670 /* NCCollectionViewCommon+CollectionViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+CollectionViewDataSource.swift"; sourceTree = "<group>"; };
 		F77444F322281649000D5EB0 /* NCGridMediaCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCGridMediaCell.swift; sourceTree = "<group>"; };
 		F77444F422281649000D5EB0 /* NCGridMediaCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCGridMediaCell.xib; sourceTree = "<group>"; };
 		F77444F7222816D5000D5EB0 /* NCPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPickerViewController.swift; sourceTree = "<group>"; };
+		F778231D2C42C07C001BB94F /* NCCollectionViewCommon+MediaLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+MediaLayout.swift"; sourceTree = "<group>"; };
 		F7792DE429EEE02D005930CE /* MobileVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MobileVLCKit.xcframework; path = Carthage/Build/MobileVLCKit.xcframework; sourceTree = "<group>"; };
 		F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+Menu.swift"; sourceTree = "<group>"; };
 		F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = "<group>"; };
@@ -1452,7 +1477,7 @@
 		F78ACD4521903D010088454D /* NCGridCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCGridCell.xib; sourceTree = "<group>"; };
 		F78ACD4821903F850088454D /* NCTrashListCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCTrashListCell.swift; sourceTree = "<group>"; };
 		F78ACD4921903F850088454D /* NCTrashListCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCTrashListCell.xib; sourceTree = "<group>"; };
-		F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionHeaderMenu.swift; sourceTree = "<group>"; };
+		F78ACD51219046DC0088454D /* NCSectionFirstHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionFirstHeader.swift; sourceTree = "<group>"; };
 		F78ACD53219047D40088454D /* NCSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionFooter.xib; sourceTree = "<group>"; };
 		F78B87E62B62527100C65ADC /* NCMediaDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMediaDataSource.swift; sourceTree = "<group>"; };
 		F78B87E82B62550800C65ADC /* NCMediaDownloadThumbnaill.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMediaDownloadThumbnaill.swift; sourceTree = "<group>"; };
@@ -1471,6 +1496,10 @@
 		F794E13E2BBC0F70003693D7 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
 		F79918A021997F9000C2E308 /* UICKeyChainStore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UICKeyChainStore.framework; path = Carthage/Build/iOS/UICKeyChainStore.framework; sourceTree = "<group>"; };
 		F79918A72199840500C2E308 /* Sheeeeeeeeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sheeeeeeeeet.framework; path = Carthage/Build/iOS/Sheeeeeeeeet.framework; sourceTree = "<group>"; };
+		F799DF812C4B7DCC003410B5 /* NCSectionFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionFooter.swift; sourceTree = "<group>"; };
+		F799DF842C4B7E56003410B5 /* NCSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionHeader.swift; sourceTree = "<group>"; };
+		F799DF872C4B83CC003410B5 /* NCCollectionViewCommon+EasyTipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+EasyTipView.swift"; sourceTree = "<group>"; };
+		F799DF8A2C4B84EB003410B5 /* NCCollectionViewCommon+EndToEndInitialize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+EndToEndInitialize.swift"; sourceTree = "<group>"; };
 		F79A65C22191D90F00FF6DCC /* NCSelect.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCSelect.storyboard; sourceTree = "<group>"; };
 		F79A65C52191D95E00FF6DCC /* NCSelect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSelect.swift; sourceTree = "<group>"; };
 		F79B645F26CA661600838ACA /* UIControl+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIControl+Extension.swift"; sourceTree = "<group>"; };
@@ -1483,6 +1512,7 @@
 		F7A48414297028FC00BD1B49 /* Nextcloud Hub.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Nextcloud Hub.png"; sourceTree = SOURCE_ROOT; };
 		F7A509242C26BD5D00326106 /* NCCreateDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateDocument.swift; sourceTree = "<group>"; };
 		F7A560412AE1593700BE8FD6 /* NCOperationSaveLivePhoto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCOperationSaveLivePhoto.swift; sourceTree = "<group>"; };
+		F7A5DF042C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderExtension+NetworkingDelegate.swift"; sourceTree = "<group>"; };
 		F7A60F84292D215000FCE1F2 /* NCShareAccounts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAccounts.swift; sourceTree = "<group>"; };
 		F7A60F85292D215000FCE1F2 /* NCShareAccounts.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCShareAccounts.storyboard; sourceTree = "<group>"; };
 		F7A7FDDB2C2DBD6200E9A93A /* NCDeepLinkHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCDeepLinkHandler.swift; sourceTree = "<group>"; };
@@ -1539,7 +1569,7 @@
 		F7AF7632246BEDFE00B86E3C /* TOPasscodeViewController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TOPasscodeViewController.framework; path = Carthage/Build/iOS/TOPasscodeViewController.framework; sourceTree = "<group>"; };
 		F7B1076C25D3CF2800E72DE2 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = System/Library/Frameworks/BackgroundTasks.framework; sourceTree = SDKROOT; };
 		F7B1A7761EBB3C8000BFB6D1 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
-		F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionHeaderMenu.xib; sourceTree = "<group>"; };
+		F7B398412A6A91D5007538D6 /* NCSectionFirstHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionFirstHeader.xib; sourceTree = "<group>"; };
 		F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCScan+CollectionView.swift"; sourceTree = "<group>"; };
 		F7B7504A2397D38E004E13EC /* UIImage+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = "<group>"; };
 		F7B769A72B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Metadata+Session.swift"; sourceTree = "<group>"; };
@@ -1549,6 +1579,9 @@
 		F7BB04851FD58ACB00BBFD2A /* cs-CZ */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "cs-CZ"; path = "cs-CZ.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewCertificateDetails.storyboard; sourceTree = "<group>"; };
 		F7BC287F26663F85004D46C5 /* NCViewCertificateDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewCertificateDetails.swift; sourceTree = "<group>"; };
+		F7BD09FF2C468925003A4A6D /* NCMedia+CollectionViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCMedia+CollectionViewDataSource.swift"; sourceTree = "<group>"; };
+		F7BD0A012C4689A4003A4A6D /* NCMedia+CollectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCMedia+CollectionViewDelegate.swift"; sourceTree = "<group>"; };
+		F7BD0A032C4689E9003A4A6D /* NCMedia+MediaLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCMedia+MediaLayout.swift"; sourceTree = "<group>"; };
 		F7BE7C25290AC8C9002ABB61 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intent.strings; sourceTree = "<group>"; };
 		F7BE7C27290ADEFD002ABB61 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Intent.strings; sourceTree = "<group>"; };
 		F7BE7C29290ADEFD002ABB61 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Intent.strings; sourceTree = "<group>"; };
@@ -1620,8 +1653,8 @@
 		F7CA212B25F1333200826ABB /* NCAccountRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAccountRequest.swift; sourceTree = "<group>"; };
 		F7CA212C25F1333200826ABB /* NCAccountRequest.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCAccountRequest.storyboard; sourceTree = "<group>"; };
 		F7CB68992541676B0050EC94 /* NCMore.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCMore.storyboard; sourceTree = "<group>"; };
-		F7CBC1212BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionHeaderEmptyData.xib; sourceTree = "<group>"; };
-		F7CBC1222BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCSectionHeaderEmptyData.swift; sourceTree = "<group>"; };
+		F7CBC1212BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionFirstHeaderEmptyData.xib; sourceTree = "<group>"; };
+		F7CBC1222BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCSectionFirstHeaderEmptyData.swift; sourceTree = "<group>"; };
 		F7CC04E61F5AD50D00378CEF /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
 		F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nextcloud.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Share.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1899,8 +1932,6 @@
 				AF93471627E2361E002537EE /* NCShareAdvancePermission.swift */,
 				AF93471827E2361E002537EE /* NCShareAdvancePermissionFooter.swift */,
 				AF93471427E2361E002537EE /* NCShareAdvancePermissionFooter.xib */,
-				AF93471527E2361E002537EE /* NCShareAdvancePermissionHeader.swift */,
-				AF93471727E2361E002537EE /* NCShareAdvancePermissionHeader.xib */,
 				AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */,
 				AF93474D27E3F211002537EE /* NCShareNewUserAddComment.swift */,
 			);
@@ -2126,6 +2157,8 @@
 				F774264822EB4D0000B23912 /* NCSearchUserDropDownCell.xib */,
 				F769453B22E9CFFF000A798A /* NCShareUserCell.xib */,
 				AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */,
+				AF93471727E2361E002537EE /* NCShareHeader.xib */,
+				AF93471527E2361E002537EE /* NCShareHeader.swift */,
 			);
 			path = Share;
 			sourceTree = "<group>";
@@ -2199,6 +2232,7 @@
 				F72CD63925C19EBF00F46F9A /* NCAutoUpload.swift */,
 				F77BC3EC293E528A005F2B08 /* NCConfigServer.swift */,
 				F75A9EE523796C6F0044CFCE /* NCNetworking.swift */,
+				F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */,
 				F7327E1F2B73A42F00A462C7 /* NCNetworking+Download.swift */,
 				F7327E272B73A53400A462C7 /* NCNetworking+Upload.swift */,
 				F7327E2F2B73A86700A462C7 /* NCNetworking+WebDAV.swift */,
@@ -2245,6 +2279,8 @@
 			isa = PBXGroup;
 			children = (
 				370D26AE248A3D7A00121797 /* NCCellProtocol.swift */,
+				F751247A2C42919C00E63DB8 /* NCPhotoCell.swift */,
+				F751247B2C42919C00E63DB8 /* NCPhotoCell.xib */,
 				F78ACD3F21903CC20088454D /* NCGridCell.swift */,
 				F78ACD4521903D010088454D /* NCGridCell.xib */,
 				F78ACD4121903CE00088454D /* NCListCell.swift */,
@@ -2259,8 +2295,14 @@
 				F75FE06B2BB01D0D00A0EFEF /* Cell */,
 				F78ACD50219046AC0088454D /* Section Header Footer */,
 				F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */,
+				F799DF8A2C4B84EB003410B5 /* NCCollectionViewCommon+EndToEndInitialize.swift */,
+				F799DF872C4B83CC003410B5 /* NCCollectionViewCommon+EasyTipView.swift */,
+				F747EB0C2C4AC1FF00F959A8 /* NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift */,
+				F7743A132C33F13A0034F670 /* NCCollectionViewCommon+CollectionViewDataSource.swift */,
+				F7743A112C33F0A20034F670 /* NCCollectionViewCommon+CollectionViewDelegate.swift */,
+				F778231D2C42C07C001BB94F /* NCCollectionViewCommon+MediaLayout.swift */,
 				F7D890742BD25C570050B8A6 /* NCCollectionViewCommon+DragDrop.swift */,
-				F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */,
+				F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBar.swift */,
 				F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */,
 				F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */,
 				F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */,
@@ -2428,6 +2470,7 @@
 				F76673EC22C901F5007ED366 /* FileProviderDomain.swift */,
 				F771E3D620E2392D00AFB62D /* FileProviderEnumerator.swift */,
 				F771E3D220E2392D00AFB62D /* FileProviderExtension.swift */,
+				F7A5DF042C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift */,
 				F771E3F420E239B400AFB62D /* FileProviderExtension+Actions.swift */,
 				F771E3F520E239B400AFB62D /* FileProviderExtension+Thumbnail.swift */,
 				F771E3D420E2392D00AFB62D /* FileProviderItem.swift */,
@@ -2470,12 +2513,14 @@
 		F78ACD50219046AC0088454D /* Section Header Footer */ = {
 			isa = PBXGroup;
 			children = (
+				F78ACD51219046DC0088454D /* NCSectionFirstHeader.swift */,
+				F7B398412A6A91D5007538D6 /* NCSectionFirstHeader.xib */,
+				F7CBC1222BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift */,
+				F7CBC1212BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib */,
+				F799DF812C4B7DCC003410B5 /* NCSectionFooter.swift */,
 				F78ACD53219047D40088454D /* NCSectionFooter.xib */,
+				F799DF842C4B7E56003410B5 /* NCSectionHeader.swift */,
 				F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */,
-				F7CBC1222BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift */,
-				F7CBC1212BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib */,
-				F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */,
-				F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */,
 			);
 			path = "Section Header Footer";
 			sourceTree = "<group>";
@@ -2595,8 +2640,8 @@
 			isa = PBXGroup;
 			children = (
 				F7C9555221F0C4CA0024296E /* NCActivity.storyboard */,
-				AF56C1DB2784856200D8BAE2 /* NCActivityCommentView.xib */,
 				F7C9555421F0C5470024296E /* NCActivity.swift */,
+				AF56C1DB2784856200D8BAE2 /* NCActivityCommentView.xib */,
 				AFA2AC8427849604008E1EA7 /* NCActivityCommentView.swift */,
 				D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */,
 			);
@@ -2826,8 +2871,11 @@
 				F720B5B72507B9A5008C94E5 /* Cell */,
 				F7501C302212E57400FB1415 /* NCMedia.storyboard */,
 				F7501C312212E57400FB1415 /* NCMedia.swift */,
+				F7BD09FF2C468925003A4A6D /* NCMedia+CollectionViewDataSource.swift */,
+				F7BD0A012C4689A4003A4A6D /* NCMedia+CollectionViewDelegate.swift */,
 				F72408322B8A27C900F128E2 /* NCMedia+Command.swift */,
 				F7802B312BD5584F00D74270 /* NCMedia+DragDrop.swift */,
+				F7BD0A032C4689E9003A4A6D /* NCMedia+MediaLayout.swift */,
 				F78B87E62B62527100C65ADC /* NCMediaDataSource.swift */,
 				F78B87E82B62550800C65ADC /* NCMediaDownloadThumbnaill.swift */,
 				F755CB3F2B8CB13C00CE27E9 /* NCMediaLayout.swift */,
@@ -3614,14 +3662,13 @@
 				F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */,
 				F7E4022C2BA85D1D007E5609 /* PrivacyInfo.xcprivacy in Resources */,
 				AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */,
-				F7CBC1242BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib in Resources */,
-				F7B398432A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */,
+				F7CBC1242BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib in Resources */,
+				F7B398432A6A91D5007538D6 /* NCSectionFirstHeader.xib in Resources */,
 				AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */,
 				F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */,
 				F746EC51273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */,
 				F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */,
 				F79EC784263161BA004E59D6 /* NCRenameFile.storyboard in Resources */,
-				F714804F262ED4F900693E51 /* NCGridCell.xib in Resources */,
 				F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */,
 				F700222D1EC479840080073F /* Custom.xcassets in Resources */,
 			);
@@ -3678,7 +3725,7 @@
 				F7F4F10B27ECDBDB008676F9 /* Inconsolata-Light.ttf in Resources */,
 				F7CEE6002BA9A5C9003EFD89 /* NCTrashGridCell.xib in Resources */,
 				3704EB2A23D5A58400455C5B /* NCMenu.storyboard in Resources */,
-				AF93471C27E2361E002537EE /* NCShareAdvancePermissionHeader.xib in Resources */,
+				AF93471C27E2361E002537EE /* NCShareHeader.xib in Resources */,
 				F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */,
 				F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */,
 				F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */,
@@ -3695,7 +3742,7 @@
 				F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */,
 				AF93471927E2361E002537EE /* NCShareAdvancePermissionFooter.xib in Resources */,
 				F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */,
-				F7CBC1232BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.xib in Resources */,
+				F7CBC1232BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.xib in Resources */,
 				F700510122DF63AC003A3356 /* NCShare.storyboard in Resources */,
 				F787704F22E7019900F287A9 /* NCShareLinkCell.xib in Resources */,
 				F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */,
@@ -3710,6 +3757,7 @@
 				F7C9555321F0C4CA0024296E /* NCActivity.storyboard in Resources */,
 				F7BC287E26663F6C004D46C5 /* NCViewCertificateDetails.storyboard in Resources */,
 				F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */,
+				F751247E2C42919C00E63DB8 /* NCPhotoCell.xib in Resources */,
 				F704B5E32430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard in Resources */,
 				F7EDE509262DA9D600414FE6 /* NCSelectCommandViewSelect.xib in Resources */,
 				F732D23327CF8AED000B0F1B /* NCPlayerToolBar.xib in Resources */,
@@ -3726,7 +3774,7 @@
 				F73CB3B222E072A000AD728E /* NCShareHeaderView.xib in Resources */,
 				F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */,
 				F7EDE514262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib in Resources */,
-				F7B398422A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */,
+				F7B398422A6A91D5007538D6 /* NCSectionFirstHeader.xib in Resources */,
 				F7501C322212E57500FB1415 /* NCMedia.storyboard in Resources */,
 				F74DE14425135B6800917068 /* NCTransfers.storyboard in Resources */,
 				F76687082B7D067400779E3F /* NCAudioRecorderViewController.storyboard in Resources */,
@@ -3916,7 +3964,7 @@
 				F7864ACF2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F71F6D0A2B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */,
 				F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */,
-				F7EDE4E5262D7BBE00414FE6 /* NCSectionHeaderMenu.swift in Sources */,
+				F7EDE4E5262D7BBE00414FE6 /* NCSectionFirstHeader.swift in Sources */,
 				F7BF9D852934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
 				F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */,
 				F7C30DFB291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */,
@@ -3924,7 +3972,7 @@
 				F7817CFB29801A3500FFBC65 /* Data+Extension.swift in Sources */,
 				F72429362AFE39860040AEF3 /* NCLivePhoto.swift in Sources */,
 				AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */,
-				F7CBC1262BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift in Sources */,
+				F7CBC1262BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift in Sources */,
 				F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */,
 				F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */,
 				F7327E372B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift in Sources */,
@@ -3960,7 +4008,9 @@
 				F7BD71E62636EAFC00643C34 /* NCNetworkingE2EE.swift in Sources */,
 				F7F878AF1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */,
 				F7327E3B2B73B8D600A462C7 /* Array+Extension.swift in Sources */,
+				F799DF832C4B7DCC003410B5 /* NCSectionFooter.swift in Sources */,
 				AF22B218277D196700DAB0CC /* NCShareExtension+Files.swift in Sources */,
+				F799DF862C4B7E56003410B5 /* NCSectionHeader.swift in Sources */,
 				F702F2D025EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
 				F343A4BE2A1E734600DDA874 /* Optional+Extension.swift in Sources */,
 				F72437802C10B92400C7C68D /* NCPermissions.swift in Sources */,
@@ -3975,7 +4025,6 @@
 				F7327E232B73A42F00A462C7 /* NCNetworking+Download.swift in Sources */,
 				F749B64D297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
 				F72FD3B8297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
-				F7EDE4E0262D7BAF00414FE6 /* NCGridCell.swift in Sources */,
 				F7A76DC8256A71CD00119AB3 /* UIImage+Extension.swift in Sources */,
 				F711A4E52AF9310500095DD8 /* NCUtility+Image.swift in Sources */,
 				F73EF7AA2B0223900087E6E9 /* NCManageDatabase+Comments.swift in Sources */,
@@ -3987,6 +4036,7 @@
 				F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */,
 				F7C30DF7291BC0D30017149B /* NCNetworkingE2EEUpload.swift in Sources */,
 				F78A10C229322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */,
+				F713FBE72C31646500F10760 /* NCNetworking+AsyncAwait.swift in Sources */,
 				F79FFB272A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */,
 				F7D68FCE28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				F73EF7B22B0224350087E6E9 /* NCManageDatabase+DirectEditing.swift in Sources */,
@@ -4025,6 +4075,7 @@
 				F359D8682A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				F7346E1628B0EF5C006CE2D2 /* Widget.swift in Sources */,
 				F78302F828B4C3E100B84583 /* NCManageDatabase+Activity.swift in Sources */,
+				F713FBE62C31646400F10760 /* NCNetworking+AsyncAwait.swift in Sources */,
 				F711A4E92AF9327600095DD8 /* UIImage+animatedGIF.m in Sources */,
 				F73EF7D02B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */,
 				F783030228B4C4B800B84583 /* NCUtility.swift in Sources */,
@@ -4096,6 +4147,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F713FBE82C31646600F10760 /* NCNetworking+AsyncAwait.swift in Sources */,
 				F36E64FC2B9733F20085ABB5 /* UIView+Extension.swift in Sources */,
 				F771E3F720E239B500AFB62D /* FileProviderExtension+Actions.swift in Sources */,
 				F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */,
@@ -4124,6 +4176,7 @@
 				F7327E2C2B73A53400A462C7 /* NCNetworking+Upload.swift in Sources */,
 				F7D68FCF28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				F73EF7CB2B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift in Sources */,
+				F7A5DF052C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift in Sources */,
 				F33EE6F62BF4C9B200CA1A51 /* PKCS12.swift in Sources */,
 				F73EF7C32B02250B0087E6E9 /* NCManageDatabase+GPS.swift in Sources */,
 				F7C9B9212B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */,
@@ -4138,6 +4191,7 @@
 				F7E98C1827E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				F785EEA42461A4A600B3F945 /* NCUtility.swift in Sources */,
 				F79B646226CA661600838ACA /* UIControl+Extension.swift in Sources */,
+				F7C1DAEF2C3D1DF4000BDC69 /* FileProviderDomain.swift in Sources */,
 				F73EF7AB2B0223900087E6E9 /* NCManageDatabase+Comments.swift in Sources */,
 				F7327E392B73B8D400A462C7 /* Array+Extension.swift in Sources */,
 				F78E2D6929AF02DB0024D4F3 /* Database.swift in Sources */,
@@ -4175,6 +4229,7 @@
 				F7E402332BA89551007E5609 /* NCTrash+Networking.swift in Sources */,
 				F7E7AEA72BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift in Sources */,
 				F73EF7A72B0223900087E6E9 /* NCManageDatabase+Comments.swift in Sources */,
+				F799DF882C4B83CC003410B5 /* NCCollectionViewCommon+EasyTipView.swift in Sources */,
 				F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */,
 				F702F30825EE5D47008F8E80 /* NCPopupViewController.swift in Sources */,
 				F733598125C1C188002ABA72 /* NCAskAuthorization.swift in Sources */,
@@ -4187,7 +4242,9 @@
 				F790110E21415BF600D7B136 /* NCViewerRichDocument.swift in Sources */,
 				F78ACD4021903CC20088454D /* NCGridCell.swift in Sources */,
 				F7D890752BD25C570050B8A6 /* NCCollectionViewCommon+DragDrop.swift in Sources */,
+				F7BD0A042C4689E9003A4A6D /* NCMedia+MediaLayout.swift in Sources */,
 				F761856B29E98543006EB3B0 /* NCIntroViewController.swift in Sources */,
+				F7743A142C33F13A0034F670 /* NCCollectionViewCommon+CollectionViewDataSource.swift in Sources */,
 				F75B0ABD244C4DBB00E58DCA /* NCActionCenter.swift in Sources */,
 				AF935067276B84E700BD078F /* NCMenu+FloatingPanel.swift in Sources */,
 				F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */,
@@ -4197,6 +4254,7 @@
 				F7802B322BD5584F00D74270 /* NCMedia+DragDrop.swift in Sources */,
 				F7EE66AD2A20B226009AE765 /* UILabel+Extension.swift in Sources */,
 				F78ACD4221903CE00088454D /* NCListCell.swift in Sources */,
+				F7BD0A002C468925003A4A6D /* NCMedia+CollectionViewDataSource.swift in Sources */,
 				F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */,
 				F7245924289BB50C00474787 /* ThreadSafeDictionary.swift in Sources */,
 				F73F537F1E929C8500F8678D /* NCMore.swift in Sources */,
@@ -4214,11 +4272,13 @@
 				F7B769A82B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift in Sources */,
 				F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */,
 				F7B934FE2BDCFE1E002B2FC9 /* NCDragDrop.swift in Sources */,
+				F713FBE52C31645200F10760 /* NCNetworking+AsyncAwait.swift in Sources */,
 				F77444F8222816D5000D5EB0 /* NCPickerViewController.swift in Sources */,
 				F77BB74A2899857B0090FC19 /* UINavigationController+Extension.swift in Sources */,
 				F7327E282B73A53400A462C7 /* NCNetworking+Upload.swift in Sources */,
 				F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */,
 				F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */,
+				F799DF822C4B7DCC003410B5 /* NCSectionFooter.swift in Sources */,
 				F76B649C2ADFFAED00014640 /* NCImageCache.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
 				F768822E2C0DD1E7001CF441 /* NCSettingsBundleHelper.swift in Sources */,
@@ -4235,7 +4295,7 @@
 				F75A9EE623796C6F0044CFCE /* NCNetworking.swift in Sources */,
 				F758B460212C56A400515F55 /* NCScan.swift in Sources */,
 				F76882262C0DD1E7001CF441 /* NCSettingsView.swift in Sources */,
-				F78ACD52219046DC0088454D /* NCSectionHeaderMenu.swift in Sources */,
+				F78ACD52219046DC0088454D /* NCSectionFirstHeader.swift in Sources */,
 				F72944F52A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */,
 				F710D2022405826100A6033D /* NCViewer+Menu.swift in Sources */,
 				F765E9CD295C585800A09ED8 /* NCUploadScanDocument.swift in Sources */,
@@ -4244,7 +4304,9 @@
 				F7BF9D822934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
 				AF7E504E27A2D8FF00B5E4AF /* UIBarButton+Extension.swift in Sources */,
 				F7A846DE2BB01ACB0024816F /* NCTrashCellProtocol.swift in Sources */,
+				F799DF852C4B7E56003410B5 /* NCSectionHeader.swift in Sources */,
 				F78A10BF29322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */,
+				F7743A122C33F0A20034F670 /* NCCollectionViewCommon+CollectionViewDelegate.swift in Sources */,
 				F704B5E92430C0B800632F5F /* NCCreateFormUploadConflictCell.swift in Sources */,
 				F7327E3D2B73B92800A462C7 /* NCNetworking+Synchronization.swift in Sources */,
 				F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */,
@@ -4255,9 +4317,8 @@
 				F76882362C0DD1E7001CF441 /* NCAcknowledgementsView.swift in Sources */,
 				F785EE9D246196DF00B3F945 /* NCNetworkingE2EE.swift in Sources */,
 				F724377B2C10B83E00C7C68D /* NCPermissions.swift in Sources */,
-				F76673ED22C901F6007ED366 /* FileProviderDomain.swift in Sources */,
 				F794E13D2BBBFF2E003693D7 /* NCMainTabBarController.swift in Sources */,
-				F7CBC1252BAC8B0000EC1D55 /* NCSectionHeaderEmptyData.swift in Sources */,
+				F7CBC1252BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift in Sources */,
 				F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */,
 				F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */,
 				F76882352C0DD1E7001CF441 /* NCWebBrowserView.swift in Sources */,
@@ -4294,9 +4355,10 @@
 				F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */,
 				F76882222C0DD1E7001CF441 /* NCCapabilitiesView.swift in Sources */,
-				AF93471A27E2361E002537EE /* NCShareAdvancePermissionHeader.swift in Sources */,
+				AF93471A27E2361E002537EE /* NCShareHeader.swift in Sources */,
 				F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */,
 				F7A7FDDD2C2DBD6200E9A93A /* NCDeepLinkHandler.swift in Sources */,
+				F778231E2C42C07C001BB94F /* NCCollectionViewCommon+MediaLayout.swift in Sources */,
 				3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */,
 				F710D1F52405770F00A6033D /* NCViewerPDF.swift in Sources */,
 				F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */,
@@ -4315,6 +4377,7 @@
 				F7145610296433C80038D028 /* NCDocumentCamera.swift in Sources */,
 				F76882312C0DD1E7001CF441 /* NCFileNameView.swift in Sources */,
 				F7381EE1218218C9000B1560 /* NCOffline.swift in Sources */,
+				F751247C2C42919C00E63DB8 /* NCPhotoCell.swift in Sources */,
 				F7A509252C26BD5D00326106 /* NCCreateDocument.swift in Sources */,
 				F7FA80002C0F4F3B0072FC60 /* NCUploadAssetsModel.swift in Sources */,
 				F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */,
@@ -4322,6 +4385,7 @@
 				F79B646026CA661600838ACA /* UIControl+Extension.swift in Sources */,
 				F768823E2C0DD305001CF441 /* LazyView.swift in Sources */,
 				F7CA212D25F1333300826ABB /* NCAccountRequest.swift in Sources */,
+				F747EB0D2C4AC1FF00F959A8 /* NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift in Sources */,
 				F765F73125237E3F00391DBE /* NCRecent.swift in Sources */,
 				F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */,
 				F7FA7FFC2C0F4EE40072FC60 /* NCViewerQuickLookView.swift in Sources */,
@@ -4338,6 +4402,7 @@
 				F76882302C0DD1E7001CF441 /* NCFileNameModel.swift in Sources */,
 				F72FD3B5297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
 				F73EF7CF2B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */,
+				F7BD0A022C4689A4003A4A6D /* NCMedia+CollectionViewDelegate.swift in Sources */,
 				F3A047982BD2668800658E7B /* NCAssistantCreateNewTask.swift in Sources */,
 				AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */,
 				F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */,
@@ -4351,7 +4416,7 @@
 				F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F3A047992BD2668800658E7B /* NCAssistantTask.swift in Sources */,
 				F76882322C0DD1E7001CF441 /* NCAutoUploadView.swift in Sources */,
-				F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */,
+				F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBar.swift in Sources */,
 				F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
 				F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */,
 				F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */,
@@ -4393,6 +4458,7 @@
 				F76882232C0DD1E7001CF441 /* NCCapabilitiesModel.swift in Sources */,
 				F7D68FCC28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				F76882292C0DD1E7001CF441 /* NCManageE2EEModel.swift in Sources */,
+				F799DF8B2C4B84EB003410B5 /* NCCollectionViewCommon+EndToEndInitialize.swift in Sources */,
 				F78E2D6529AF02DB0024D4F3 /* Database.swift in Sources */,
 				F70CEF5623E9C7E50007035B /* UIColor+Extension.swift in Sources */,
 				F76882242C0DD1E7001CF441 /* NCSettingsAdvancedView.swift in Sources */,
@@ -5348,7 +5414,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 11;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -5375,7 +5441,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 5.4.2;
+				MARKETING_VERSION = 5.5.0;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_CFLAGS = "-v";
 				OTHER_LDFLAGS = "";
@@ -5414,7 +5480,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 11;
 				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
@@ -5438,7 +5504,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 5.4.2;
+				MARKETING_VERSION = 5.5.0;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_CFLAGS = "-v";
 				OTHER_LDFLAGS = "";
@@ -5713,7 +5779,7 @@
 			repositoryURL = "https://github.com/nextcloud/NextcloudKit";
 			requirement = {
 				kind = exactVersion;
-				version = 3.0.2;
+				version = 4.0.3;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {

+ 96 - 0
Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension UI.xcscheme

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1520"
+   wasCreatedForAppExtension = "YES"
+   version = "2.0">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F70716E22987F81400E72C1D"
+               BuildableName = "File Provider Extension UI.appex"
+               BlueprintName = "File Provider Extension UI"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+               BuildableName = "Nextcloud.app"
+               BlueprintName = "Nextcloud"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      shouldAutocreateTestPlan = "YES">
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = ""
+      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
+      launchStyle = "0"
+      askForAppToLaunch = "Yes"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      askForAppToLaunch = "Yes"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 96 - 0
Nextcloud.xcodeproj/xcshareddata/xcschemes/WidgetDashboardIntentHandler.xcscheme

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1520"
+   wasCreatedForAppExtension = "YES"
+   version = "2.0">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F7C9738F28F17131002C43E2"
+               BuildableName = "WidgetDashboardIntentHandler.appex"
+               BlueprintName = "WidgetDashboardIntentHandler"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+               BuildableName = "Nextcloud.app"
+               BlueprintName = "Nextcloud"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      shouldAutocreateTestPlan = "YES">
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = ""
+      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
+      launchStyle = "0"
+      askForAppToLaunch = "Yes"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      askForAppToLaunch = "Yes"
+      launchAutomaticallySubstyle = "2">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
+            BuildableName = "Nextcloud.app"
+            BlueprintName = "Nextcloud"
+            ReferencedContainer = "container:Nextcloud.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 1 - 1
Notification Service Extension/NotificationService.swift

@@ -46,7 +46,7 @@ class NotificationService: UNNotificationServiceExtension {
                         if var json = try JSONSerialization.jsonObject(with: data) as? [String: AnyObject],
                            let subject = json["subject"] as? String {
                             bestAttemptContent.body = subject
-                            if let pref = UserDefaults(suiteName: NCBrandOptions.shared.capabilitiesGroups) {
+                            if let pref = UserDefaults(suiteName: NCBrandOptions.shared.capabilitiesGroup) {
                                 json["account"] = tableAccount.account as AnyObject
                                 pref.set(json, forKey: "NOTIFICATION_DATA")
                                 pref.synchronize()

+ 3 - 5
Share/NCShareCell.swift

@@ -50,12 +50,10 @@ class NCShareCell: UITableViewCell {
 
         if let image = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 80, height: 80)) {
             imageCell.image = image
+            imageCell.contentMode = .scaleAspectFill
         } else {
-            if !resultInternalType.iconName.isEmpty {
-                imageCell?.image = UIImage(named: resultInternalType.iconName)
-            } else {
-                imageCell?.image = NCImageCache.images.file
-            }
+            imageCell.image = utility.loadImage(named: resultInternalType.iconName, useTypeIconFile: true)
+            imageCell.contentMode = .scaleAspectFit
         }
 
         fileNameCell?.text = fileName

+ 2 - 9
Share/NCShareExtension+DataSource.swift

@@ -42,7 +42,7 @@ extension NCShareExtension: UICollectionViewDelegate {
 
     func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
         if kind == UICollectionView.elementKindSectionHeader {
-            guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderEmptyData", for: indexPath) as? NCSectionHeaderEmptyData else { return NCSectionHeaderEmptyData() }
+            guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() }
             if self.dataSourceTask?.state == .running {
                 header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.brandElement])
                 header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
@@ -80,7 +80,6 @@ extension NCShareExtension: UICollectionViewDataSource {
     }
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-
         guard let metadata = dataSource.cellForItemAt(indexPath: indexPath), let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else {
             return UICollectionViewCell()
         }
@@ -92,7 +91,6 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.fileUser = metadata.ownerId
         cell.labelTitle.text = metadata.fileNameView
         cell.labelTitle.textColor = NCBrandColor.shared.textColor
-
         cell.imageSelect.image = nil
         cell.imageStatus.image = nil
         cell.imageLocal.image = nil
@@ -101,14 +99,12 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.imageMore.image = nil
         cell.imageItem.image = nil
         cell.imageItem.backgroundColor = nil
-
         cell.progressView.progress = 0.0
 
         if metadata.directory {
             setupDirectoryCell(cell, indexPath: indexPath, with: metadata)
         }
 
-        // image Favorite
         if metadata.favorite {
             cell.imageFavorite.image = NCImageCache.images.favorite
         }
@@ -117,17 +113,14 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.backgroundView = nil
         cell.hideButtonMore(true)
         cell.hideButtonShare(true)
-        cell.selectMode(false)
+        cell.selected(false, isEditMode: false)
 
-        // Live Photo
         if metadata.isLivePhoto {
             cell.imageStatus.image = NCImageCache.images.livePhoto
         }
 
-        // Add TAGS
         cell.setTags(tags: Array(metadata.tags))
 
-        // Remove last separator
         cell.separator.isHidden = collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1
 
         return cell

+ 1 - 21
Share/NCShareExtension+Files.swift

@@ -25,39 +25,20 @@ import Foundation
 import UniformTypeIdentifiers
 
 extension NCShareExtension {
-
     @objc func reloadDatasource(withLoadFolder: Bool) {
-
-        var groupByField = "name"
-
         layoutForView = NCManageDatabase.shared.setLayoutForView(account: activeAccount.account, key: keyLayout, serverUrl: serverUrl)
-
-        // set GroupField for Grid
-        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
-            groupByField = "classFile"
-        }
-
         let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
-        self.dataSource = NCDataSource(
-            metadatas: metadatas,
-            account: activeAccount.account,
-            sort: layoutForView?.sort,
-            ascending: layoutForView?.ascending,
-            directoryOnTop: layoutForView?.directoryOnTop,
-            favoriteOnTop: true,
-            groupByField: groupByField)
+        self.dataSource = NCDataSource(metadatas: metadatas, account: activeAccount.account, layoutForView: layoutForView)
 
         if withLoadFolder {
             loadFolder()
         } else {
             self.refreshControl.endRefreshing()
         }
-
         collectionView.reloadData()
     }
 
     @objc func didCreateFolder(_ notification: NSNotification) {
-
         guard let userInfo = notification.userInfo as NSDictionary?,
               let ocId = userInfo["ocId"] as? String,
               let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId)
@@ -69,7 +50,6 @@ extension NCShareExtension {
     }
 
     func loadFolder() {
-
         NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { task in
             self.dataSourceTask = task
             self.collectionView.reloadData()

+ 9 - 1
Share/NCShareExtension+NCDelegate.swift

@@ -97,7 +97,6 @@ extension NCShareExtension: NCAccountRequestDelegate {
 }
 
 extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCellDelegate {
-
     func removeFile(named fileName: String) {
         guard let index = self.filesName.firstIndex(of: fileName) else {
             return showAlert(title: "_file_not_found_", description: fileName)
@@ -139,6 +138,15 @@ extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCel
         filesName[fileIx] = fileNameNew
         tableView.reloadData()
     }
+
+    func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) {
+    }
+
+    func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) {
+    }
+
+    func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {
+    }
 }
 
 extension NCShareExtension: NCCreateFormUploadConflictDelegate {

+ 1 - 2
Share/NCShareExtension.swift

@@ -81,7 +81,7 @@ class NCShareExtension: UIViewController {
 
         self.navigationController?.navigationBar.prefersLargeTitles = false
 
-        collectionView.register(UINib(nibName: "NCSectionHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderEmptyData")
+        collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
         collectionView.register(UINib(nibName: "NCListCell", bundle: nil), forCellWithReuseIdentifier: "listCell")
         collectionView.collectionViewLayout = NCListLayout()
 
@@ -395,7 +395,6 @@ extension NCShareExtension {
 }
 
 extension NCShareExtension: uploadE2EEDelegate {
-
     func start() {
         self.hud.progress = 0
     }

+ 1 - 1
Tests/Common/BaseXCTestCase.swift

@@ -18,7 +18,7 @@ class BaseXCTestCase: XCTestCase {
     func setupAppToken() {
         let expectation = expectation(description: "Should get app token")
 
-        NextcloudKit.shared.getAppPassword(serverUrl: TestConstants.server, username: TestConstants.username, password: TestConstants.password) { token, data, error in
+        NextcloudKit.shared.getAppPassword(url: TestConstants.server, user: TestConstants.username, password: TestConstants.password) { token, _, error in
             XCTAssertEqual(error.errorCode, 0)
             XCTAssertNotNil(token)
 

+ 1 - 1
Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift

@@ -37,7 +37,7 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
         let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)"
         let serverUrlFileName = "\(serverUrl)/\(folderName)"
 
-        NextcloudKit.shared.setup(account: TestConstants.account, user: TestConstants.username, userId: TestConstants.username, password: appToken, urlBase: TestConstants.server)
+        NextcloudKit.shared.setup(account: TestConstants.account, user: TestConstants.username, userId: TestConstants.username, password: appToken, urlBase: TestConstants.server, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
 
         // Test creating folder
         NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, account: TestConstants.account, urlBase: TestConstants.server, userId: TestConstants.username, withPush: true, sceneIdentifier: nil) { error in

+ 4 - 5
Widget/Dashboard/DashboardData.swift

@@ -21,6 +21,7 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import Intents
 import NextcloudKit
@@ -75,7 +76,6 @@ func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
 }
 
 func convertDataToImage(data: Data?, size: CGSize, fileNameToWrite: String?) -> UIImage? {
-
     guard let data = data else { return nil }
     var imageData: UIImage?
 
@@ -97,7 +97,6 @@ func convertDataToImage(data: Data?, size: CGSize, fileNameToWrite: String?) ->
 }
 
 func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, displaySize: CGSize, completion: @escaping (_ entry: DashboardDataEntry) -> Void) {
-
     let utilityFileSystem = NCUtilityFileSystem()
     let utility = NCUtility()
     let dashboardItems = getDashboardItems(displaySize: displaySize, withButton: false)
@@ -170,7 +169,6 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
 
     let options = NKRequestOptions(timeout: 90, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
     NextcloudKit.shared.getDashboardWidgetsApplication(id, options: options) { _, results, data, error in
-
         Task {
             var datas = [DashboardData]()
             var numberItems = 0
@@ -227,8 +225,9 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
                                     if FileManager().fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
                                         icon = image
                                     } else {
-                                        let (_, data, _) = await NextcloudKit.shared.getPreview(url: url)
-                                        if let image = convertDataToImage(data: data, size: CGSize(width: 256, height: 256), fileNameToWrite: fileName) {
+                                        let (_, data, error) = await NCNetworking.shared.downloadPreview(url: url)
+                                        if error == .success,
+                                           let image = convertDataToImage(data: data, size: CGSize(width: 256, height: 256), fileNameToWrite: fileName) {
                                             icon = image
                                         }
                                     }

+ 1 - 1
Widget/Dashboard/DashboardWidgetProvider.swift

@@ -21,12 +21,12 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import Intents
 import SwiftUI
 
 struct DashboardWidgetProvider: IntentTimelineProvider {
-
     typealias Intent = DashboardIntent
     typealias Entry = DashboardDataEntry
 

+ 13 - 15
Widget/Files/FilesData.swift

@@ -21,6 +21,7 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import Intents
 import NextcloudKit
@@ -60,7 +61,6 @@ let filesDatasTest: [FilesData] = [
 ]
 
 func getTitleFilesWidget(account: tableAccount?) -> String {
-
     let hour = Calendar.current.component(.hour, from: Date())
     var good = ""
 
@@ -85,7 +85,6 @@ func getFilesItems(displaySize: CGSize) -> Int {
 }
 
 func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySize: CGSize, completion: @escaping (_ entry: FilesDataEntry) -> Void) {
-
     let utilityFileSystem = NCUtilityFileSystem()
     let utility = NCUtility()
     let filesItems = getFilesItems(displaySize: displaySize)
@@ -131,6 +130,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 <d:resourcetype/>
                 <d:getcontentlength/>
                 <d:getlastmodified/>
+                <d:getetag/>
                 <id xmlns=\"http://owncloud.org/ns\"/>
                 <fileid xmlns=\"http://owncloud.org/ns\"/>
                 <size xmlns=\"http://owncloud.org/ns\"/>
@@ -163,7 +163,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
         </d:order>
     </d:orderby>
     <d:limit>
-        <d:nresults>25</d:nresults>
+        <d:nresults>50</d:nresults>
     </d:limit>
     </d:basicsearch>
     </d:searchrequest>
@@ -197,8 +197,8 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
             let files = files.sorted(by: { ($0.date as Date) > ($1.date as Date) })
 
             for file in files {
-                var image: UIImage?
                 var useTypeIconFile = false
+                var image: UIImage?
 
                 if file.directory || (!file.livePhotoFile.isEmpty && file.classFile == NKCommon.TypeClassFile.video.rawValue) {
                     continue
@@ -216,17 +216,15 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 guard let url = URL(string: urlString) else { continue }
 
                 // IMAGE
-                if let result = utility.createFilePreviewImage(ocId: file.ocId, etag: file.etag, fileNameView: file.fileName, classFile: file.classFile, status: 0, createPreviewMedia: false) {
-                    image = result
-                } else if file.hasPreview {
-
-                    let fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)
-                    let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)
+                let fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)
+                let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)
+                if FileManager.default.fileExists(atPath: fileNameIconLocalPath) {
+                    image = UIImage(contentsOfFile: fileNameIconLocalPath)
+                }
+                if image == nil, file.hasPreview {
                     let sizePreview = NCUtility().getSizePreview(width: Int(file.width), height: Int(file.height))
-                    let (_, _, imageIcon, _, _, _) = await NextcloudKit.shared.downloadPreview(fileId: file.fileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, fileNameIconLocalPath: fileNameIconLocalPath, widthPreview: Int(sizePreview.width), heightPreview: Int(sizePreview.height), sizeIcon: NCGlobal.shared.sizeIcon, options: options)
-                    if let result = imageIcon {
-                         image = result
-                    }
+                    let (_, _, imageIcon, _, _, _) = await NCNetworking.shared.downloadPreview(fileId: file.fileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, fileNameIconLocalPath: fileNameIconLocalPath, widthPreview: Int(sizePreview.width), heightPreview: Int(sizePreview.height), sizeIcon: NCGlobal.shared.sizeIcon, options: options)
+                    image = imageIcon
                 }
                 if image == nil {
                     image = utility.loadImage(named: file.iconName, useTypeIconFile: true)
@@ -237,7 +235,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
 
                 // DATA
-                let data = FilesData(id: metadata.ocId, image: image!, title: metadata.fileNameView, subTitle: subTitle, url: url, useTypeIconFile: useTypeIconFile)
+                let data = FilesData(id: metadata.ocId, image: image ?? UIImage(), title: metadata.fileNameView, subTitle: subTitle, url: url, useTypeIconFile: useTypeIconFile)
                 datas.append(data)
                 if datas.count == filesItems { break}
             }

+ 1 - 1
Widget/Files/FilesWidgetProvider.swift

@@ -21,12 +21,12 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import Intents
 import SwiftUI
 
 struct FilesWidgetProvider: IntentTimelineProvider {
-
     typealias Entry = FilesDataEntry
     typealias Intent = AccountIntent
 

+ 1 - 1
Widget/Lockscreen/LockscreenData.swift

@@ -21,6 +21,7 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import NextcloudKit
 
@@ -36,7 +37,6 @@ struct LockscreenData: TimelineEntry {
 }
 
 func getLockscreenDataEntry(configuration: AccountIntent?, isPreview: Bool, family: WidgetFamily, completion: @escaping (_ entry: LockscreenData) -> Void) {
-
     let utilityFileSystem = NCUtilityFileSystem()
     var account: tableAccount?
     var quotaRelative: Float = 0

+ 1 - 1
Widget/Lockscreen/LockscreenWidgetProvider.swift

@@ -21,12 +21,12 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import Intents
 import SwiftUI
 
 struct LockscreenWidgetProvider: IntentTimelineProvider {
-
     typealias Entry = LockscreenData
     typealias Intent = AccountIntent
 

+ 1 - 1
Widget/Toolbar/ToolbarData.swift

@@ -21,6 +21,7 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 
 struct ToolbarDataEntry: TimelineEntry {
@@ -33,7 +34,6 @@ struct ToolbarDataEntry: TimelineEntry {
 }
 
 func getToolbarDataEntry(isPreview: Bool, completion: @escaping (_ entry: ToolbarDataEntry) -> Void) {
-
     var userId = ""
     var url = ""
 

+ 1 - 1
Widget/Toolbar/ToolbarWidgetProvider.swift

@@ -21,11 +21,11 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+import UIKit
 import WidgetKit
 import SwiftUI
 
 struct ToolbarWidgetProvider: TimelineProvider {
-
     typealias Entry = ToolbarDataEntry
 
     func placeholder(in context: Context) -> Entry {

+ 0 - 10
WidgetDashboardIntentHandler/IntentHandler.swift

@@ -28,10 +28,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
 
     // MARK: - Account
 
-    // Account
-
     func provideAccountsOptionsCollection(for intent: AccountIntent, with completion: @escaping (INObjectCollection<Accounts>?, Error?) -> Void) {
-
         var accounts: [Accounts] = []
         let results = NCManageDatabase.shared.getAllAccount()
 
@@ -52,7 +49,6 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     func defaultAccounts(for intent: AccountIntent) -> Accounts? {
-
         if NCManageDatabase.shared.getActiveAccount() == nil {
             return nil
         } else {
@@ -63,9 +59,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     // MARK: - Dashboard
 
     // Application
-
     func provideApplicationsOptionsCollection(for intent: DashboardIntent, with completion: @escaping (INObjectCollection<Applications>?, Error?) -> Void) {
-
         var applications: [Applications] = []
         var account: tableAccount?
 
@@ -90,7 +84,6 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     func defaultApplications(for intent: DashboardIntent) -> Applications? {
-
         guard let account = NCManageDatabase.shared.getActiveAccount() else {
             return nil
         }
@@ -101,9 +94,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     // Account
-
     func provideAccountsOptionsCollection(for intent: DashboardIntent, with completion: @escaping (INObjectCollection<Accounts>?, Error?) -> Void) {
-
         var accounts: [Accounts] = []
         let results = NCManageDatabase.shared.getAllAccount()
 
@@ -124,7 +115,6 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     func defaultAccounts(for intent: DashboardIntent) -> Accounts? {
-
         if NCManageDatabase.shared.getActiveAccount() == nil {
             return nil
         } else {

+ 9 - 7
iOSClient/Account Settings/NCAccountSettingsModel.swift

@@ -131,19 +131,19 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
     }
 
     /// Function to update the user data
-    func getUserStatus() -> (statusImage: UIImage, statusMessage: String, descriptionMessage: String) {
+    func getUserStatus() -> (statusImage: UIImage?, statusMessage: String, descriptionMessage: String) {
         guard let activeAccount else { return (UIImage(), "", "") }
         if NCGlobal.shared.capabilityUserStatusEnabled,
            let tableAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", activeAccount.account)) {
             return NCUtility().getUserStatus(userIcon: tableAccount.userStatusIcon, userStatus: tableAccount.userStatusStatus, userMessage: tableAccount.userStatusMessage)
         }
-        return (UIImage(), "", "")
+        return (nil, "", "")
     }
 
     /// Function to know the height of "account" data
     func getTableViewHeight() -> CGFloat {
         guard let activeAccount else { return 0 }
-        var height: CGFloat = 190
+        var height: CGFloat = NCGlobal.shared.capabilityUserStatusEnabled ? 190 : 220
         if NCGlobal.shared.capabilityUserStatusEnabled,
            let tableAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", activeAccount.account)) {
             if !tableAccount.email.isEmpty { height += 30 }
@@ -168,20 +168,22 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
 
     @objc func changeAccount() {
         if let activeAccount {
-            self.appDelegate.changeAccount(activeAccount.account, userProfile: nil)
+            self.appDelegate.changeAccount(activeAccount.account, userProfile: nil) { }
         }
     }
 
     /// Function to delete the current account
     func deleteAccount() {
         if let activeAccount {
-            appDelegate.deleteAccount(activeAccount.account, wipe: false)
+            appDelegate.deleteAccount(activeAccount.account)
             if let account = NCManageDatabase.shared.getAllAccount().first?.account {
-                appDelegate.changeAccount(account, userProfile: nil)
+                appDelegate.changeAccount(account, userProfile: nil) {
+                    onViewAppear()
+                }
             } else {
                 dismissView = true
+                appDelegate.openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
             }
-            onViewAppear()
         }
     }
 }

+ 32 - 28
iOSClient/Account Settings/NCAccountSettingsView.swift

@@ -52,16 +52,18 @@ struct NCAccountSettingsView: View {
                                         .resizable()
                                         .scaledToFit()
                                         .frame(width: UIScreen.main.bounds.width, height: 75)
-                                    ZStack {
-                                        Circle()
-                                            .fill(.white)
-                                            .frame(width: 30, height: 30)
-                                        Image(uiImage: status.statusImage)
-                                            .resizable()
-                                            .scaledToFit()
-                                            .frame(width: 30, height: 30)
+                                    if let statusImage = status.statusImage {
+                                        ZStack {
+                                            Circle()
+                                                .fill(.white)
+                                                .frame(width: 30, height: 30)
+                                            Image(uiImage: statusImage)
+                                                .resizable()
+                                                .scaledToFit()
+                                                .frame(width: 30, height: 30)
                                         }
                                         .offset(x: 30, y: 30)
+                                    }
                                 }
                                 .frame(maxWidth: .infinity, maxHeight: .infinity)
                                 Text(model.getUserName())
@@ -153,28 +155,30 @@ struct NCAccountSettingsView: View {
                     }
                     ///
                     /// User Status
-                    Button(action: {
-                        showUserStatus = true
-                    }, label: {
-                        HStack {
-                            Image(systemName: "moon.fill")
-                                .resizable()
-                                .scaledToFit()
-                                .font(Font.system(.body).weight(.light))
-                                .frame(width: 20, height: 20)
-                                .foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
-                            Text(NSLocalizedString("_set_user_status_", comment: ""))
-                                .lineLimit(1)
-                                .truncationMode(.middle)
-                                .foregroundStyle(Color(NCBrandColor.shared.textColor))
-                                .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
+                    if NCGlobal.shared.capabilityUserStatusEnabled {
+                        Button(action: {
+                            showUserStatus = true
+                        }, label: {
+                            HStack {
+                                Image(systemName: "moon.fill")
+                                    .resizable()
+                                    .scaledToFit()
+                                    .font(Font.system(.body).weight(.light))
+                                    .frame(width: 20, height: 20)
+                                    .foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
+                                Text(NSLocalizedString("_set_user_status_", comment: ""))
+                                    .lineLimit(1)
+                                    .truncationMode(.middle)
+                                    .foregroundStyle(Color(NCBrandColor.shared.textColor))
+                                    .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
+                            }
+                            .font(.system(size: 14))
+                        })
+                        .sheet(isPresented: $showUserStatus) {
+                            UserStatusView(showUserStatus: $showUserStatus)
                         }
-                        .font(.system(size: 14))
-                    })
-                    .sheet(isPresented: $showUserStatus) {
-                        UserStatusView(showUserStatus: $showUserStatus)
+                        .onChange(of: showUserStatus) { _ in }
                     }
-                    .onChange(of: showUserStatus) { _ in }
                     ///
                     /// Certificate server
                     Button(action: {

+ 20 - 60
iOSClient/Activity/NCActivity.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="nhT-TJ-YvX">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="nhT-TJ-YvX">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -17,93 +17,52 @@
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="20" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="X49-xg-JXO">
-                                <rect key="frame" x="0.0" y="44" width="414" height="818"/>
+                                <rect key="frame" x="0.0" y="48" width="414" height="848"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <prototypes>
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="tableCell" rowHeight="120" id="ggj-aE-fnh" customClass="NCActivityTableViewCell" customModule="Nextcloud" customModuleProvider="target">
-                                        <rect key="frame" x="0.0" y="44.5" width="414" height="120"/>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="tableCell" rowHeight="35" id="ggj-aE-fnh" customClass="NCActivityTableViewCell" customModule="Nextcloud" customModuleProvider="target">
+                                        <rect key="frame" x="0.0" y="50" width="414" height="35"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ggj-aE-fnh" id="i35-U4-bEk">
-                                            <rect key="frame" x="0.0" y="0.0" width="414" height="120"/>
+                                            <rect key="frame" x="0.0" y="0.0" width="414" height="35"/>
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcO-YL-MuT">
-                                                    <rect key="frame" x="88" y="5" width="316" height="25"/>
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" text="Label" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcO-YL-MuT">
+                                                    <rect key="frame" x="100" y="0.0" width="304" height="18"/>
                                                     <fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="15"/>
                                                     <nil key="textColor"/>
                                                     <nil key="highlightedColor"/>
                                                 </label>
-                                                <imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="LQ8-cO-794" userLabel="avatar">
-                                                    <rect key="frame" x="50" y="0.0" width="30" height="30"/>
+                                                <imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="LQ8-cO-794" userLabel="avatar">
+                                                    <rect key="frame" x="50" y="0.0" width="35" height="35"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="width" constant="30" id="OKz-e8-DzD"/>
-                                                        <constraint firstAttribute="height" constant="30" id="fwd-J4-5uY"/>
+                                                        <constraint firstAttribute="width" constant="35" id="OKz-e8-DzD"/>
+                                                        <constraint firstAttribute="height" constant="35" id="fwd-J4-5uY"/>
                                                     </constraints>
                                                 </imageView>
                                                 <imageView userInteractionEnabled="NO" alpha="0.59999999999999998" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xNG-sf-PnA">
-                                                    <rect key="frame" x="20" y="5" width="20" height="20"/>
+                                                    <rect key="frame" x="20" y="8" width="20" height="20"/>
                                                     <constraints>
                                                         <constraint firstAttribute="width" constant="20" id="Lbv-yi-vAh"/>
                                                         <constraint firstAttribute="height" constant="20" id="TML-VJ-2i3"/>
                                                     </constraints>
                                                 </imageView>
-                                                <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="KpO-no-BMl">
-                                                    <rect key="frame" x="50" y="40" width="364" height="60"/>
-                                                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                                    <constraints>
-                                                        <constraint firstAttribute="height" constant="60" id="deQ-wc-jNT" userLabel="height = 60"/>
-                                                    </constraints>
-                                                    <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="nnU-ds-wda">
-                                                        <size key="itemSize" width="50" height="50"/>
-                                                        <size key="headerReferenceSize" width="0.0" height="0.0"/>
-                                                        <size key="footerReferenceSize" width="0.0" height="0.0"/>
-                                                        <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
-                                                    </collectionViewFlowLayout>
-                                                    <cells>
-                                                        <collectionViewCell clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="collectionCell" id="uIN-z5-7vk" customClass="NCActivityCollectionViewCell" customModule="Nextcloud" customModuleProvider="target">
-                                                            <rect key="frame" x="0.0" y="5" width="50" height="50"/>
-                                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                                                            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
-                                                                <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
-                                                                <autoresizingMask key="autoresizingMask"/>
-                                                                <subviews>
-                                                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="J5b-2X-AsF">
-                                                                        <rect key="frame" x="0.0" y="-1" width="50" height="50"/>
-                                                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                                                                    </imageView>
-                                                                </subviews>
-                                                            </view>
-                                                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                                            <connections>
-                                                                <outlet property="imageView" destination="J5b-2X-AsF" id="7dI-m8-o1E"/>
-                                                            </connections>
-                                                        </collectionViewCell>
-                                                    </cells>
-                                                </collectionView>
                                             </subviews>
-                                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                             <constraints>
                                                 <constraint firstAttribute="trailing" secondItem="fcO-YL-MuT" secondAttribute="trailing" constant="10" id="1pG-qk-inI"/>
                                                 <constraint firstItem="LQ8-cO-794" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" id="3fU-rp-D7s"/>
-                                                <constraint firstItem="xNG-sf-PnA" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" constant="5" id="4EB-59-y1Y"/>
+                                                <constraint firstItem="xNG-sf-PnA" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" constant="8" id="4EB-59-y1Y"/>
                                                 <constraint firstItem="xNG-sf-PnA" firstAttribute="leading" secondItem="i35-U4-bEk" secondAttribute="leading" constant="20" id="CRN-18-SeU"/>
-                                                <constraint firstAttribute="bottom" secondItem="KpO-no-BMl" secondAttribute="bottom" constant="20" id="ULe-Tt-dBj"/>
-                                                <constraint firstItem="fcO-YL-MuT" firstAttribute="leading" secondItem="xNG-sf-PnA" secondAttribute="trailing" constant="48" id="am5-CT-0kZ" userLabel="Subject.leading = Icon.trailing + 50"/>
+                                                <constraint firstItem="fcO-YL-MuT" firstAttribute="leading" secondItem="LQ8-cO-794" secondAttribute="trailing" constant="15" id="am5-CT-0kZ" userLabel="Subject.leading = Icon.trailing + 50"/>
                                                 <constraint firstItem="LQ8-cO-794" firstAttribute="leading" secondItem="xNG-sf-PnA" secondAttribute="trailing" constant="10" id="aqp-Wu-9Hk"/>
-                                                <constraint firstItem="fcO-YL-MuT" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" constant="5" id="faC-by-km5"/>
-                                                <constraint firstItem="KpO-no-BMl" firstAttribute="leading" secondItem="i35-U4-bEk" secondAttribute="leading" constant="50" id="l0Y-89-eTm"/>
-                                                <constraint firstAttribute="trailing" secondItem="KpO-no-BMl" secondAttribute="trailing" id="vWj-9H-JLc"/>
-                                                <constraint firstItem="KpO-no-BMl" firstAttribute="top" secondItem="fcO-YL-MuT" secondAttribute="bottom" constant="10" id="z1e-vv-qJb"/>
+                                                <constraint firstItem="fcO-YL-MuT" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" id="faC-by-km5"/>
                                             </constraints>
                                         </tableViewCellContentView>
-                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                         <connections>
                                             <outlet property="avatar" destination="LQ8-cO-794" id="2PE-uD-pug"/>
-                                            <outlet property="collectionView" destination="KpO-no-BMl" id="xvR-CA-cZx"/>
-                                            <outlet property="collectionViewHeightConstraint" destination="deQ-wc-jNT" id="nAT-iS-UjK"/>
                                             <outlet property="icon" destination="xNG-sf-PnA" id="hxb-Vr-oQX"/>
                                             <outlet property="subject" destination="fcO-YL-MuT" id="L4q-rj-l04"/>
-                                            <outlet property="subjectTrailingConstraint" destination="am5-CT-0kZ" id="PeK-M5-hHW"/>
+                                            <outlet property="subjectLeadingConstraint" destination="am5-CT-0kZ" id="J7c-Hb-2V2"/>
                                         </connections>
                                     </tableViewCell>
                                 </prototypes>
@@ -115,10 +74,11 @@
                             </tableView>
                         </subviews>
                         <viewLayoutGuide key="safeArea" id="USa-eR-a1s"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                         <constraints>
                             <constraint firstItem="X49-xg-JXO" firstAttribute="trailing" secondItem="USa-eR-a1s" secondAttribute="trailing" id="5we-Fh-GVu"/>
                             <constraint firstItem="X49-xg-JXO" firstAttribute="top" secondItem="USa-eR-a1s" secondAttribute="top" id="E1U-4Q-6uu"/>
-                            <constraint firstItem="USa-eR-a1s" firstAttribute="bottom" secondItem="X49-xg-JXO" secondAttribute="bottom" id="aHq-g4-dUG"/>
+                            <constraint firstAttribute="bottom" secondItem="X49-xg-JXO" secondAttribute="bottom" id="aHq-g4-dUG"/>
                             <constraint firstItem="X49-xg-JXO" firstAttribute="leading" secondItem="USa-eR-a1s" secondAttribute="leading" id="pfF-ag-f7x"/>
                         </constraints>
                     </view>
@@ -129,7 +89,7 @@
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="UOE-pW-DRy" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="-330.39999999999998" y="139.65517241379311"/>
+            <point key="canvasLocation" x="-330.43478260869568" y="139.28571428571428"/>
         </scene>
     </scenes>
 </document>

+ 40 - 49
iOSClient/Activity/NCActivity.swift

@@ -27,15 +27,12 @@ import SwiftRichString
 import NextcloudKit
 
 class NCActivity: UIViewController, NCSharePagingContent {
-
+    @IBOutlet weak var viewContainerConstraint: NSLayoutConstraint!
     @IBOutlet weak var tableView: UITableView!
 
     var commentView: NCActivityCommentView?
-    var textField: UITextField? { commentView?.newCommentField }
-
-    @IBOutlet weak var viewContainerConstraint: NSLayoutConstraint!
+    var textField: UIView? { commentView?.newCommentField }
     var height: CGFloat = 0
-
     var metadata: tableMetadata?
     var showComments: Bool = false
 
@@ -46,7 +43,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
     var sectionDates: [Date] = []
     var dataSourceTask: URLSessionTask?
 
-    var insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
+    var insets = UIEdgeInsets(top: 8, left: 0, bottom: 0, right: 0)
     var didSelectItemEnable: Bool = true
     var objectType: String?
 
@@ -117,7 +114,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
         tableView.tableHeaderView = commentView
         commentView?.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
         commentView?.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
-        viewContainerConstraint.constant = height
+        viewContainerConstraint.constant = height - 10
     }
 
     func makeTableFooterView() -> UIView {
@@ -130,9 +127,12 @@ class NCActivity: UIViewController, NCSharePagingContent {
         label.textColor = NCBrandColor.shared.textColor2
         label.textAlignment = .center
         label.text = NSLocalizedString("_no_activity_footer_", comment: "")
-        label.frame = CGRect(x: 0, y: 10, width: tableView.frame.width, height: 60)
-
         view.addSubview(label)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        label.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
+        label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
+        label.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
+
         return view
     }
 }
@@ -140,22 +140,16 @@ class NCActivity: UIViewController, NCSharePagingContent {
 // MARK: - Table View
 
 extension NCActivity: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
-        return 120
-    }
-
     func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
-        return 60
+        return 50
     }
 
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
-        return UITableView.automaticDimension
+        return 80
     }
 
     func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
-
-        let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 60))
+        let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 50))
         view.backgroundColor = .clear
 
         let label = UILabel()
@@ -163,20 +157,33 @@ extension NCActivity: UITableViewDelegate {
         label.textColor = NCBrandColor.shared.textColor
         label.text = utility.getTitleFromDate(sectionDates[section])
         label.textAlignment = .center
-        label.layer.cornerRadius = 11
-        label.layer.masksToBounds = true
-        label.layer.backgroundColor = UIColor(red: 152.0 / 255.0, green: 167.0 / 255.0, blue: 181.0 / 255.0, alpha: 0.8).cgColor
-        let widthFrame = label.intrinsicContentSize.width + 30
-        let xFrame = tableView.bounds.width / 2 - widthFrame / 2
-        label.frame = CGRect(x: xFrame, y: 10, width: widthFrame, height: 22)
 
+        let blur = UIBlurEffect(style: .systemMaterial)
+        let blurredEffectView = UIVisualEffectView(effect: blur)
+        blurredEffectView.layer.cornerRadius = 11
+        blurredEffectView.layer.masksToBounds = true
+
+        view.addSubview(blurredEffectView)
         view.addSubview(label)
+
+        blurredEffectView.translatesAutoresizingMaskIntoConstraints = false
+        label.translatesAutoresizingMaskIntoConstraints = false
+
+        NSLayoutConstraint.activate([
+            blurredEffectView.topAnchor.constraint(equalTo: view.topAnchor),
+            blurredEffectView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+            blurredEffectView.widthAnchor.constraint(equalToConstant: label.intrinsicContentSize.width + 30),
+            blurredEffectView.heightAnchor.constraint(equalToConstant: 22),
+            label.topAnchor.constraint(equalTo: view.topAnchor),
+            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+            label.centerYAnchor.constraint(equalTo: blurredEffectView.centerYAnchor)
+        ])
+
         return view
     }
 }
 
 extension NCActivity: UITableViewDataSource {
-
     func numberOfSections(in tableView: UITableView) -> Int {
         return sectionDates.count
     }
@@ -237,27 +244,24 @@ extension NCActivity: UITableViewDataSource {
         guard let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as? NCActivityTableViewCell else {
             return UITableViewCell()
         }
-
         var orderKeysId: [String] = []
 
         cell.idActivity = activity.idActivity
         cell.indexPath = indexPath
         cell.avatar.image = nil
         cell.avatar.isHidden = true
-        cell.subjectTrailingConstraint.constant = 10
         cell.didSelectItemEnable = self.didSelectItemEnable
         cell.subject.textColor = NCBrandColor.shared.textColor
         cell.viewController = self
 
         // icon
         if !activity.icon.isEmpty {
-
             let fileNameIcon = (activity.icon as NSString).lastPathComponent
             let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileNameIcon
 
             if FileManager.default.fileExists(atPath: fileNameLocalPath) {
                 if let image = UIImage(contentsOfFile: fileNameLocalPath) {
-                    cell.icon.image = image
+                    cell.icon.image = image.withTintColor(NCBrandColor.shared.textColor, renderingMode: .alwaysOriginal)
                 }
             } else {
                 NextcloudKit.shared.downloadContent(serverUrl: activity.icon) { _, data, error in
@@ -273,20 +277,18 @@ extension NCActivity: UITableViewDataSource {
 
         // avatar
         if !activity.user.isEmpty && activity.user != appDelegate.userId {
-
-            cell.subjectTrailingConstraint.constant = 50
             cell.avatar.isHidden = false
             cell.fileUser = activity.user
-
             let fileName = appDelegate.userBaseUrl + "-" + activity.user + ".png"
-
             NCNetworking.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView)
+            cell.subjectLeadingConstraint.constant = 15
+        } else {
+            cell.subjectLeadingConstraint.constant = -30
         }
 
         // subject
         cell.subject.text = activity.subject
         if !activity.subjectRich.isEmpty {
-
             var subject = activity.subjectRich
             var keys: [String] = []
 
@@ -317,15 +319,6 @@ extension NCActivity: UITableViewDataSource {
             cell.subject.attributedText = subject.set(style: StyleGroup(base: normal, ["bold": bold, "date": date]))
         }
 
-        // CollectionView
-        cell.activityPreviews = NCManageDatabase.shared.getActivityPreview(account: activity.account, idActivity: activity.idActivity, orderKeysId: orderKeysId)
-        if cell.activityPreviews.isEmpty {
-            cell.collectionViewHeightConstraint.constant = 0
-        } else {
-            cell.collectionViewHeightConstraint.constant = 60
-        }
-        cell.collectionView.reloadData()
-
         return cell
     }
 }
@@ -345,16 +338,15 @@ extension NCActivity: UIScrollViewDelegate {
 // MARK: - NC API & Algorithm
 
 extension NCActivity {
-
     func fetchAll(isInitial: Bool) {
         guard !isFetchingActivity else { return }
         self.isFetchingActivity = true
-
         var bottom: CGFloat = 0
+
         if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
             bottom = -mainTabBar.getHeight()
         }
-        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom - 5, style: .medium)
+        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom - 35, style: .medium)
 
         let dispatchGroup = DispatchGroup()
         loadComments(disptachGroup: dispatchGroup)
@@ -377,8 +369,8 @@ extension NCActivity {
     }
 
     func loadDataSource() {
-
         var newItems = [DateCompareable]()
+
         if showComments, let metadata = metadata, let account = NCManageDatabase.shared.getActiveAccount() {
             let comments = NCManageDatabase.shared.getComments(account: account.account, objectId: metadata.fileId)
             newItems += comments
@@ -450,10 +442,9 @@ extension NCActivity {
 
     func loadActivity(idActivity: Int, limit: Int = 200, disptachGroup: DispatchGroup) {
         guard hasActivityToLoad else { return }
-
         var resultActivityId = 0
-        disptachGroup.enter()
 
+        disptachGroup.enter()
         NextcloudKit.shared.getActivity(
             since: idActivity,
             limit: min(limit, 200),

+ 9 - 8
iOSClient/Activity/NCActivityCommentView.swift

@@ -25,7 +25,6 @@ import UIKit
 
 class NCActivityCommentView: UIView, UITextFieldDelegate {
     @IBOutlet weak var imageItem: UIImageView!
-    @IBOutlet weak var labelUser: UILabel!
     @IBOutlet weak var newCommentField: UITextField!
 
     var completionHandler: ((String?) -> Void)?
@@ -42,13 +41,6 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
         } else {
             imageItem.image = NCUtility().loadImage(named: "person.crop.circle", colors: [NCBrandColor.shared.iconImageColor])
         }
-
-        if account.displayName.isEmpty {
-            labelUser.text = account.user
-        } else {
-            labelUser.text = account.displayName
-        }
-        labelUser.textColor = NCBrandColor.shared.textColor
     }
 
     func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@@ -57,3 +49,12 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
         return true
     }
 }
+
+extension NCActivityCommentView: UISearchBarDelegate {
+    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
+        searchBar.resignFirstResponder()
+        completionHandler?(searchBar.text)
+    }
+
+    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { }
+}

+ 26 - 25
iOSClient/Activity/NCActivityCommentView.xib

@@ -1,60 +1,61 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
         <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GuF-Pi-nHv" customClass="NCActivityCommentView" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="269" height="100"/>
+            <rect key="frame" x="0.0" y="0.0" width="269" height="55"/>
             <subviews>
                 <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YXy-gE-g7y">
-                    <rect key="frame" x="10" y="10" width="40" height="40"/>
+                    <rect key="frame" x="15" y="10" width="35" height="35"/>
                     <constraints>
-                        <constraint firstAttribute="width" constant="40" id="kUz-t2-bFL"/>
-                        <constraint firstAttribute="height" constant="40" id="yRS-7c-bMw"/>
+                        <constraint firstAttribute="width" constant="35" id="kUz-t2-bFL"/>
+                        <constraint firstAttribute="height" constant="35" id="yRS-7c-bMw"/>
                     </constraints>
                 </imageView>
-                <textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="5pv-VB-vbL">
-                    <rect key="frame" x="60" y="60" width="199" height="30"/>
+                <textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Test" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="5pv-VB-vbL">
+                    <rect key="frame" x="65" y="13" width="188" height="30"/>
+                    <color key="backgroundColor" systemColor="systemGray6Color"/>
                     <constraints>
                         <constraint firstAttribute="height" constant="30" id="OLX-lD-EIH"/>
                     </constraints>
-                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
                     <textInputTraits key="textInputTraits"/>
                 </textField>
-                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="user" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Ja-ik-S0n">
-                    <rect key="frame" x="60" y="21.5" width="199" height="17"/>
-                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
-                    <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                    <nil key="highlightedColor"/>
-                </label>
             </subviews>
             <viewLayoutGuide key="safeArea" id="Y5x-Vi-PYA"/>
+            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
             <constraints>
                 <constraint firstItem="YXy-gE-g7y" firstAttribute="top" secondItem="GuF-Pi-nHv" secondAttribute="top" constant="10" id="26g-rF-Ags"/>
-                <constraint firstAttribute="trailing" secondItem="0Ja-ik-S0n" secondAttribute="trailing" constant="10" id="7ue-4o-ZT2"/>
-                <constraint firstAttribute="height" constant="100" id="IsL-V9-dXU"/>
-                <constraint firstItem="0Ja-ik-S0n" firstAttribute="centerY" secondItem="YXy-gE-g7y" secondAttribute="centerY" id="NxM-vu-j06"/>
-                <constraint firstItem="5pv-VB-vbL" firstAttribute="leading" secondItem="YXy-gE-g7y" secondAttribute="trailing" constant="10" id="Oza-Za-mDZ"/>
-                <constraint firstItem="5pv-VB-vbL" firstAttribute="top" secondItem="YXy-gE-g7y" secondAttribute="bottom" constant="10" id="iie-Nv-YUr"/>
-                <constraint firstItem="0Ja-ik-S0n" firstAttribute="leading" secondItem="YXy-gE-g7y" secondAttribute="trailing" constant="10" id="j0L-NP-Z4H"/>
-                <constraint firstAttribute="trailing" secondItem="5pv-VB-vbL" secondAttribute="trailing" constant="10" id="oXJ-ov-XCK"/>
-                <constraint firstItem="YXy-gE-g7y" firstAttribute="leading" secondItem="GuF-Pi-nHv" secondAttribute="leading" constant="10" id="t5p-fd-swt"/>
-                <constraint firstAttribute="bottom" secondItem="5pv-VB-vbL" secondAttribute="bottom" constant="10" id="yEr-QL-mtD"/>
+                <constraint firstAttribute="height" constant="55" id="IsL-V9-dXU"/>
+                <constraint firstItem="5pv-VB-vbL" firstAttribute="leading" secondItem="GuF-Pi-nHv" secondAttribute="leading" constant="65" id="Oza-Za-mDZ"/>
+                <constraint firstItem="5pv-VB-vbL" firstAttribute="top" secondItem="YXy-gE-g7y" secondAttribute="bottom" constant="-32" id="iie-Nv-YUr"/>
+                <constraint firstAttribute="trailing" secondItem="5pv-VB-vbL" secondAttribute="trailing" constant="16" id="oXJ-ov-XCK"/>
+                <constraint firstItem="YXy-gE-g7y" firstAttribute="leading" secondItem="GuF-Pi-nHv" secondAttribute="leading" constant="15" id="t5p-fd-swt"/>
+                <constraint firstAttribute="bottom" secondItem="5pv-VB-vbL" secondAttribute="bottom" constant="12" id="yEr-QL-mtD"/>
             </constraints>
             <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
             <connections>
                 <outlet property="imageItem" destination="YXy-gE-g7y" id="yWc-3P-gIU"/>
-                <outlet property="labelUser" destination="0Ja-ik-S0n" id="GkS-TV-2ic"/>
                 <outlet property="newCommentField" destination="5pv-VB-vbL" id="8vL-Mt-0rZ"/>
             </connections>
             <point key="canvasLocation" x="-231.15942028985509" y="-99.776785714285708"/>
         </view>
     </objects>
+    <resources>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemGray6Color">
+            <color red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
 </document>

+ 5 - 30
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -28,7 +28,6 @@ import JGProgressHUD
 import Queuer
 
 class NCActivityCollectionViewCell: UICollectionViewCell {
-
     @IBOutlet weak var imageView: UIImageView!
 
     var fileId = ""
@@ -40,13 +39,10 @@ class NCActivityCollectionViewCell: UICollectionViewCell {
 }
 
 class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
-
-    @IBOutlet weak var collectionView: UICollectionView!
     @IBOutlet weak var icon: UIImageView!
     @IBOutlet weak var avatar: UIImageView!
     @IBOutlet weak var subject: UILabel!
-    @IBOutlet weak var subjectTrailingConstraint: NSLayoutConstraint!
-    @IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var subjectLeadingConstraint: NSLayoutConstraint!
 
     private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     private var user: String = ""
@@ -73,8 +69,6 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
     override func awakeFromNib() {
         super.awakeFromNib()
 
-        collectionView.delegate = self
-        collectionView.dataSource = self
         let avatarRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapAvatarImage))
         avatar.addGestureRecognizer(avatarRecognizer)
     }
@@ -88,19 +82,16 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
 // MARK: - Collection View
 
 extension NCActivityTableViewCell: UICollectionViewDelegate {
-
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-
         // Select not permitted
         if !didSelectItemEnable {
             return
         }
-
         let activityPreview = activityPreviews[indexPath.row]
 
         if activityPreview.view == "trashbin" {
-
             var responder: UIResponder? = collectionView
+
             while !(responder is UIViewController) {
                 responder = responder?.next
                 if responder == nil {
@@ -119,23 +110,19 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
                     }
                 }
             }
-
             return
         }
 
         if activityPreview.view == NCGlobal.shared.appName && activityPreview.mimeType != "dir" {
-
             guard let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: activityPreview.idActivity, id: String(activityPreview.fileId)) else {
                 return
             }
-
             NCActionCenter.shared.viewerFile(account: appDelegate.account, fileId: activitySubjectRich.id, viewController: viewController)
         }
     }
 }
 
 extension NCActivityTableViewCell: UICollectionViewDataSource {
-
     func numberOfSections(in collectionView: UICollectionView) -> Int {
         return 1
     }
@@ -146,7 +133,6 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
     }
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-
         guard let cell: NCActivityCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as? NCActivityCollectionViewCell else {
             return UICollectionViewCell()
         }
@@ -159,7 +145,6 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
 
         // Trashbin
         if activityPreview.view == "trashbin" {
-
             let source = activityPreview.source
 
             utility.convertSVGtoPNGWriteToUserData(svgUrlString: source, width: 100, rewrite: false, account: appDelegate.account, id: idActivity) { imageNamePath, id in
@@ -169,11 +154,8 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                     cell.imageView.image = NCImageCache.images.file
                 }
             }
-
         } else {
-
             if activityPreview.isMimeTypeIcon {
-
                 let source = activityPreview.source
 
                 utility.convertSVGtoPNGWriteToUserData(svgUrlString: source, width: 150, rewrite: false, account: appDelegate.account, id: idActivity) { imageNamePath, id in
@@ -183,11 +165,8 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                         cell.imageView.image = NCImageCache.images.file
                     }
                 }
-
             } else {
-
                 if let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: idActivity, id: fileId) {
-
                     let fileNamePath = NCUtilityFileSystem().directoryUserData + "/" + activitySubjectRich.name
 
                     if FileManager.default.fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
@@ -206,20 +185,17 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                 }
             }
         }
-
         return cell
     }
-
 }
 
 extension NCActivityTableViewCell: UICollectionViewDelegateFlowLayout {
-
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
-        return CGSize(width: 50, height: 50)
+        return CGSize(width: 50, height: 30)
     }
 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
-        return 20
+        return 0
     }
 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
@@ -228,7 +204,6 @@ extension NCActivityTableViewCell: UICollectionViewDelegateFlowLayout {
 }
 
 class NCOperationDownloadThumbnailActivity: ConcurrentOperation {
-
     var cell: NCActivityCollectionViewCell?
     var collectionView: UICollectionView?
     var fileNamePreviewLocalPath: String
@@ -243,10 +218,10 @@ class NCOperationDownloadThumbnailActivity: ConcurrentOperation {
 
     override func start() {
         guard !isCancelled else { return self.finish() }
+
         NextcloudKit.shared.downloadPreview(fileId: fileId,
                                             fileNamePreviewLocalPath: fileNamePreviewLocalPath,
                                             options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, imagePreview, _, _, _, error in
-
             if error == .success, let imagePreview = imagePreview {
                 DispatchQueue.main.async {
                     if self.fileId == self.cell?.fileId, let imageView = self.cell?.imageView {

+ 34 - 59
iOSClient/AppDelegate.swift

@@ -87,12 +87,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NextcloudKit.shared.nkCommonInstance.pathLog = utilityFileSystem.directoryGroup
 
         if NCBrandOptions.shared.disable_log {
-
             utilityFileSystem.removeFile(atPath: NextcloudKit.shared.nkCommonInstance.filenamePathLog)
             utilityFileSystem.removeFile(atPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NextcloudKit.shared.nkCommonInstance.filenameLog)
-
         } else {
-
             levelLog = NCKeychain().logLevel
             NextcloudKit.shared.nkCommonInstance.levelLog = levelLog
             NextcloudKit.shared.nkCommonInstance.copyLogToDocumentDirectory = true
@@ -101,6 +98,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Account active \(activeAccount.account)")
+
             if NCKeychain().getPassword(account: activeAccount.account).isEmpty {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] PASSWORD NOT FOUND for \(activeAccount.account)")
             }
@@ -111,16 +109,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             userId = activeAccount.userId
             password = NCKeychain().getPassword(account: account)
 
-            NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
+            NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
             NCManageDatabase.shared.setCapabilities(account: account)
 
             NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
             DispatchQueue.global().async {
                 NCImageCache.shared.createMediaCache(account: self.account, withCacheSize: true)
             }
-
         } else {
-
             NCKeychain().removeAll()
             if let bundleID = Bundle.main.bundleIdentifier {
                 UserDefaults.standard.removePersistentDomain(forName: bundleID)
@@ -287,7 +283,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
-        if let pref = UserDefaults(suiteName: NCBrandOptions.shared.capabilitiesGroups),
+        if let pref = UserDefaults(suiteName: NCBrandOptions.shared.capabilitiesGroup),
            let data = pref.object(forKey: "NOTIFICATION_DATA") as? [String: AnyObject] {
             nextcloudPushNotificationAction(data: data)
             pref.set(nil, forKey: "NOTIFICATION_DATA")
@@ -321,8 +317,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 let accounts = NCManageDatabase.shared.getAllAccount()
                 for account in accounts {
                     if account.account == accountPush {
-                        self.changeAccount(account.account, userProfile: nil)
-                        findAccount = true
+                        self.changeAccount(account.account, userProfile: nil) {
+                            findAccount = true
+                        }
                     }
                 }
             }
@@ -363,22 +360,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             }
         }
 
-        // [WEBPersonalized] [AppConfig]
-        if NCBrandOptions.shared.use_AppConfig {
-            if activeLogin?.view.window == nil {
-                urlBase = NCBrandOptions.shared.loginBaseUrl
-                NextcloudKit.shared.getLoginFlowV2(serverUrl: urlBase) { token, endpoint, login, _, error in
-                    // Login Flow V2
-                    if error == .success, let token, let endpoint, let login {
-                        let vc = UIHostingController(rootView: NCLoginPoll(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login, cancelButtonDisabled: NCManageDatabase.shared.getAccounts().isEmptyOrNil))
-                        UIApplication.shared.firstWindow?.rootViewController?.present(vc, animated: true)
-                    }
-                }
-
-                return
-            }
-        }
-
         // Nextcloud standard login
         if selector == NCGlobal.shared.introSignup {
             if activeLogin?.view.window == nil {
@@ -394,7 +375,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 }
             }
 
-        } else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url {
+        } else if NCBrandOptions.shared.disable_request_login_url {
             if activeLogin?.view.window == nil {
                 activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
                 activeLogin?.urlBase = NCBrandOptions.shared.loginBaseUrl
@@ -468,42 +449,45 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - Account
 
-    func createAccount(server: String, username: String, password: String, completion: @escaping (_ error: NKError) -> Void) {
-        var urlBase = server
+    func createAccount(urlBase: String,
+                       user: String,
+                       password: String,
+                       completion: @escaping (_ error: NKError) -> Void) {
+        var urlBase = urlBase
         if urlBase.last == "/" { urlBase = String(urlBase.dropLast()) }
-        let account: String = "\(username) \(urlBase)"
-        let user = username
+        let account: String = "\(user) \(urlBase)"
 
-        NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
+        NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
         NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
-
             if error == .success, let userProfile {
-
                 NCManageDatabase.shared.deleteAccount(account)
                 NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
-
                 NCKeychain().setClientCertificate(account: account, p12Data: NCNetworking.shared.p12Data, p12Password: NCNetworking.shared.p12Password)
-
-                self.changeAccount(account, userProfile: userProfile)
+                self.changeAccount(account, userProfile: userProfile) {
+                    completion(error)
+                }
             } else {
-
+                NextcloudKit.shared.setup(account: self.account, user: self.user, userId: self.userId, password: self.password, urlBase: self.urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
                 let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
                 alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
                 UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true)
+                completion(error)
             }
-
-            completion(error)
         }
     }
 
-    func changeAccount(_ account: String, userProfile: NKUserProfile?) {
+    func changeAccount(_ account: String,
+                       userProfile: NKUserProfile?,
+                       completion: () -> Void) {
+        guard let tableAccount = NCManageDatabase.shared.setAccountActive(account) else {
+            return completion()
+        }
+
         NCNetworking.shared.cancelAllQueue()
         NCNetworking.shared.cancelDataTask()
         NCNetworking.shared.cancelDownloadTasks()
         NCNetworking.shared.cancelUploadTasks()
 
-        guard let tableAccount = NCManageDatabase.shared.setAccountActive(account) else { return }
-
         if account != self.account {
             DispatchQueue.global().async {
                 if NCManageDatabase.shared.getAccounts()?.count == 1 {
@@ -520,7 +504,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         self.userId = tableAccount.userId
         self.password = NCKeychain().getPassword(account: tableAccount.account)
 
-        NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
+        NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
         NCManageDatabase.shared.setCapabilities(account: account)
 
         if let userProfile {
@@ -528,7 +512,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
 
         NCPushNotification.shared.pushNotification()
-
         NCService().startRequestServicesServer()
 
         NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
@@ -536,19 +519,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
 
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser)
+        completion()
     }
 
-    func deleteAccount(_ account: String, wipe: Bool) {
+    func deleteAccount(_ account: String) {
         UIApplication.shared.allSceneSessionDestructionExceptFirst()
 
         if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
             NCPushNotification.shared.unsubscribingNextcloudServerPushNotification(account: account.account, urlBase: account.urlBase, user: account.user, withSubscribing: false)
         }
 
-        NextcloudKit.shared.deleteAppPassword(serverUrl: urlBase, username: userId, password: password) { _, error in
-            print(error)
-        }
-
         let results = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@", account), sorted: "ocId", ascending: false)
         let utilityFileSystem = NCUtilityFileSystem()
         for result in results {
@@ -566,22 +546,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         self.userId = ""
         self.password = ""
 
-        if wipe {
-            let accounts = NCManageDatabase.shared.getAccounts()
-            if accounts?.count ?? 0 > 0 {
-                if let newAccount = accounts?.first {
-                    self.changeAccount(newAccount, userProfile: nil)
-                }
-            } else {
-                openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
-            }
+        /*
+        NextcloudKit.shared.deleteAppPassword(serverUrl: urlBase, username: userId, password: password) { _, error in
+            print(error)
         }
+        */
     }
 
     func deleteAllAccounts() {
         let accounts = NCManageDatabase.shared.getAccounts()
         accounts?.forEach({ account in
-            deleteAccount(account, wipe: true)
+            deleteAccount(account)
         })
     }
 

+ 29 - 33
iOSClient/Data/NCDataSource.swift

@@ -25,13 +25,13 @@ import UIKit
 import NextcloudKit
 
 class NCDataSource: NSObject {
-
     var metadatas: [tableMetadata] = []
     var metadatasForSection: [NCMetadataForSection] = []
-
     var directory: tableDirectory?
-    var groupByField: String = ""
+    var groupBy: String?
+    var layout: String?
 
+    private let utilityFileSystem = NCUtilityFileSystem()
     private var sectionsValue: [String] = []
     private var providers: [NKSearchProvider]?
     private var searchResults: [NKSearchResult]?
@@ -45,18 +45,25 @@ class NCDataSource: NSObject {
         super.init()
     }
 
-    init(metadatas: [tableMetadata], account: String, directory: tableDirectory? = nil, sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, groupByField: String = "name", providers: [NKSearchProvider]? = nil, searchResults: [NKSearchResult]? = nil) {
+    init(metadatas: [tableMetadata],
+         account: String,
+         directory: tableDirectory? = nil,
+         layoutForView: NCDBLayoutForView?,
+         favoriteOnTop: Bool = true,
+         providers: [NKSearchProvider]? = nil,
+         searchResults: [NKSearchResult]? = nil) {
         super.init()
 
         self.metadatas = metadatas.filter({
             !(NCGlobal.shared.includeHiddenFiles.contains($0.fileNameView) || $0.isTransferInForeground)
         })
         self.directory = directory
-        self.sort = sort ?? "none"
-        self.ascending = ascending ?? false
-        self.directoryOnTop = directoryOnTop ?? true
-        self.favoriteOnTop = favoriteOnTop ?? true
-        self.groupByField = groupByField
+        self.sort = layoutForView?.sort ?? "none"
+        self.ascending = layoutForView?.ascending ?? false
+        self.directoryOnTop = layoutForView?.directoryOnTop ?? true
+        self.favoriteOnTop = favoriteOnTop
+        self.groupBy = layoutForView?.groupBy ?? "none"
+        self.layout = layoutForView?.layout
         // unified search
         self.providers = providers
         self.searchResults = searchResults
@@ -67,7 +74,6 @@ class NCDataSource: NSObject {
     // MARK: -
 
     func clearDataSource() {
-
         self.metadatas.removeAll()
         self.metadatasForSection.removeAll()
         self.directory = nil
@@ -77,14 +83,12 @@ class NCDataSource: NSObject {
     }
 
     func clearDirectory() {
-
         self.directory = nil
     }
 
-    func changeGroupByField(_ groupByField: String) {
-
-        self.groupByField = groupByField
-        print("DATASOURCE: set group by filed " + groupByField)
+    func changeGroupByField(_ groupBy: String) {
+        self.groupBy = groupBy
+        print("DATASOURCE: set group by filed " + groupBy)
         self.metadatasForSection.removeAll()
         self.sectionsValue.removeAll()
         print("DATASOURCE: remove  all sections")
@@ -93,7 +97,6 @@ class NCDataSource: NSObject {
     }
 
     func addSection(metadatas: [tableMetadata], searchResult: NKSearchResult?) {
-
         self.metadatas.append(contentsOf: metadatas)
 
         if let searchResult = searchResult {
@@ -104,7 +107,6 @@ class NCDataSource: NSObject {
     }
 
     internal func createSections() {
-
         // get all Section
         for metadata in self.metadatas {
             // skipped livePhoto VIDEO part
@@ -115,8 +117,14 @@ class NCDataSource: NSObject {
             if !self.sectionsValue.contains(section) {
                 self.sectionsValue.append(section)
             }
+            // image Cache
+            if (layout == NCGlobal.shared.layoutPhotoRatio || layout == NCGlobal.shared.layoutPhotoSquare),
+               (metadata.isVideo || metadata.isImage),
+               NCImageCache.shared.getPreviewImageCache(ocId: metadata.ocId, etag: metadata.etag) == nil,
+               let image = UIImage(contentsOfFile: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)) {
+                NCImageCache.shared.addPreviewImageCache(metadata: metadata, image: image)
+            }
         }
-
         // Unified search
         if let providers = self.providers, !providers.isEmpty {
             let sectionsDictionary = ThreadSafeDictionary<String, Int>()
@@ -134,9 +142,7 @@ class NCDataSource: NSObject {
                     self.sectionsValue.append(section.key)
                 }
             }
-
         } else {
-
             // normal
             let directory = NSLocalizedString("directory", comment: "").lowercased().firstUppercased
             self.sectionsValue = self.sectionsValue.sorted {
@@ -162,7 +168,6 @@ class NCDataSource: NSObject {
     }
 
     internal func createMetadataForSection(sectionValue: String) {
-
         var searchResult: NKSearchResult?
         if let providers = self.providers, !providers.isEmpty, let searchResults = self.searchResults {
             searchResult = searchResults.filter({ $0.id == sectionValue}).first
@@ -179,20 +184,17 @@ class NCDataSource: NSObject {
     }
 
     func getMetadataSourceForAllSections() -> [tableMetadata] {
-
         var metadatas: [tableMetadata] = []
 
         for section in metadatasForSection {
             metadatas.append(contentsOf: section.metadatas)
         }
-
         return metadatas
     }
 
     // MARK: -
 
     func appendMetadatasToSection(_ metadatas: [tableMetadata], metadataForSection: NCMetadataForSection, lastSearchResult: NKSearchResult) {
-
         guard let sectionIndex = getSectionIndex(metadataForSection.sectionValue) else { return }
         var indexPaths: [IndexPath] = []
 
@@ -241,7 +243,6 @@ class NCDataSource: NSObject {
     }
 
     func getFooterInformationAllMetadatas() -> (directories: Int, files: Int, size: Int64) {
-
         var directories: Int = 0
         var files: Int = 0
         var size: Int64 = 0
@@ -251,7 +252,6 @@ class NCDataSource: NSObject {
             files += metadataForSection.numFile
             size += metadataForSection.totalSize
         }
-
         return (directories, files, size)
     }
 
@@ -263,14 +263,13 @@ class NCDataSource: NSObject {
     }
 
     internal func getSectionValue(metadata: tableMetadata) -> String {
-
-        switch self.groupByField {
-        case "name":
+        switch self.groupBy {
+        case "name", "none":
             return NSLocalizedString(metadata.name, comment: "")
         case "classFile":
             return NSLocalizedString(metadata.classFile, comment: "").lowercased().firstUppercased
         default:
-            return NSLocalizedString(metadata.classFile, comment: "")
+            return NSLocalizedString(metadata.name, comment: "")
         }
     }
 
@@ -300,7 +299,6 @@ class NCDataSource: NSObject {
 // MARK: -
 
 class NCMetadataForSection: NSObject {
-
     var sectionValue: String
     var metadatas: [tableMetadata]
     var lastSearchResult: NKSearchResult?
@@ -337,7 +335,6 @@ class NCMetadataForSection: NSObject {
     }
 
     func createMetadatas() {
-
         // Clear
         //
         metadatasSorted.removeAll()
@@ -357,7 +354,6 @@ class NCMetadataForSection: NSObject {
         //
         if sort != "none" && !sort.isEmpty {
             metadatasSorted = metadatas.sorted {
-
                 switch sort {
                 case "date":
                     if ascending {

+ 21 - 57
iOSClient/Data/NCManageDatabase+Account.swift

@@ -80,22 +80,16 @@ class tableAccount: Object, NCUserBaseUrl {
 }
 
 extension NCManageDatabase {
-
     func addAccount(_ account: String, urlBase: String, user: String, userId: String, password: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
                 let addObject = tableAccount()
-
                 addObject.account = account
-
                 NCKeychain().setPassword(account: account, password: password)
-
                 addObject.urlBase = urlBase
                 addObject.user = user
                 addObject.userId = userId
-
                 realm.add(addObject, update: .all)
             }
         } catch let error {
@@ -103,8 +97,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func updateAccount(_ account: tableAccount) {
-
+    func updateAccount(_ account: tableAccount) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -115,8 +108,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func deleteAccount(_ account: String) {
-
+    func deleteAccount(_ account: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -128,8 +120,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func getActiveAccount() -> tableAccount? {
-
+    func getActiveAccount() -> tableAccount? {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return nil }
@@ -137,12 +128,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
-    @objc func getAccounts() -> [String]? {
-
+    func getAccounts() -> [String]? {
         do {
             let realm = try Realm()
             let results = realm.objects(tableAccount.self).sorted(byKeyPath: "account", ascending: true)
@@ -152,12 +141,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
-    @objc func getAccount(predicate: NSPredicate) -> tableAccount? {
-
+    func getAccount(predicate: NSPredicate) -> tableAccount? {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter(predicate).first else { return nil }
@@ -165,12 +152,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
-    @objc func getAllAccount() -> [tableAccount] {
-
+    func getAllAccount() -> [tableAccount] {
         do {
             let realm = try Realm()
             let sorted = [SortDescriptor(keyPath: "active", ascending: false), SortDescriptor(keyPath: "user", ascending: true)]
@@ -179,12 +164,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
-    @objc func getAllAccountOrderAlias() -> [tableAccount] {
-
+    func getAllAccountOrderAlias() -> [tableAccount] {
         do {
             let realm = try Realm()
             let sorted = [SortDescriptor(keyPath: "active", ascending: false), SortDescriptor(keyPath: "alias", ascending: true), SortDescriptor(keyPath: "user", ascending: true)]
@@ -193,12 +176,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
-    @objc func getAccountAutoUploadFileName() -> String {
-
+    func getAccountAutoUploadFileName() -> String {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return "" }
@@ -210,12 +191,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return ""
     }
 
-    @objc func getAccountAutoUploadDirectory(urlBase: String, userId: String, account: String) -> String {
-
+    func getAccountAutoUploadDirectory(urlBase: String, userId: String, account: String) -> String {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return "" }
@@ -232,21 +211,17 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return ""
     }
 
-    @objc func getAccountAutoUploadPath(urlBase: String, userId: String, account: String) -> String {
-
+    func getAccountAutoUploadPath(urlBase: String, userId: String, account: String) -> String {
         let cameraFileName = self.getAccountAutoUploadFileName()
         let cameraDirectory = self.getAccountAutoUploadDirectory(urlBase: urlBase, userId: userId, account: account)
         let folderPhotos = utilityFileSystem.stringAppendServerUrl(cameraDirectory, addFileName: cameraFileName)
-
         return folderPhotos
     }
 
-    @objc func getAccountAutoUploadSubfolderGranularity() -> Int {
-
+    func getAccountAutoUploadSubfolderGranularity() -> Int {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return NCGlobal.shared.subfolderGranularityMonthly }
@@ -254,12 +229,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return NCGlobal.shared.subfolderGranularityMonthly
     }
 
     func setAccountActive(_ account: String) -> tableAccount? {
-
         var accountReturn = tableAccount()
 
         do {
@@ -279,12 +252,10 @@ extension NCManageDatabase {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
             return nil
         }
-
         return tableAccount.init(value: accountReturn)
     }
 
-    @objc func removePasswordAccount(_ account: String) {
-
+    func removePasswordAccount(_ account: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -297,8 +268,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountAutoUploadProperty(_ property: String, state: Bool) {
-
+    func setAccountAutoUploadProperty(_ property: String, state: Bool) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -313,8 +283,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountAutoUploadGranularity(_ property: String, state: Int) {
-
+    func setAccountAutoUploadGranularity(_ property: String, state: Int) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -327,8 +296,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountAutoUploadFileName(_ fileName: String) {
-
+    func setAccountAutoUploadFileName(_ fileName: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -341,8 +309,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountAutoUploadDirectory(_ serverUrl: String?, urlBase: String, userId: String, account: String) {
-
+    func setAccountAutoUploadDirectory(_ serverUrl: String?, urlBase: String, userId: String, account: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -359,8 +326,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountUserProfile(account: String, userProfile: NKUserProfile) {
-
+    func setAccountUserProfile(account: String, userProfile: NKUserProfile) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -395,8 +361,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountMediaPath(_ path: String, account: String) {
-
+    func setAccountMediaPath(_ path: String, account: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -409,13 +374,12 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountUserStatus(userStatusClearAt: NSDate?, userStatusIcon: String?, userStatusMessage: String?, userStatusMessageId: String?, userStatusMessageIsPredefined: Bool, userStatusStatus: String?, userStatusStatusIsUserDefined: Bool, account: String) {
-
+    func setAccountUserStatus(userStatusClearAt: Date?, userStatusIcon: String?, userStatusMessage: String?, userStatusMessageId: String?, userStatusMessageIsPredefined: Bool, userStatusStatus: String?, userStatusStatusIsUserDefined: Bool, account: String) {
         do {
             let realm = try Realm()
             try realm.write {
                 if let result = realm.objects(tableAccount.self).filter("account == %@", account).first {
-                    result.userStatusClearAt = userStatusClearAt
+                    result.userStatusClearAt = userStatusClearAt as? NSDate
                     result.userStatusIcon = userStatusIcon
                     result.userStatusMessage = userStatusMessage
                     result.userStatusMessageId = userStatusMessageId
@@ -429,7 +393,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountAlias(_ account: String, alias: String) {
+    func setAccountAlias(_ account: String, alias: String) {
         let alias = alias.trimmingCharacters(in: .whitespacesAndNewlines)
 
         do {

+ 1 - 19
iOSClient/Data/NCManageDatabase+Activity.swift

@@ -56,7 +56,6 @@ class tableActivity: Object, DateCompareable {
 }
 
 class tableActivityLatestId: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var activityFirstKnown: Int = 0
     @objc dynamic var activityLastGiven: Int = 0
@@ -67,7 +66,6 @@ class tableActivityLatestId: Object {
 }
 
 class tableActivityPreview: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var filename = ""
     @objc dynamic var idPrimaryKey = ""
@@ -85,7 +83,6 @@ class tableActivityPreview: Object {
 }
 
 class tableActivitySubjectRich: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var idActivity: Int = 0
     @objc dynamic var idPrimaryKey = ""
@@ -102,21 +99,17 @@ class tableActivitySubjectRich: Object {
 }
 
 extension NCManageDatabase {
-
     func addActivity(_ activities: [NKActivity], account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
-
                 for activity in activities {
-
                     let addObjectActivity = tableActivity()
 
                     addObjectActivity.account = account
                     addObjectActivity.idActivity = activity.idActivity
                     addObjectActivity.idPrimaryKey = account + String(activity.idActivity)
-                    addObjectActivity.date = activity.date
+                    addObjectActivity.date = activity.date as NSDate
                     addObjectActivity.app = activity.app
                     addObjectActivity.type = activity.type
                     addObjectActivity.user = activity.user
@@ -193,7 +186,6 @@ extension NCManageDatabase {
     }
 
     func getActivity(predicate: NSPredicate, filterFileId: String?) -> (all: [tableActivity], filter: [tableActivity]) {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -208,12 +200,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return([], [])
     }
 
     func getActivitySubjectRich(account: String, idActivity: Int, key: String) -> tableActivitySubjectRich? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -222,12 +212,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getActivitySubjectRich(account: String, idActivity: Int, id: String) -> tableActivitySubjectRich? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -244,12 +232,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getActivityPreview(account: String, idActivity: Int, orderKeysId: [String]) -> [tableActivityPreview] {
-
         var results: [tableActivityPreview] = []
 
         do {
@@ -264,12 +250,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
    func updateLatestActivityId(activityFirstKnown: Int, activityLastGiven: Int, account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -285,7 +269,6 @@ extension NCManageDatabase {
     }
 
     func getLatestActivityId(account: String) -> tableActivityLatestId? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -293,7 +276,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 }

+ 0 - 2
iOSClient/Data/NCManageDatabase+Avatar.swift

@@ -63,7 +63,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -102,7 +101,6 @@ extension NCManageDatabase {
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-
         return image
     }
 

+ 1 - 7
iOSClient/Data/NCManageDatabase+Capabilities.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableCapabilities: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var jsondata: Data?
 
@@ -36,9 +35,7 @@ class tableCapabilities: Object {
 }
 
 extension NCManageDatabase {
-
     func addCapabilitiesJSon(_ data: Data, account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -53,7 +50,6 @@ extension NCManageDatabase {
     }
 
     func getCapabilities(account: String) -> Data? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -67,11 +63,9 @@ extension NCManageDatabase {
     }
 
     func setCapabilities(account: String, data: Data? = nil) {
-
         let jsonData: Data?
 
         struct CapabilityNextcloud: Codable {
-
             struct Ocs: Codable {
                 let meta: Meta
                 let data: Data
@@ -360,7 +354,7 @@ extension NCManageDatabase {
             global.capabilityFilesComments = data.capabilities.files?.comments ?? false
             global.capabilityFilesBigfilechunking = data.capabilities.files?.bigfilechunking ?? false
 
-            global.capabilityUserStatusEnabled = data.capabilities.files?.undelete ?? false
+            global.capabilityUserStatusEnabled = data.capabilities.userstatus?.enabled ?? false
             if data.capabilities.external != nil {
                 global.capabilityExternalSites = true
             }

+ 0 - 10
iOSClient/Data/NCManageDatabase+Chunk.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableChunk: Object {
-
     @Persisted var account = ""
     @Persisted var chunkFolder = ""
     @Persisted(primaryKey: true) var index = ""
@@ -36,9 +35,7 @@ class tableChunk: Object {
 }
 
 extension NCManageDatabase {
-
     func getChunkFolder(account: String, ocId: String) -> String {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -47,12 +44,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return NSUUID().uuidString
     }
 
     func getChunks(account: String, ocId: String) -> [(fileName: String, size: Int64)] {
-
         var filesChunk: [(fileName: String, size: Int64)] = []
 
         do {
@@ -65,12 +60,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return filesChunk
     }
 
     func addChunks(account: String, ocId: String, chunkFolder: String, filesChunk: [(fileName: String, size: Int64)]) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -93,7 +86,6 @@ extension NCManageDatabase {
     }
 
     func deleteChunk(account: String, ocId: String, fileChunk: (fileName: String, size: Int64), directory: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -108,7 +100,6 @@ extension NCManageDatabase {
     }
 
     func deleteChunks(account: String, ocId: String, directory: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -123,5 +114,4 @@ extension NCManageDatabase {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
     }
-
 }

+ 0 - 4
iOSClient/Data/NCManageDatabase+Comments.swift

@@ -47,9 +47,7 @@ class tableComments: Object, DateCompareable {
 }
 
 extension NCManageDatabase {
-
     func addComments(_ comments: [NKComments], account: String, objectId: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -78,7 +76,6 @@ extension NCManageDatabase {
     }
 
     func getComments(account: String, objectId: String) -> [tableComments] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -87,7 +84,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 }

+ 0 - 10
iOSClient/Data/NCManageDatabase+DashboardWidget.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableDashboardWidget: Object {
-
     @Persisted(primaryKey: true) var index = ""
     @Persisted var account = ""
     @Persisted var id = ""
@@ -39,7 +38,6 @@ class tableDashboardWidget: Object {
 }
 
 class tableDashboardWidgetButton: Object {
-
     @Persisted(primaryKey: true) var index = ""
     @Persisted var account = ""
     @Persisted var id = ""
@@ -49,9 +47,7 @@ class tableDashboardWidgetButton: Object {
 }
 
 extension NCManageDatabase {
-
     func getDashboardWidget(account: String, id: String) -> (tableDashboardWidget?, [tableDashboardWidgetButton]?) {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -61,12 +57,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return (nil, nil)
     }
 
     func getDashboardWidgetApplications(account: String) -> [tableDashboardWidget] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -76,19 +70,15 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func addDashboardWidget(account: String, dashboardWidgets: [NCCDashboardWidget]) {
-
         do {
             let realm = try Realm()
             try realm.write {
-
                 let resultDashboard = realm.objects(tableDashboardWidget.self).filter("account == %@", account)
                 realm.delete(resultDashboard)
-
                 let resultDashboardButton = realm.objects(tableDashboardWidgetButton.self).filter("account == %@", account)
                 realm.delete(resultDashboardButton)
 

+ 0 - 16
iOSClient/Data/NCManageDatabase+DirectEditing.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableDirectEditingCreators: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var editor = ""
     @objc dynamic var ext = ""
@@ -37,7 +36,6 @@ class tableDirectEditingCreators: Object {
 }
 
 class tableDirectEditingEditors: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var editor = ""
     let mimetypes = List<String>()
@@ -47,21 +45,16 @@ class tableDirectEditingEditors: Object {
 }
 
 extension NCManageDatabase {
-
     func addDirectEditing(account: String, editors: [NKEditorDetailsEditors], creators: [NKEditorDetailsCreators]) {
-
         do {
             let realm = try Realm()
             try realm.write {
-
                 let resultsCreators = realm.objects(tableDirectEditingCreators.self).filter("account == %@", account)
                 realm.delete(resultsCreators)
-
                 let resultsEditors = realm.objects(tableDirectEditingEditors.self).filter("account == %@", account)
                 realm.delete(resultsEditors)
 
                 for creator in creators {
-
                     let addObject = tableDirectEditingCreators()
 
                     addObject.account = account
@@ -71,12 +64,10 @@ extension NCManageDatabase {
                     addObject.mimetype = creator.mimetype
                     addObject.name = creator.name
                     addObject.templates = creator.templates
-
                     realm.add(addObject)
                 }
 
                 for editor in editors {
-
                     let addObject = tableDirectEditingEditors()
 
                     addObject.account = account
@@ -93,7 +84,6 @@ extension NCManageDatabase {
                         addObject.optionalMimetypes.append(mimeType)
                     }
                     addObject.secure = editor.secure
-
                     realm.add(addObject)
                 }
             }
@@ -103,7 +93,6 @@ extension NCManageDatabase {
     }
 
     func getDirectEditingCreators(account: String) -> [tableDirectEditingCreators]? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -116,12 +105,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
 
     func getDirectEditingCreators(predicate: NSPredicate) -> [tableDirectEditingCreators]? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -134,12 +121,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
 
     func getDirectEditingEditors(account: String) -> [tableDirectEditingEditors]? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -152,7 +137,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
 }

+ 1 - 5
iOSClient/Data/NCManageDatabase+Directory.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableDirectory: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var colorFolder: String?
     @objc dynamic var e2eEncrypted: Bool = false
@@ -46,7 +45,6 @@ class tableDirectory: Object {
 }
 
 extension NCManageDatabase {
-
     func addDirectory(e2eEncrypted: Bool, favorite: Bool, ocId: String, fileId: String, etag: String? = nil, permissions: String? = nil, richWorkspace: String? = nil, serverUrl: String, account: String) {
         do {
             let realm = try Realm()
@@ -88,7 +86,6 @@ extension NCManageDatabase {
     }
 
     func deleteDirectoryAndSubDirectory(serverUrl: String, account: String) {
-
 #if !EXTENSION
         DispatchQueue.main.async {
             let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
@@ -208,7 +205,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -235,7 +231,7 @@ extension NCManageDatabase {
                     directory.serverUrl = serverUrl
                     directory.offline = offline
                     addDirectory(directory: directory, metadata: metadata)
-                    realm.add(directory)
+                    realm.add(directory, update: .all)
                 }
             }
         } catch let error {

+ 0 - 36
iOSClient/Data/NCManageDatabase+E2EE.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableE2eEncryptionLock: Object {
-
     @Persisted(primaryKey: true) var fileId = ""
     @Persisted var account = ""
     @Persisted var date = Date()
@@ -36,7 +35,6 @@ class tableE2eEncryptionLock: Object {
 
 typealias tableE2eEncryption = tableE2eEncryptionV4
 class tableE2eEncryptionV4: Object {
-
     @Persisted(primaryKey: true) var primaryKey = ""
     @Persisted var account = ""
     @Persisted var authenticationTag: String = ""
@@ -65,7 +63,6 @@ class tableE2eEncryptionV4: Object {
 // MARK: Table V1, V1.2
 
 class tableE2eMetadata12: Object {
-
     @Persisted(primaryKey: true) var serverUrl = ""
     @Persisted var account = ""
     @Persisted var metadataKey = ""
@@ -77,7 +74,6 @@ class tableE2eMetadata12: Object {
 
 typealias tableE2eMetadata = tableE2eMetadataV2
 class tableE2eMetadataV2: Object {
-
     @Persisted(primaryKey: true) var primaryKey = ""
     @Persisted var account = ""
     @Persisted var deleted: Bool = false
@@ -96,7 +92,6 @@ class tableE2eMetadataV2: Object {
 }
 
 class tableE2eCounter: Object {
-
     @Persisted(primaryKey: true) var primaryKey: String
     @Persisted var account: String
     @Persisted var counter: Int
@@ -112,7 +107,6 @@ class tableE2eCounter: Object {
 }
 
 class tableE2eUsers: Object {
-
     @Persisted(primaryKey: true) var primaryKey = ""
     @Persisted var account = ""
     @Persisted var certificate = ""
@@ -132,12 +126,9 @@ class tableE2eUsers: Object {
 }
 
 extension NCManageDatabase {
-
     // MARK: -
     // MARK: tableE2eEncryption
-
     func addE2eEncryption(_ object: tableE2eEncryption) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -149,7 +140,6 @@ extension NCManageDatabase {
     }
 
     func deleteE2eEncryption(predicate: NSPredicate) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -162,7 +152,6 @@ extension NCManageDatabase {
     }
 
     func getE2eEncryption(predicate: NSPredicate) -> tableE2eEncryption? {
-
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableE2eEncryption.self).filter(predicate).sorted(byKeyPath: "metadataKeyIndex", ascending: false).first else { return nil }
@@ -170,12 +159,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getE2eEncryptions(predicate: NSPredicate) -> [tableE2eEncryption] {
-
         do {
             let realm = try Realm()
             let results: Results<tableE2eEncryption>
@@ -184,12 +171,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func renameFileE2eEncryption(account: String, serverUrl: String, fileNameIdentifier: String, newFileName: String, newFileNamePath: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -206,7 +191,6 @@ extension NCManageDatabase {
     // MARK: Table e2e Encryption Lock
 
     func getE2ETokenLock(account: String, serverUrl: String) -> tableE2eEncryptionLock? {
-
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableE2eEncryptionLock.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
@@ -214,12 +198,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getE2EAllTokenLock(account: String) -> [tableE2eEncryptionLock] {
-
         do {
             let realm = try Realm()
             let results = realm.objects(tableE2eEncryptionLock.self).filter("account == %@", account)
@@ -231,12 +213,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func setE2ETokenLock(account: String, serverUrl: String, fileId: String, e2eToken: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -253,7 +233,6 @@ extension NCManageDatabase {
     }
 
     func deleteE2ETokenLock(account: String, serverUrl: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -270,7 +249,6 @@ extension NCManageDatabase {
     // MARK: V1
 
     func getE2eMetadata(account: String, serverUrl: String) -> tableE2eMetadata12? {
-
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableE2eMetadata12.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
@@ -278,12 +256,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func setE2eMetadata(account: String, serverUrl: String, metadataKey: String, version: Double) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -309,7 +285,6 @@ extension NCManageDatabase {
                      certificate: String,
                      encryptedMetadataKey: String?,
                      metadataKey: Data?) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -326,7 +301,6 @@ extension NCManageDatabase {
     }
 
     func deleteE2EUsers(account: String, ocIdServerUrl: String, userId: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -340,43 +314,36 @@ extension NCManageDatabase {
     }
 
     func getE2EUsers(account: String, ocIdServerUrl: String) -> Results<tableE2eUsers>? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableE2eUsers.self).filter("account == %@ AND ocIdServerUrl == %@", account, ocIdServerUrl)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getE2EUser(account: String, ocIdServerUrl: String, userId: String) -> tableE2eUsers? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableE2eUsers.self).filter("account == %@ && ocIdServerUrl == %@ AND userId == %@", account, ocIdServerUrl, userId).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getE2eMetadata(account: String, ocIdServerUrl: String) -> tableE2eMetadata? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableE2eMetadata.self).filter("account == %@ && ocIdServerUrl == %@", account, ocIdServerUrl).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func addE2eMetadata(account: String, serverUrl: String, ocIdServerUrl: String, keyChecksums: [String]?, deleted: Bool, folders: [String: String]?, version: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -401,7 +368,6 @@ extension NCManageDatabase {
     }
 
     func updateCounterE2eMetadata(account: String, ocIdServerUrl: String, counter: Int) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -414,14 +380,12 @@ extension NCManageDatabase {
     }
 
     func getCounterE2eMetadata(account: String, ocIdServerUrl: String) -> Int? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableE2eCounter.self).filter("account == %@ && ocIdServerUrl == %@", account, ocIdServerUrl).first?.counter
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 }

+ 0 - 6
iOSClient/Data/NCManageDatabase+ExternalSites.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableExternalSites: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var icon = ""
     @objc dynamic var idExternalSite: Int = 0
@@ -37,9 +36,7 @@ class tableExternalSites: Object {
 }
 
 extension NCManageDatabase {
-
     func addExternalSites(_ externalSite: NKExternalSite, account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -61,7 +58,6 @@ extension NCManageDatabase {
     }
 
     func deleteExternalSites(account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -74,7 +70,6 @@ extension NCManageDatabase {
     }
 
     func getAllExternalSites(account: String) -> [tableExternalSites]? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -87,7 +82,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
 }

+ 2 - 5
iOSClient/Data/NCManageDatabase+GPS.swift

@@ -33,9 +33,7 @@ class tableGPSV2: Object {
 }
 
 extension NCManageDatabase {
-
-    @objc func addGeocoderLocation(_ location: String, latitude: Double, longitude: Double) {
-
+    func addGeocoderLocation(_ location: String, latitude: Double, longitude: Double) {
         do {
             let realm = try Realm()
             realm.refresh()
@@ -52,7 +50,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func getLocationFromLatAndLong(latitude: Double, longitude: Double) -> String? {
+    func getLocationFromLatAndLong(latitude: Double, longitude: Double) -> String? {
         do {
             let realm = try Realm()
             realm.refresh()
@@ -61,7 +59,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
 }

+ 0 - 4
iOSClient/Data/NCManageDatabase+Groupfolders.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class TableGroupfolders: Object {
-
     @Persisted var account = ""
     @Persisted var acl: Bool = false
     @Persisted var groups: List<TableGroupfoldersGroups>
@@ -38,7 +37,6 @@ class TableGroupfolders: Object {
 }
 
 class TableGroupfoldersGroups: Object {
-
     @Persisted var account = ""
     @Persisted var group = ""
     @Persisted var permission: Int = 0
@@ -53,9 +51,7 @@ class TableGroupfoldersGroups: Object {
 }
 
 extension NCManageDatabase {
-
     func addGroupfolders(account: String, groupfolders: [NKGroupfolders]) {
-
         do {
             let realm = try Realm()
             try realm.write {

+ 25 - 18
iOSClient/Data/NCManageDatabase+LayoutForView.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class NCDBLayoutForView: Object {
-
     @Persisted(primaryKey: true) var index = ""
     @Persisted var account = ""
     @Persisted var keyStore = ""
@@ -36,14 +35,23 @@ class NCDBLayoutForView: Object {
     @Persisted var groupBy: String = "none"
     @Persisted var directoryOnTop: Bool = true
     @Persisted var titleButtonHeader: String = "_sorted_by_name_a_z_"
-    @Persisted var itemForLine: Int = 3
+    @Persisted var columnGrid: Int = 3
+    @Persisted var columnPhoto: Int = 3
 }
 
 extension NCManageDatabase {
-
     @discardableResult
-    func setLayoutForView(account: String, key: String, serverUrl: String, layout: String? = nil, sort: String? = nil, ascending: Bool? = nil, groupBy: String? = nil, directoryOnTop: Bool? = nil, titleButtonHeader: String? = nil, itemForLine: Int? = nil) -> NCDBLayoutForView? {
-
+    func setLayoutForView(account: String,
+                          key: String,
+                          serverUrl: String,
+                          layout: String? = nil,
+                          sort: String? = nil,
+                          ascending: Bool? = nil,
+                          groupBy: String? = nil,
+                          directoryOnTop: Bool? = nil,
+                          titleButtonHeader: String? = nil,
+                          columnGrid: Int? = nil,
+                          columnPhoto: Int? = nil) -> NCDBLayoutForView? {
         var keyStore = key
         if !serverUrl.isEmpty { keyStore = serverUrl}
         let index = account + " " + keyStore
@@ -59,42 +67,43 @@ extension NCManageDatabase {
                 }
                 addObject.account = account
                 addObject.keyStore = keyStore
-                if let layout = layout {
+                if let layout {
                     addObject.layout = layout
                 }
-                if let sort = sort {
+                if let sort {
                     addObject.sort = sort
                 }
-                if let sort = sort {
+                if let sort {
                     addObject.sort = sort
                 }
-                if let ascending = ascending {
+                if let ascending {
                     addObject.ascending = ascending
                 }
-                if let groupBy = groupBy {
+                if let groupBy {
                     addObject.groupBy = groupBy
                 }
-                if let directoryOnTop = directoryOnTop {
+                if let directoryOnTop {
                     addObject.directoryOnTop = directoryOnTop
                 }
-                if let titleButtonHeader = titleButtonHeader {
+                if let titleButtonHeader {
                     addObject.titleButtonHeader = titleButtonHeader
                 }
-                if let itemForLine = itemForLine {
-                    addObject.itemForLine = itemForLine
+                if let columnGrid {
+                    addObject.columnGrid = columnGrid
+                }
+                if let columnPhoto {
+                    addObject.columnPhoto = columnPhoto
                 }
                 realm.add(addObject, update: .all)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-
         return NCDBLayoutForView(value: addObject)
     }
 
     @discardableResult
     func setLayoutForView(layoutForView: NCDBLayoutForView) -> NCDBLayoutForView? {
-
         let result = NCDBLayoutForView(value: layoutForView)
 
         do {
@@ -110,7 +119,6 @@ extension NCManageDatabase {
     }
 
     func getLayoutForView(account: String, key: String, serverUrl: String) -> NCDBLayoutForView? {
-
         var keyStore = key
         if !serverUrl.isEmpty { keyStore = serverUrl}
         let index = account + " " + keyStore
@@ -126,7 +134,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return setLayoutForView(account: account, key: key, serverUrl: serverUrl)
     }
 }

+ 1 - 21
iOSClient/Data/NCManageDatabase+LocalFile.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableLocalFile: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var etag = ""
     @objc dynamic var exifDate: NSDate?
@@ -43,13 +42,11 @@ class tableLocalFile: Object {
         return "ocId"
     }
 }
-extension NCManageDatabase {
 
+extension NCManageDatabase {
     // MARK: -
     // MARK: Table LocalFile - return RESULT
-
     func getTableLocalFile(ocId: String) -> tableLocalFile? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first
@@ -63,7 +60,6 @@ extension NCManageDatabase {
     // MARK: Table LocalFile
 
     func addLocalFile(metadata: tableMetadata, offline: Bool? = nil) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -86,7 +82,6 @@ extension NCManageDatabase {
     }
 
     func addLocalFile(account: String, etag: String, ocId: String, fileName: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -106,7 +101,6 @@ extension NCManageDatabase {
     }
 
     func deleteLocalFile(predicate: NSPredicate) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -119,7 +113,6 @@ extension NCManageDatabase {
     }
 
     func setLocalFile(ocId: String, fileName: String?) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -134,7 +127,6 @@ extension NCManageDatabase {
     }
 
     @objc func setLocalFile(ocId: String, exifDate: NSDate?, exifLatitude: String, exifLongitude: String, exifLensModel: String?) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -153,7 +145,6 @@ extension NCManageDatabase {
     }
 
     func setOffLocalFile(ocId: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -166,7 +157,6 @@ extension NCManageDatabase {
     }
 
     func getTableLocalFile(account: String) -> [tableLocalFile] {
-
         do {
             let realm = try Realm()
             let results = realm.objects(tableLocalFile.self).filter("account == %@", account)
@@ -174,12 +164,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func getTableLocalFile(predicate: NSPredicate) -> tableLocalFile? {
-
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableLocalFile.self).filter(predicate).first else { return nil }
@@ -187,24 +175,20 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getResultsTableLocalFile(predicate: NSPredicate) -> Results<tableLocalFile>? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableLocalFile.self).filter(predicate)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getTableLocalFiles(predicate: NSPredicate, sorted: String, ascending: Bool) -> [tableLocalFile] {
-
         do {
             let realm = try Realm()
             let results = realm.objects(tableLocalFile.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
@@ -212,24 +196,20 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func getResultsTableLocalFile(predicate: NSPredicate, sorted: String, ascending: Bool) -> Results<tableLocalFile>? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableLocalFile.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func setLastOpeningDate(metadata: tableMetadata) {
-
         do {
             let realm = try Realm()
             try realm.write {

+ 1 - 2
iOSClient/Data/NCManageDatabase+Metadata+Session.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 extension NCManageDatabase {
-
     func setMetadataSession(ocId: String,
                             newFileName: String? = nil,
                             session: String? = nil,
@@ -80,7 +79,6 @@ extension NCManageDatabase {
     func setMetadataSession(ocId: String,
                             status: Int? = nil,
                             taskIdentifier: Int? = nil) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -160,6 +158,7 @@ extension NCManageDatabase {
     @discardableResult
     func setMetadataStatus(ocId: String, status: Int) -> tableMetadata? {
         var result: tableMetadata?
+
         do {
             let realm = try Realm()
             try realm.write {

+ 69 - 63
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -42,6 +42,11 @@ class tableMetadata: Object, NCUserBaseUrl {
            self.favorite == object.favorite,
            self.livePhotoFile == object.livePhotoFile,
            self.sharePermissionsCollaborationServices == object.sharePermissionsCollaborationServices,
+           self.height == object.height,
+           self.width == object.width,
+           self.latitude == object.latitude,
+           self.longitude == object.longitude,
+           self.altitude == object.altitude,
            Array(self.tags).elementsEqual(Array(object.tags)),
            Array(self.shareType).elementsEqual(Array(object.shareType)),
            Array(self.sharePermissionsCloudMesh).elementsEqual(Array(object.sharePermissionsCloudMesh)) {
@@ -82,6 +87,7 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var name = "" // for unifiedSearch is the provider.id
     @objc dynamic var note = ""
     @objc dynamic var ocId = ""
+    @objc dynamic var ocIdTemp = ""
     @objc dynamic var ownerId = ""
     @objc dynamic var ownerDisplayName = ""
     @objc public var lock = false
@@ -132,7 +138,6 @@ class tableMetadata: Object, NCUserBaseUrl {
 }
 
 extension tableMetadata {
-
     var fileExtension: String {
         (fileNameView as NSString).pathExtension
     }
@@ -227,16 +232,6 @@ extension tableMetadata {
         return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCKeychain().isEndToEndEnabled(account: account)
     }
 
-    var canOpenExternalEditor: Bool {
-        if isDocumentViewableOnly {
-            return false
-        }
-        let utility = NCUtility()
-        let editors = utility.isDirectEditing(account: account, contentType: contentType)
-        let isRichDocument = utility.isRichDocument(self)
-        return classFile == NKCommon.TypeClassFile.document.rawValue && editors.contains(NCGlobal.shared.editorText) && ((editors.contains(NCGlobal.shared.editorOnlyoffice) || isRichDocument))
-    }
-
     var isWaitingTransfer: Bool {
         status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploadError
     }
@@ -281,6 +276,41 @@ extension tableMetadata {
         !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStoragePreviewIconExists(ocId, etag: etag)
     }
 
+    var isAvailableEditorView: Bool {
+        guard (classFile == NKCommon.TypeClassFile.document.rawValue) && NextcloudKit.shared.isNetworkReachable() else { return false }
+        let utility = NCUtility()
+        let directEditingEditors = utility.editorsDirectEditing(account: account, contentType: contentType)
+        let richDocumentEditor = utility.isTypeFileRichDocument(self)
+
+        if NCGlobal.shared.capabilityRichDocumentsEnabled && richDocumentEditor && directEditingEditors.isEmpty {
+            // RichDocument: Collabora
+            return true
+        } else if directEditingEditors.contains(NCGlobal.shared.editorText) || directEditingEditors.contains(NCGlobal.shared.editorOnlyoffice) {
+            // DirectEditing: Nextcloud Text - OnlyOffice
+           return true
+        }
+        return false
+    }
+
+    var isAvailableRichDocumentEditorView: Bool {
+        guard (classFile == NKCommon.TypeClassFile.document.rawValue) && NCGlobal.shared.capabilityRichDocumentsEnabled && NextcloudKit.shared.isNetworkReachable() else { return false }
+
+        if NCUtility().isTypeFileRichDocument(self) {
+            return true
+        }
+        return false
+    }
+
+    var isAvailableDirectEditingEditorView: Bool {
+        guard (classFile == NKCommon.TypeClassFile.document.rawValue) && NextcloudKit.shared.isNetworkReachable() else { return false }
+        let editors = NCUtility().editorsDirectEditing(account: account, contentType: contentType)
+
+        if editors.contains(NCGlobal.shared.editorText) || editors.contains(NCGlobal.shared.editorOnlyoffice) {
+            return true
+        }
+        return false
+    }
+
     /// Returns false if the user is lokced out of the file. I.e. The file is locked but by somone else
     func canUnlock(as user: String) -> Bool {
         return !lock || (lockOwner == user && lockOwnerType == 0)
@@ -296,9 +326,7 @@ extension tableMetadata {
 }
 
 extension NCManageDatabase {
-
     func convertFileToMetadata(_ file: NKFile, isDirectoryE2EE: Bool) -> tableMetadata {
-
         let metadata = tableMetadata()
 
         metadata.account = file.account
@@ -306,12 +334,12 @@ extension NCManageDatabase {
         metadata.commentsUnread = file.commentsUnread
         metadata.contentType = file.contentType
         if let date = file.creationDate {
-            metadata.creationDate = date
+            metadata.creationDate = date as NSDate
         } else {
-            metadata.creationDate = file.date
+            metadata.creationDate = file.date as NSDate
         }
         metadata.dataFingerprint = file.dataFingerprint
-        metadata.date = file.date
+        metadata.date = file.date as NSDate
         metadata.directory = file.directory
         metadata.downloadURL = file.downloadURL
         metadata.e2eEncrypted = file.e2eEncrypted
@@ -360,9 +388,9 @@ extension NCManageDatabase {
             metadata.classFile = NKCommon.TypeClassFile.document.rawValue
         }
         if let date = file.uploadDate {
-            metadata.uploadDate = date
+            metadata.uploadDate = date as NSDate
         } else {
-            metadata.uploadDate = file.date
+            metadata.uploadDate = file.date as NSDate
         }
         metadata.urlBase = file.urlBase
         metadata.user = file.user
@@ -385,21 +413,17 @@ extension NCManageDatabase {
                 metadata.classFile = results.classFile
             }
         }
-
         return metadata
     }
 
     func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatas: [tableMetadata]) -> Void) {
-
         var counter: Int = 0
         var isDirectoryE2EE: Bool = false
         let listServerUrl = ThreadSafeDictionary<String, Bool>()
-
         var metadataFolder = tableMetadata()
         var metadatas: [tableMetadata] = []
 
         for file in files {
-
             if let key = listServerUrl[file.serverUrl] {
                 isDirectoryE2EE = key
             } else {
@@ -417,12 +441,10 @@ extension NCManageDatabase {
 
             counter += 1
         }
-
         completion(metadataFolder, metadatas)
     }
 
     func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool) async -> (metadataFolder: tableMetadata, metadatas: [tableMetadata]) {
-
         await withUnsafeContinuation({ continuation in
             convertFilesToMetadatas(files, useFirstAsMetadataFolder: useFirstAsMetadataFolder) { metadataFolder, metadatas in
                 continuation.resume(returning: (metadataFolder, metadatas))
@@ -431,8 +453,8 @@ extension NCManageDatabase {
     }
 
     func createMetadata(account: String, user: String, userId: String, fileName: String, fileNameView: String, ocId: String, serverUrl: String, urlBase: String, url: String, contentType: String, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil) -> tableMetadata {
-
         let metadata = tableMetadata()
+
         if isUrl {
             metadata.contentType = "text/uri-list"
             if let iconName = iconName {
@@ -467,6 +489,7 @@ extension NCManageDatabase {
         metadata.fileNameView = fileName
         metadata.name = name
         metadata.ocId = ocId
+        metadata.ocIdTemp = ocId
         metadata.permissions = "RGDNVW"
         metadata.serverUrl = serverUrl
         metadata.subline = subline
@@ -479,7 +502,6 @@ extension NCManageDatabase {
         if !metadata.urlBase.isEmpty, metadata.serverUrl.hasPrefix(metadata.urlBase) {
             metadata.path = String(metadata.serverUrl.dropFirst(metadata.urlBase.count)) + "/"
         }
-
         return metadata
     }
 
@@ -689,12 +711,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getMetadatas(predicate: NSPredicate) -> [tableMetadata] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -703,12 +723,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func getMetadatas(predicate: NSPredicate, sorted: String, ascending: Bool = false) -> [tableMetadata]? {
-
         do {
             let realm = try Realm()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
@@ -716,7 +734,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -731,31 +748,26 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getResultsMetadatas(predicate: NSPredicate, sorted: [RealmSwift.SortDescriptor]) -> Results<tableMetadata>? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableMetadata.self).filter(predicate).sorted(by: sorted)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getResultMetadata(predicate: NSPredicate) -> tableMetadata? {
-
         do {
             let realm = try Realm()
             return realm.objects(tableMetadata.self).filter(predicate).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -774,12 +786,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return metadatas
     }
 
     func getMetadataAtIndex(predicate: NSPredicate, sorted: String, ascending: Bool, index: Int) -> tableMetadata? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -792,13 +802,12 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
-
         guard let ocId else { return nil }
+
         do {
             let realm = try Realm()
             realm.refresh()
@@ -807,12 +816,28 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
+        return nil
+    }
+
+    func getMetadataFromOcIdAndOcIdTemp(_ ocId: String?) -> tableMetadata? {
+        guard let ocId else { return nil }
 
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+                return tableMetadata(value: result)
+            }
+            if let result = realm.objects(tableMetadata.self).filter("ocIdTemp == %@", ocId).first {
+                return tableMetadata(value: result)
+            }
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+        }
         return nil
     }
 
     func getMetadataFromFileName(_ fileName: String, serverUrl: String) -> tableMetadata? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -821,13 +846,12 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getMetadataFromFileNameLocalPath(_ fileNameLocalPath: String?) -> tableMetadata? {
-
         let components = fileNameLocalPath?.components(separatedBy: "/")
+
         if let count = components?.count,
            components?.count ?? 0 > 2,
            let ocId = components?[count - 2] {
@@ -840,13 +864,12 @@ extension NCManageDatabase {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             }
         }
-
         return nil
     }
 
     func getTableMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
-
         guard let ocId else { return nil }
+
         do {
             let realm = try Realm()
             realm.refresh()
@@ -858,7 +881,6 @@ extension NCManageDatabase {
     }
 
     func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -868,16 +890,14 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getMetadataFolder(account: String, urlBase: String, userId: String, serverUrl: String) -> tableMetadata? {
-
         var serverUrl = serverUrl
         var fileName = ""
-
         let serverUrlHome = utilityFileSystem.getHomeServer(urlBase: urlBase, userId: userId)
+
         if serverUrlHome == serverUrl {
             fileName = "."
             serverUrl = ".."
@@ -896,7 +916,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -915,7 +934,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return listIdentifierRank
     }
 
@@ -958,7 +976,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -986,7 +1003,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -996,12 +1012,9 @@ extension NCManageDatabase {
         var isMounted = false
 
         if metadataFolder != nil {
-
             isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared)
             isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted)
-
         } else if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
-
             isShare = metadata.permissions.contains(permissions.permissionShared) && !directory.permissions.contains(permissions.permissionShared)
             isMounted = metadata.permissions.contains(permissions.permissionMounted) && !directory.permissions.contains(permissions.permissionMounted)
         }
@@ -1026,7 +1039,6 @@ extension NCManageDatabase {
     }
 
     func getNumMetadatasInUpload() -> Int {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -1034,7 +1046,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return 0
     }
 
@@ -1048,7 +1059,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -1071,7 +1081,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return metadatas
     }
 
@@ -1083,7 +1092,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
@@ -1114,7 +1122,6 @@ extension NCManageDatabase {
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-
         return (metadatasDifferentCount, metadatasModified)
     }
 
@@ -1145,7 +1152,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 }

+ 0 - 7
iOSClient/Data/NCManageDatabase+PhotoLibrary.swift

@@ -28,7 +28,6 @@ import RealmSwift
 import NextcloudKit
 
 class tablePhotoLibrary: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var assetLocalIdentifier = ""
     @objc dynamic var creationDate: NSDate?
@@ -42,10 +41,8 @@ class tablePhotoLibrary: Object {
 }
 
 extension NCManageDatabase {
-
     @discardableResult
     func addPhotoLibrary(_ assets: [PHAsset], account: String) -> Bool {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -70,12 +67,10 @@ extension NCManageDatabase {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
             return false
         }
-
         return true
     }
 
     func getPhotoLibraryIdAsset(image: Bool, video: Bool, account: String) -> [String]? {
-
         var predicate = NSPredicate()
 
         if image && video {
@@ -95,8 +90,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
-
 }

+ 0 - 8
iOSClient/Data/NCManageDatabase+SecurityGuard.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class TableSecurityGuardDiagnostics: Object {
-
     @Persisted var account = ""
     @Persisted(primaryKey: true) var primaryKey = ""
     @Persisted var issue: String = ""
@@ -48,9 +47,7 @@ class TableSecurityGuardDiagnostics: Object {
 }
 
 extension NCManageDatabase {
-
     func addDiagnostic(account: String, issue: String, error: String? = nil) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -69,7 +66,6 @@ extension NCManageDatabase {
     }
 
     func existsDiagnostics(account: String) -> Bool {
-
         do {
             let realm = try Realm()
             let results = realm.objects(TableSecurityGuardDiagnostics.self).where({
@@ -79,12 +75,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return false
     }
 
     func getDiagnostics(account: String, issue: String) -> Results<TableSecurityGuardDiagnostics>? {
-
         do {
             let realm = try Realm()
             let results = realm.objects(TableSecurityGuardDiagnostics.self).where({
@@ -94,12 +88,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func deleteDiagnostics(account: String, ids: [ObjectId]) {
-
         do {
             let realm = try Realm()
             try realm.write {

+ 2 - 18
iOSClient/Data/NCManageDatabase+Share.swift

@@ -27,7 +27,6 @@ import NextcloudKit
 
 typealias tableShare = tableShareV2
 class tableShareV2: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var canEdit: Bool = false
     @objc dynamic var canDelete: Bool = false
@@ -75,9 +74,7 @@ class tableShareV2: Object {
 }
 
 extension NCManageDatabase {
-
     func addShare(account: String, home: String, shares: [NKShare]) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -92,7 +89,7 @@ extension NCManageDatabase {
                     object.serverUrl = serverUrl
                     object.canEdit = share.canEdit
                     object.canDelete = share.canDelete
-                    object.date = share.date
+                    object.date = share.date as? NSDate
                     object.displaynameFileOwner = share.displaynameFileOwner
                     object.displaynameOwner = share.displaynameOwner
                     object.expirationDate = share.expirationDate
@@ -122,7 +119,7 @@ extension NCManageDatabase {
                     object.uidOwner = share.uidOwner
                     object.uidFileOwner = share.uidFileOwner
                     object.url = share.url
-                    object.userClearAt = share.userClearAt
+                    object.userClearAt = share.userClearAt as? NSDate
                     object.userIcon = share.userIcon
                     object.userMessage = share.userMessage
                     object.userStatus = share.userStatus
@@ -136,7 +133,6 @@ extension NCManageDatabase {
     }
 
     func getTableShares(account: String) -> [tableShare] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -146,12 +142,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func getTableShares(metadata: tableMetadata) -> (firstShareLink: tableShare?, share: [tableShare]?) {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -167,12 +161,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return (nil, nil)
     }
 
     func getTableShare(account: String, idShare: Int) -> tableShare? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -181,12 +173,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func getTableShares(account: String, serverUrl: String) -> [tableShare] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -196,12 +186,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return []
     }
 
     func getTableShares(account: String, serverUrl: String, fileName: String) -> [tableShare] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -216,7 +204,6 @@ extension NCManageDatabase {
     }
 
     func deleteTableShare(account: String, idShare: Int) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -229,7 +216,6 @@ extension NCManageDatabase {
     }
 
     func deleteTableShare(account: String, path: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -242,7 +228,6 @@ extension NCManageDatabase {
     }
 
     func deleteTableShare(account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -280,5 +265,4 @@ extension NCManageDatabase {
         }
         return true
     }
-
 }

+ 0 - 8
iOSClient/Data/NCManageDatabase+Tag.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableTag: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var ocId = ""
     @objc dynamic var tagIOS: Data?
@@ -37,9 +36,7 @@ class tableTag: Object {
 }
 
 extension NCManageDatabase {
-
     func addTag(_ ocId: String, tagIOS: Data?, account: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -55,7 +52,6 @@ extension NCManageDatabase {
     }
 
     func deleteTag(_ ocId: String) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -68,7 +64,6 @@ extension NCManageDatabase {
     }
 
     func getTags(predicate: NSPredicate) -> [tableTag] {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -77,12 +72,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return []
     }
 
     func getTag(predicate: NSPredicate) -> tableTag? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -91,7 +84,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not acess to database: \(error)")
         }
-
         return nil
     }
 }

+ 0 - 1
iOSClient/Data/NCManageDatabase+Tip.swift

@@ -41,7 +41,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return false
     }
 

+ 2 - 10
iOSClient/Data/NCManageDatabase+Trash.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableTrash: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var classFile = ""
     @objc dynamic var contentType = ""
@@ -48,9 +47,7 @@ class tableTrash: Object {
 }
 
 extension NCManageDatabase {
-
     func addTrash(account: String, items: [NKTrash]) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -58,7 +55,7 @@ extension NCManageDatabase {
                     let object = tableTrash()
                     object.account = account
                     object.contentType = trash.contentType
-                    object.date = trash.date
+                    object.date = trash.date as NSDate
                     object.directory = trash.directory
                     object.fileId = trash.fileId
                     object.fileName = trash.fileName
@@ -66,7 +63,7 @@ extension NCManageDatabase {
                     object.hasPreview = trash.hasPreview
                     object.iconName = trash.iconName
                     object.size = trash.size
-                    object.trashbinDeletionTime = trash.trashbinDeletionTime
+                    object.trashbinDeletionTime = trash.trashbinDeletionTime as NSDate
                     object.trashbinFileName = trash.trashbinFileName
                     object.trashbinOriginalLocation = trash.trashbinOriginalLocation
                     object.classFile = trash.classFile
@@ -79,7 +76,6 @@ extension NCManageDatabase {
     }
 
     func deleteTrash(filePath: String?, account: String) {
-
         var predicate = NSPredicate()
 
         do {
@@ -99,7 +95,6 @@ extension NCManageDatabase {
     }
 
     func deleteTrash(fileId: String?, account: String) {
-
         var predicate = NSPredicate()
 
         do {
@@ -127,12 +122,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return []
     }
 
     func getTrashItem(fileId: String, account: String) -> tableTrash? {
-
         do {
             let realm = try Realm()
             realm.refresh()
@@ -141,7 +134,6 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-
         return nil
     }
 }

+ 1 - 4
iOSClient/Data/NCManageDatabase+UserStatus.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 
 class tableUserStatus: Object {
-
     @objc dynamic var account = ""
     @objc dynamic var clearAt: NSDate?
     @objc dynamic var clearAtTime: String?
@@ -40,9 +39,7 @@ class tableUserStatus: Object {
 }
 
 extension NCManageDatabase {
-
     func addUserStatus(_ userStatuses: [NKUserStatus], account: String, predefined: Bool) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -51,7 +48,7 @@ extension NCManageDatabase {
                 for userStatus in userStatuses {
                     let object = tableUserStatus()
                     object.account = account
-                    object.clearAt = userStatus.clearAt
+                    object.clearAt = userStatus.clearAt as? NSDate
                     object.clearAtTime = userStatus.clearAtTime
                     object.clearAtType = userStatus.clearAtType
                     object.icon = userStatus.icon

+ 0 - 7
iOSClient/Data/NCManageDatabase+Video.swift

@@ -27,7 +27,6 @@ import NextcloudKit
 
 typealias tableVideo = tableVideoV4
 class tableVideoV4: Object {
-
     @Persisted var account = ""
     @Persisted(primaryKey: true) var ocId = ""
     @Persisted var position: Float?
@@ -45,9 +44,7 @@ class tableVideoV4: Object {
 }
 
 extension NCManageDatabase {
-
     func addVideo(metadata: tableMetadata, position: Float? = nil, width: Int? = nil, height: Int? = nil, length: Int? = nil, currentAudioTrackIndex: Int? = nil, currentVideoSubTitleIndex: Int? = nil) {
-
         if metadata.isLivePhoto { return }
 
         do {
@@ -104,7 +101,6 @@ extension NCManageDatabase {
     }
 
     func addVideoCodec(metadata: tableMetadata, codecNameVideo: String?, codecNameAudio: String?, codecAudioChannelLayout: String?, codecAudioLanguage: String?, codecMaxCompatibility: Bool, codecQuality: String?) {
-
         do {
             let realm = try Realm()
             try realm.write {
@@ -135,7 +131,6 @@ extension NCManageDatabase {
     }
 
     func getVideo(metadata: tableMetadata?) -> tableVideo? {
-
         guard let metadata = metadata else { return nil }
 
         do {
@@ -146,12 +141,10 @@ extension NCManageDatabase {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-
         return nil
     }
 
     func deleteVideo(metadata: tableMetadata) {
-
         do {
             let realm = try Realm()
             try realm.write {

+ 56 - 98
iOSClient/Data/NCManageDatabase.swift

@@ -37,18 +37,12 @@ class NCManageDatabase: NSObject {
         let instance = NCManageDatabase()
         return instance
     }()
-
     let utilityFileSystem = NCUtilityFileSystem()
 
     override init() {
-
-        let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroups)
+        let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
         let databaseFileUrlPath = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + databaseName)
 
-        let bundleUrl: URL = Bundle.main.bundleURL
-        let bundlePathExtension: String = bundleUrl.pathExtension
-        let isAppex: Bool = bundlePathExtension == "appex"
-
         if let databaseFilePath = databaseFileUrlPath?.path {
             if FileManager.default.fileExists(atPath: databaseFilePath) {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] DATABASE FOUND in " + databaseFilePath)
@@ -68,98 +62,40 @@ class NCManageDatabase: NSObject {
             }
         }
 
-        if isAppex {
-
-            Realm.Configuration.defaultConfiguration = Realm.Configuration(
-                fileURL: dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + databaseName),
+        do {
+            _ = try Realm(configuration: Realm.Configuration(
+                fileURL: databaseFileUrlPath,
                 schemaVersion: databaseSchemaVersion,
-                objectTypes: [tableMetadata.self,
-                              tableLocalFile.self,
-                              tableDirectory.self,
-                              tableTag.self,
-                              tableAccount.self,
-                              tableCapabilities.self,
-                              tablePhotoLibrary.self,
-                              tableE2eEncryption.self,
-                              tableE2eEncryptionLock.self,
-                              tableE2eMetadata12.self,
-                              tableE2eMetadata.self,
-                              tableE2eUsers.self,
-                              tableE2eCounter.self,
-                              tableShare.self,
-                              tableChunk.self,
-                              tableAvatar.self,
-                              tableDashboardWidget.self,
-                              tableDashboardWidgetButton.self,
-                              NCDBLayoutForView.self,
-                              TableSecurityGuardDiagnostics.self]
-            )
-
-        } else {
-
-            do {
-                _ = try Realm(configuration: Realm.Configuration(
-
-                    fileURL: databaseFileUrlPath,
-                    schemaVersion: databaseSchemaVersion,
-
-                    migrationBlock: { migration, oldSchemaVersion in
-
-                        if oldSchemaVersion < 255 {
-                            migration.deleteData(forType: tableActivity.className())
-                            migration.deleteData(forType: tableActivityLatestId.className())
-                            migration.deleteData(forType: tableActivityPreview.className())
-                            migration.deleteData(forType: tableActivitySubjectRich.className())
-                        }
-
-                        if oldSchemaVersion < 292 {
-                            migration.deleteData(forType: tableVideo.className())
-                        }
-
-                        if oldSchemaVersion < 319 {
-                            migration.deleteData(forType: tableChunk.className())
-                            migration.deleteData(forType: tableDirectory.className())
-                            migration.deleteData(forType: tableE2eEncryptionLock.className())
-                            migration.deleteData(forType: tableGPS.className())
-                        }
-
-                        if oldSchemaVersion < 333 {
-                            migration.deleteData(forType: tableMetadata.className())
-                            migration.enumerateObjects(ofType: tableDirectory.className()) { _, newObject in
-                                newObject?["etag"] = ""
-                            }
-                        }
-
-                    }, shouldCompactOnLaunch: { totalBytes, usedBytes in
-
-                        // totalBytes refers to the size of the file on disk in bytes (data + free space)
-                        // usedBytes refers to the number of bytes used by data in the file
-
-                        // Compact if the file is over 100MB in size and less than 50% 'used'
-                        let oneHundredMB = 100 * 1024 * 1024
-                        return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
+                migrationBlock: { migration, oldSchemaVersion in
+                    if oldSchemaVersion < 354 {
+                        migration.deleteData(forType: NCDBLayoutForView.className())
                     }
-                ))
-
-            } catch let error {
-                if let databaseFileUrlPath = databaseFileUrlPath {
-                    do {
+                }, shouldCompactOnLaunch: { totalBytes, usedBytes in
+                    // totalBytes refers to the size of the file on disk in bytes (data + free space)
+                    // usedBytes refers to the number of bytes used by data in the file
+                    // Compact if the file is over 100MB in size and less than 50% 'used'
+                    let oneHundredMB = 100 * 1024 * 1024
+                    return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
+                }
+            ))
+        } catch let error {
+            if let databaseFileUrlPath = databaseFileUrlPath {
+                do {
 #if !EXTENSION
-                        let nkError = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: error.localizedDescription)
-                        NCContentPresenter().showError(error: nkError, priority: .max)
+                    let nkError = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: error.localizedDescription)
+                    NCContentPresenter().showError(error: nkError, priority: .max)
 #endif
-                        NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] DATABASE ERROR: \(error.localizedDescription)")
-                        try FileManager.default.removeItem(at: databaseFileUrlPath)
-                    } catch {}
-                }
+                    NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] DATABASE ERROR: \(error.localizedDescription)")
+                    try FileManager.default.removeItem(at: databaseFileUrlPath)
+                } catch {}
             }
-
-            Realm.Configuration.defaultConfiguration = Realm.Configuration(
-                fileURL: dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + databaseName),
-                schemaVersion: databaseSchemaVersion
-            )
         }
 
+        Realm.Configuration.defaultConfiguration = Realm.Configuration(
+            fileURL: dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + databaseName),
+            schemaVersion: databaseSchemaVersion
+        )
+
         // Verify Database, if corrupt remove it
         do {
             _ = try Realm()
@@ -228,7 +164,6 @@ class NCManageDatabase: NSObject {
         self.clearTable(tableGPS.self, account: nil)
         self.clearTable(TableGroupfolders.self, account: account)
         self.clearTable(TableGroupfoldersGroups.self, account: account)
-        self.clearTable(NCDBLayoutForView.self, account: account)
         self.clearTable(tableLocalFile.self, account: account)
         self.clearTable(tableMetadata.self, account: account)
         self.clearTable(tablePhotoLibrary.self, account: account)
@@ -241,7 +176,6 @@ class NCManageDatabase: NSObject {
     }
 
     func clearTablesE2EE(account: String?) {
-
         self.clearTable(tableE2eEncryption.self, account: account)
         self.clearTable(tableE2eEncryptionLock.self, account: account)
         self.clearTable(tableE2eMetadata12.self, account: account)
@@ -251,7 +185,6 @@ class NCManageDatabase: NSObject {
     }
 
     @objc func removeDB() {
-
         let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
         let realmURLs = [
             realmURL,
@@ -259,6 +192,7 @@ class NCManageDatabase: NSObject {
             realmURL.appendingPathExtension("note"),
             realmURL.appendingPathExtension("management")
         ]
+
         for URL in realmURLs {
             do {
                 try FileManager.default.removeItem(at: URL)
@@ -269,22 +203,46 @@ class NCManageDatabase: NSObject {
     }
 
     func getThreadConfined(_ object: Object) -> Any {
-
         return ThreadSafeReference(to: object)
     }
 
     func putThreadConfined(_ tableRef: ThreadSafeReference<Object>) -> Object? {
-
         do {
             let realm = try Realm()
             return realm.resolve(tableRef)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-
         return nil
     }
 
+    // MARK: -
+    // MARK: Func T
+
+    func fetchPagedResults<T: Object>(ofType type: T.Type, primaryKey: String, recordsPerPage: Int, pageNumber: Int, filter: NSPredicate? = nil, sortedByKeyPath: String? = nil, sortedAscending: Bool = true) -> Results<T>? {
+        let startIndex = recordsPerPage * (pageNumber - 1)
+
+        do {
+            let realm = try Realm()
+            var results = realm.objects(type)
+
+            if let filter, let sortedByKeyPath {
+                results = results.filter(filter).sorted(byKeyPath: sortedByKeyPath, ascending: sortedAscending)
+            }
+
+            guard startIndex < results.count else {
+                return nil
+            }
+            let pagedResults = results.dropFirst(startIndex).prefix(recordsPerPage)
+            let pagedResultsKeys = pagedResults.compactMap { $0.value(forKey: primaryKey) as? String }
+
+            return realm.objects(type).filter("\(primaryKey) IN %@", Array(pagedResultsKeys))
+        } catch {
+            print("Error opening Realm: \(error)")
+            return nil
+        }
+    }
+
     // MARK: -
     // MARK: SWIFTUI PREVIEW
 

+ 1 - 1
iOSClient/Extensions/UIAlertController+Extension.swift

@@ -39,7 +39,7 @@ extension UIAlertController {
             guard let fileNameFolder = alertController.textFields?.first?.text else { return }
             if markE2ee {
                 Task {
-                    let createFolderResults = await NextcloudKit.shared.createFolder(serverUrlFileName: serverUrl + "/" + fileNameFolder)
+                    let createFolderResults = await NCNetworking.shared.createFolder(serverUrlFileName: serverUrl + "/" + fileNameFolder)
                     if createFolderResults.error == .success {
                         let error = await NCNetworkingE2EEMarkFolder().markFolderE2ee(account: urlBase.account, fileName: fileNameFolder, serverUrl: serverUrl, userId: urlBase.userId)
                         if error != .success {

+ 9 - 0
iOSClient/Extensions/UIView+Extension.swift

@@ -55,6 +55,15 @@ extension UIView {
         self.addSubview(blurredEffectView)
     }
 
+    func insertBlur(style: UIBlurEffect.Style) {
+        let blur = UIBlurEffect(style: style)
+        let blurredEffectView = UIVisualEffectView(effect: blur)
+        blurredEffectView.frame = self.bounds
+        blurredEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        blurredEffectView.isUserInteractionEnabled = false
+        self.insertSubview(blurredEffectView, at: 0)
+    }
+
     func makeCircularBackground(withColor backgroundColor: UIColor) {
         self.backgroundColor = backgroundColor
         self.layer.cornerRadius = self.frame.size.width / 2

+ 1 - 9
iOSClient/Favorites/NCFavorite.swift

@@ -62,15 +62,7 @@ class NCFavorite: NCCollectionViewCommon {
             metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
         }
 
-        self.dataSource = NCDataSource(metadatas: metadatas,
-                                       account: self.appDelegate.account,
-                                       sort: self.layoutForView?.sort,
-                                       ascending: self.layoutForView?.ascending,
-                                       directoryOnTop: self.layoutForView?.directoryOnTop,
-                                       favoriteOnTop: true,
-                                       groupByField: self.groupByField,
-                                       providers: self.providers,
-                                       searchResults: self.searchResults)
+        self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account, layoutForView: layoutForView, providers: self.providers, searchResults: self.searchResults)
     }
 
     override func reloadDataSourceNetwork(withQueryDB: Bool = false) {

+ 5 - 14
iOSClient/Files/NCFiles.swift

@@ -58,11 +58,12 @@ class NCFiles: NCCollectionViewCommon {
                 self.selectOcId.removeAll()
 
                 self.layoutForView = NCManageDatabase.shared.getLayoutForView(account: self.appDelegate.account, key: self.layoutKey, serverUrl: self.serverUrl)
-                self.gridLayout.itemForLine = CGFloat(self.layoutForView?.itemForLine ?? 3)
                 if self.layoutForView?.layout == NCGlobal.shared.layoutList {
                     self.collectionView?.collectionViewLayout = self.listLayout
-                } else {
+                } else if self.layoutForView?.layout == NCGlobal.shared.layoutGrid {
                     self.collectionView?.collectionViewLayout = self.gridLayout
+                } else if self.layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare || self.layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio {
+                    self.collectionView?.collectionViewLayout = self.mediaLayout
                 }
 
                 self.titleCurrentFolder = self.getNavigationTitle()
@@ -111,17 +112,7 @@ class NCFiles: NCCollectionViewCommon {
         }
 
         self.richWorkspaceText = directory?.richWorkspace
-        self.dataSource = NCDataSource(
-            metadatas: metadatas,
-            account: self.appDelegate.account,
-            directory: directory,
-            sort: self.layoutForView?.sort,
-            ascending: self.layoutForView?.ascending,
-            directoryOnTop: self.layoutForView?.directoryOnTop,
-            favoriteOnTop: true,
-            groupByField: self.groupByField,
-            providers: self.providers,
-            searchResults: self.searchResults)
+        self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account, layoutForView: layoutForView, providers: self.providers, searchResults: self.searchResults)
     }
 
     override func reloadDataSource(withQueryDB: Bool = true) {
@@ -298,7 +289,7 @@ class NCFiles: NCCollectionViewCommon {
         if NCManageDatabase.shared.getAllAccount().isEmpty {
             appDelegate.openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
         } else if let account = tableAccount?.account, account != appDelegate.account {
-            appDelegate.changeAccount(account, userProfile: nil)
+            appDelegate.changeAccount(account, userProfile: nil) { }
         } else if isRoot {
             titleCurrentFolder = getNavigationTitle()
             navigationItem.title = titleCurrentFolder

+ 2 - 11
iOSClient/Groupfolders/NCGroupfolders.swift

@@ -62,16 +62,7 @@ class NCGroupfolders: NCCollectionViewCommon {
             metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
         }
 
-        self.dataSource = NCDataSource(
-            metadatas: metadatas,
-            account: self.appDelegate.account,
-            sort: self.layoutForView?.sort,
-            ascending: self.layoutForView?.ascending,
-            directoryOnTop: self.layoutForView?.directoryOnTop,
-            favoriteOnTop: true,
-            groupByField: self.groupByField,
-            providers: self.providers,
-            searchResults: self.searchResults)
+        self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account, layoutForView: layoutForView, providers: self.providers, searchResults: self.searchResults)
     }
 
     override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
@@ -90,7 +81,7 @@ class NCGroupfolders: NCCollectionViewCommon {
                         let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
                         let serverUrlFileName = homeServerUrl + mountPoint
                         if NCManageDatabase.shared.getMetadataFromDirectory(account: self.appDelegate.account, serverUrl: serverUrlFileName) == nil {
-                            let results = await NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles)
+                            let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles)
                             if results.error == .success, let file = results.files.first {
                                 let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file)
                                 let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)

+ 0 - 15
iOSClient/Images.xcassets/qrcode.imageset/Contents.json

@@ -1,15 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "qrcode.pdf",
-      "idiom" : "universal"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/qrcode.imageset/qrcode.pdf


+ 38 - 152
iOSClient/Login/NCLogin.swift

@@ -28,7 +28,6 @@ import SwiftEntryKit
 import SwiftUI
 
 class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
-
     @IBOutlet weak var imageBrand: UIImageView!
     @IBOutlet weak var imageBrandConstraintY: NSLayoutConstraint!
     @IBOutlet weak var baseUrl: UITextField!
@@ -108,11 +107,13 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         // brand
         if NCBrandOptions.shared.disable_request_login_url {
             baseUrl.text = NCBrandOptions.shared.loginBaseUrl
-            baseUrl.isHidden = true
+            baseUrl.isEnabled = false
+            baseUrl.isUserInteractionEnabled = false
+            baseUrl.alpha = 0.5
         }
 
         // qrcode
-        qrCode.setImage(UIImage(named: "qrcode")?.image(color: textColor, size: 100), for: .normal)
+        qrCode.setImage(UIImage(systemName: "qrcode.viewfinder")?.image(color: textColor, size: 100), for: .normal)
 
         // certificate
         certificate.setImage(UIImage(named: "certificate")?.image(color: textColor, size: 100), for: .normal)
@@ -165,10 +166,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
 
-        NCNetworking.shared.delegate = self
-
         handleLoginWithAppConfig()
-
         baseUrl.text = urlBase
     }
 
@@ -201,15 +199,12 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                 if let serverUrl = configurationManaged[NCGlobal.shared.configuration_serverUrl] as? String {
                     self.configServerUrl = serverUrl
                 }
-
                 if let username = configurationManaged[NCGlobal.shared.configuration_username] as? String, !username.isEmpty, username.lowercased() != "username" {
                     self.configUsername = username
                 }
-
                 if let password = configurationManaged[NCGlobal.shared.configuration_password] as? String, !password.isEmpty, password.lowercased() != "password" {
                     self.configPassword = password
                 }
-
                 if let apppassword = configurationManaged[NCGlobal.shared.configuration_apppassword] as? String, !apppassword.isEmpty, apppassword.lowercased() != "apppassword" {
                     self.configAppPassword = apppassword
                 }
@@ -217,15 +212,13 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         }
 
         // AppConfig
-        if let serverUrl = configServerUrl {
-            if let username = self.configUsername, let password = configAppPassword {
-                createAccount(server: serverUrl, username: username, password: password)
-                return
-            } else if let username = self.configUsername, let password = configPassword {
-                getAppPassword(serverUrl: serverUrl, username: username, password: password)
-                return
+        if let url = configServerUrl {
+            if let user = self.configUsername, let password = configAppPassword {
+                return createAccount(urlBase: url, user: user, password: password)
+            } else if let user = self.configUsername, let password = configPassword {
+                return getAppPassword(urlBase: url, user: user, password: password)
             } else {
-                urlBase = serverUrl
+                urlBase = url
             }
         }
     }
@@ -239,16 +232,13 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     }
 
     func textFieldDidBeginEditing(_ textField: UITextField) {
-
         self.activeTextField = textField
     }
 
     // MARK: - Keyboard notification
 
     @objc internal func keyboardWillShow(_ notification: Notification?) {
-
         activeTextfieldDiff = 0
-
         if let info = notification?.userInfo, let centerObject = self.activeTextField.superview?.convert(self.activeTextField.center, to: nil) {
 
             let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey
@@ -291,9 +281,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     // MARK: - Share accounts View Controller
 
     @objc func openShareAccountsViewController() {
-
         if let shareAccounts = self.shareAccounts, let vc = UIStoryboard(name: "NCShareAccounts", bundle: nil).instantiateInitialViewController() as? NCShareAccounts {
-
             vc.accounts = shareAccounts
             vc.enableTimerProgress = false
             vc.dismissDidEnterBackground = false
@@ -302,7 +290,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height / 5)
             let numberCell = shareAccounts.count
             let height = min(CGFloat(numberCell * Int(vc.heightCell) + 45), screenHeighMax)
-
             let popup = NCPopupViewController(contentController: vc, popupWidth: 300, popupHeight: height + 20)
 
             self.present(popup, animated: true)
@@ -315,7 +302,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         guard var url = baseUrl.text?.trimmingCharacters(in: .whitespacesAndNewlines) else { return }
         if url.hasSuffix("/") { url = String(url.dropLast()) }
         if url.isEmpty { return }
-
         // Check whether baseUrl contain protocol. If not add https:// by default.
         if url.hasPrefix("https") == false && url.hasPrefix("http") == false {
             url = "https://" + url
@@ -325,53 +311,35 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     }
 
     func isUrlValid(url: String, user: String? = nil) {
-
         loginButton.isEnabled = false
-
         NextcloudKit.shared.getServerStatus(serverUrl: url) { serverInfoResult in
-
             switch serverInfoResult {
             case .success(let serverInfo):
-
                 if let host = URL(string: url)?.host {
                     NCNetworking.shared.writeCertificate(host: host)
                 }
-
                 NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { token, endpoint, login, _, error in
-
                     self.loginButton.isEnabled = true
-
                     // Login Flow V2
                     if error == .success, let token, let endpoint, let login {
                         let vc = UIHostingController(rootView: NCLoginPoll(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login))
-
                         self.present(vc, animated: true)
                     } else if serverInfo.versionMajor < NCGlobal.shared.nextcloudVersion12 { // No login flow available
-
                         let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_webflow_not_available_", comment: ""), preferredStyle: .alert)
-
                         alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-
                         self.present(alertController, animated: true, completion: { })
                     }
                 }
-
             case .failure(let error):
-
                 self.loginButton.isEnabled = true
-
                 if error.errorCode == NSURLErrorServerCertificateUntrusted {
-
                     let alertController = UIAlertController(title: NSLocalizedString("_ssl_certificate_untrusted_", comment: ""), message: NSLocalizedString("_connect_server_anyway_", comment: ""), preferredStyle: .alert)
-
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
                         if let host = URL(string: url)?.host {
                             NCNetworking.shared.writeCertificate(host: host)
                         }
                     }))
-
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in }))
-
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in
                         if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController,
                            let viewController = navigationController.topViewController as? NCViewCertificateDetails {
@@ -381,15 +349,10 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                             self.present(navigationController, animated: true)
                         }
                     }))
-
                     self.present(alertController, animated: true)
-
                 } else {
-
                     let alertController = UIAlertController(title: NSLocalizedString("_connection_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
-
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-
                     self.present(alertController, animated: true, completion: { })
                 }
             }
@@ -399,135 +362,70 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     // MARK: - QRCode
 
     func dismissQRCode(_ value: String?, metadataType: String?) {
-
         guard var value = value else { return }
-
         let protocolLogin = NCBrandOptions.shared.webLoginAutenticationProtocol + "login/"
-
         if value.hasPrefix(protocolLogin) && value.contains("user:") && value.contains("password:") && value.contains("server:") {
-
             value = value.replacingOccurrences(of: protocolLogin, with: "")
             let valueArray = value.components(separatedBy: "&")
             if valueArray.count == 3 {
-
                 let user = valueArray[0].replacingOccurrences(of: "user:", with: "")
                 let password = valueArray[1].replacingOccurrences(of: "password:", with: "")
                 let urlBase = valueArray[2].replacingOccurrences(of: "server:", with: "")
                 let serverUrl = urlBase + "/" + NextcloudKit.shared.nkCommonInstance.dav
-
                 loginButton.isEnabled = false
-
                 NextcloudKit.shared.checkServer(serverUrl: serverUrl) { error in
-
                     self.loginButton.isEnabled = true
-                    self.standardLogin(url: urlBase, user: user, password: password, error: error)
+                    if error == .success {
+                        self.createAccount(urlBase: urlBase, user: user, password: password)
+                    } else {
+                        let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                        self.present(alertController, animated: true)
+                    }
                 }
             }
         }
     }
 
-    func standardLogin(url: String, user: String, password: String, error: NKError) {
-
-        if error == .success {
-
-            if let host = URL(string: url)?.host {
-                NCNetworking.shared.writeCertificate(host: host)
-            }
-            let urlBase = url
-            let account = user + " " + user
-
-            NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
-            NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
-
-                if error == .success, let userProfile {
-
-                    NCManageDatabase.shared.deleteAccount(account)
-                    NCManageDatabase.shared.addAccount(account, urlBase: url, user: user, userId: userProfile.userId, password: password)
-
-                    self.appDelegate.changeAccount(account, userProfile: userProfile)
-
-                    let window = UIApplication.shared.firstWindow
-                    if window?.rootViewController is NCMainTabBarController {
-                        self.dismiss(animated: true)
-                    } else {
-                        if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
-                            controller.modalPresentationStyle = .fullScreen
-                            controller.view.alpha = 0
-                            window?.rootViewController = controller
-                            window?.makeKeyAndVisible()
-                            UIView.animate(withDuration: 0.5) {
-                                controller.view.alpha = 1
-                            }
-                        }
-                    }
-                } else {
-
-                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                    self.present(alertController, animated: true)
-                }
+    private func getAppPassword(urlBase: String, user: String, password: String) {
+        NextcloudKit.shared.getAppPassword(url: urlBase, user: user, password: password) { token, _, error in
+            if error == .success, let password = token {
+                self.createAccount(urlBase: urlBase, user: user, password: password)
+            } else {
+                NCContentPresenter().showError(error: error)
+                self.dismiss(animated: true, completion: nil)
             }
-
-        } else if error.errorCode == NSURLErrorServerCertificateUntrusted {
-
-            let alertController = UIAlertController(title: NSLocalizedString("_ssl_certificate_untrusted_", comment: ""), message: NSLocalizedString("_connect_server_anyway_", comment: ""), preferredStyle: .alert)
-
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
-                if let host = URL(string: url)?.host {
-                    NCNetworking.shared.writeCertificate(host: host)
-                }
-            }))
-
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in }))
-
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in
-                if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() {
-                    self.present(navigationController, animated: true)
-                }
-            }))
-
-            self.present(alertController, animated: true)
-
-        } else {
-
-            let message = NSLocalizedString("_not_possible_connect_to_server_", comment: "") + ".\n" + error.errorDescription
-            let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: message, preferredStyle: .alert)
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-            self.present(alertController, animated: true, completion: { })
         }
     }
 
-    private func createAccount(server: String, username: String, password: String) {
-        appDelegate.createAccount(server: server, username: username, password: password) { error in
+    private func createAccount(urlBase: String, user: String, password: String) {
+        if let host = URL(string: urlBase)?.host {
+            NCNetworking.shared.writeCertificate(host: host)
+        }
+        self.appDelegate.createAccount(urlBase: urlBase, user: user, password: password) { error in
             if error == .success {
                 let window = UIApplication.shared.firstWindow
                 if window?.rootViewController is NCMainTabBarController {
                     self.dismiss(animated: true)
                 } else {
-                    if let mainTabBarController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
-                        mainTabBarController.modalPresentationStyle = .fullScreen
-                        mainTabBarController.view.alpha = 0
-                        window?.rootViewController = mainTabBarController
+                    if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+                        controller.modalPresentationStyle = .fullScreen
+                        controller.view.alpha = 0
+                        window?.rootViewController = controller
                         window?.makeKeyAndVisible()
                         UIView.animate(withDuration: 0.5) {
-                            mainTabBarController.view.alpha = 1
+                            controller.view.alpha = 1
                         }
                     }
                 }
-            }
-        }
-    }
-
-    private func getAppPassword(serverUrl: String, username: String, password: String) {
-        NextcloudKit.shared.getAppPassword(serverUrl: serverUrl, username: username, password: password) { token, _, error in
-            if error == .success, let password = token {
-                self.createAccount(server: serverUrl, username: username, password: password)
             } else {
-                NCContentPresenter().showError(error: error)
-                self.dismiss(animated: true, completion: nil)
+                let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                self.present(alertController, animated: true)
             }
         }
     }
+
 }
 
 extension NCLogin: NCShareAccountsDelegate {
@@ -539,47 +437,35 @@ extension NCLogin: NCShareAccountsDelegate {
 extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate {
     func didAskForClientCertificate() {
         let alertNoCertFound = UIAlertController(title: NSLocalizedString("_no_client_cert_found_", comment: ""), message: NSLocalizedString("_no_client_cert_found_desc_", comment: ""), preferredStyle: .alert)
-
         alertNoCertFound.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: nil))
-
         alertNoCertFound.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
             let documentProviderMenu = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.pkcs12])
             documentProviderMenu.delegate = self
-
             self.present(documentProviderMenu, animated: true, completion: nil)
         }))
-
         present(alertNoCertFound, animated: true)
     }
 
     func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
         let alertEnterPassword = UIAlertController(title: NSLocalizedString("_client_cert_enter_password_", comment: ""), message: "", preferredStyle: .alert)
-
         alertEnterPassword.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: nil))
-
         alertEnterPassword.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
             // let documentProviderMenu = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.pkcs12])
             NCNetworking.shared.p12Data = try? Data(contentsOf: urls[0])
             NCNetworking.shared.p12Password = alertEnterPassword.textFields?[0].text
-
             self.login()
         }))
-
         alertEnterPassword.addTextField { textField in
             textField.isSecureTextEntry = true
         }
-
         present(alertEnterPassword, animated: true)
     }
 
     func onIncorrectPassword() {
         NCNetworking.shared.p12Data = nil
         NCNetworking.shared.p12Password = nil
-
         let alertWrongPassword = UIAlertController(title: NSLocalizedString("_client_cert_wrong_password_", comment: ""), message: "", preferredStyle: .alert)
-
         alertWrongPassword.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default))
-
         present(alertWrongPassword, animated: true)
     }
 }

+ 6 - 10
iOSClient/Login/NCLoginPoll.swift

@@ -125,9 +125,13 @@ private class LoginManager: ObservableObject {
 
     func poll() {
         NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in
-            if error == .success, let server, let loginName, let appPassword {
+            if error == .success, let urlBase = server, let user = loginName, let appPassword {
                 self.isLoading = true
-                self.createAccount(server: server, username: loginName, password: appPassword)
+                self.appDelegate.createAccount(urlBase: urlBase, user: user, password: appPassword) { error in
+                    if error == .success {
+                        self.pollFinished = true
+                    }
+                }
             }
         }
     }
@@ -135,12 +139,4 @@ private class LoginManager: ObservableObject {
     func openLoginInBrowser() {
         UIApplication.shared.open(URL(string: loginFlowV2Login)!)
     }
-
-    private func createAccount(server: String, username: String, password: String) {
-        appDelegate.createAccount(server: server, username: username, password: password) { error in
-            if error == .success {
-                self.pollFinished = true
-            }
-        }
-    }
 }

+ 1 - 8
iOSClient/Login/NCLoginProvider.swift

@@ -128,11 +128,9 @@ extension NCLoginProvider: WKNavigationDelegate {
             }
 
             if !server.isEmpty, !user.isEmpty, !password.isEmpty {
-
                 let server: String = server.replacingOccurrences(of: "/server:", with: "")
                 let username: String = user.replacingOccurrences(of: "user:", with: "").replacingOccurrences(of: "+", with: " ")
                 let password: String = password.replacingOccurrences(of: "password:", with: "")
-
                 createAccount(server: server, username: username, password: password)
             }
         }
@@ -170,14 +168,10 @@ extension NCLoginProvider: WKNavigationDelegate {
 
         NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
         NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
-
             if error == .success, let userProfile {
-
                 NCManageDatabase.shared.deleteAccount(account)
                 NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
-
-                self.appDelegate.changeAccount(account, userProfile: userProfile)
-
+                self.appDelegate.changeAccount(account, userProfile: userProfile) { }
                 let window = UIApplication.shared.firstWindow
                 if window?.rootViewController is NCMainTabBarController {
                     self.dismiss(animated: true)
@@ -192,7 +186,6 @@ extension NCLoginProvider: WKNavigationDelegate {
                         }
                     }
                 }
-
             } else {
                 let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
                 alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))

+ 0 - 5
iOSClient/Login/NCLoginQRCode.swift

@@ -59,7 +59,6 @@ class NCLoginQRCode: NSObject, QRCodeReaderViewControllerDelegate {
         readerVC.completionBlock = { (_: QRCodeReaderResult?) in
             self.readerVC.dismiss(animated: true, completion: nil)
         }
-
         delegate?.present(readerVC, animated: true, completion: nil)
     }
 
@@ -68,11 +67,9 @@ class NCLoginQRCode: NSObject, QRCodeReaderViewControllerDelegate {
             return try QRCodeReader.supportsMetadataObjectTypes()
         } catch let error as NSError {
             let alert: UIAlertController
-
             switch error.code {
             case -11852:
                 alert = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_qrcode_not_authorized_", comment: ""), preferredStyle: .alert)
-
                 alert.addAction(UIAlertAction(title: NSLocalizedString("_settings_", comment: ""), style: .default, handler: { _ in
                     DispatchQueue.main.async {
                         if let settingsURL = URL(string: UIApplication.openSettingsURLString) {
@@ -86,9 +83,7 @@ class NCLoginQRCode: NSObject, QRCodeReaderViewControllerDelegate {
                 alert = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_qrcode_not_supported_", comment: ""), preferredStyle: .alert)
                 alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
             }
-
             delegate?.present(alert, animated: true, completion: nil)
-
             return false
         }
     }

+ 7 - 10
iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift

@@ -24,16 +24,15 @@
 import UIKit
 
 protocol NCCellProtocol {
-
     var fileAvatarImageView: UIImageView? { get }
     var fileObjectId: String? { get set }
     var filePreviewImageView: UIImageView? { get set }
+    var filePreviewImageBottom: NSLayoutConstraint? { get set }
     var fileUser: String? { get set }
     var fileTitleLabel: UILabel? { get set }
     var fileInfoLabel: UILabel? { get set }
     var fileSubinfoLabel: UILabel? { get set }
     var fileProgressView: UIProgressView? { get set }
-    var fileSelectImage: UIImageView? { get set }
     var fileStatusImage: UIImageView? { get set }
     var fileLocalImage: UIImageView? { get set }
     var fileFavoriteImage: UIImageView? { get set }
@@ -48,8 +47,7 @@ protocol NCCellProtocol {
     func setButtonMore(named: String, image: UIImage)
     func hideButtonShare(_ status: Bool)
     func hideButtonMore(_ status: Bool)
-    func selectMode(_ status: Bool)
-    func selected(_ status: Bool)
+    func selected(_ status: Bool, isEditMode: Bool)
     func setAccessibility(label: String, value: String)
     func setTags(tags: [String])
     func setIconOutlines()
@@ -68,6 +66,10 @@ extension NCCellProtocol {
         get { return nil }
         set {}
     }
+    var filePreviewImageBottom: NSLayoutConstraint? {
+        get { return nil }
+        set {}
+    }
     var fileTitleLabel: UILabel? {
         get { return nil }
         set {}
@@ -84,10 +86,6 @@ extension NCCellProtocol {
         get { return nil }
         set {}
     }
-    var fileSelectImage: UIImageView? {
-        get { return nil }
-        set {}
-    }
     var fileStatusImage: UIImageView? {
         get { return nil }
         set {}
@@ -119,8 +117,7 @@ extension NCCellProtocol {
     func setButtonMore(named: String, image: UIImage) {}
     func hideButtonShare(_ status: Bool) {}
     func hideButtonMore(_ status: Bool) {}
-    func selectMode(_ status: Bool) {}
-    func selected(_ status: Bool) {}
+    func selected(_ status: Bool, isEditMode: Bool) {}
     func setAccessibility(label: String, value: String) {}
     func setTags(tags: [String]) {}
     func setIconOutlines() {}

+ 18 - 66
iOSClient/Main/Collection Common/Cell/NCGridCell.swift

@@ -24,7 +24,6 @@
 import UIKit
 
 class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
-
     @IBOutlet weak var imageItem: UIImageView!
     @IBOutlet weak var imageSelect: UIImageView!
     @IBOutlet weak var imageStatus: UIImageView!
@@ -67,10 +66,6 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         get { return labelSubinfo }
         set { labelSubinfo = newValue }
     }
-    var fileSelectImage: UIImageView? {
-        get { return imageSelect }
-        set { imageSelect = newValue }
-    }
     var fileStatusImage: UIImageView? {
         get { return imageStatus }
         set { imageStatus = newValue }
@@ -86,8 +81,15 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 
     override func awakeFromNib() {
         super.awakeFromNib()
+        initCell()
+    }
+
+    override func prepareForReuse() {
+        super.prepareForReuse()
+        initCell()
+    }
 
-        // use entire cell as accessibility element
+    func initCell() {
         accessibilityHint = nil
         accessibilityLabel = nil
         accessibilityValue = nil
@@ -100,31 +102,20 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         imageVisualEffect.clipsToBounds = true
         imageVisualEffect.alpha = 0.5
 
+        imageSelect.isHidden = true
+        imageSelect.image = NCImageCache.images.checkedYes
+
         let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:)))
         longPressedGesture.minimumPressDuration = 0.5
         longPressedGesture.delegate = self
         longPressedGesture.delaysTouchesBegan = true
         self.addGestureRecognizer(longPressedGesture)
 
-        let longPressedGestureMore = UILongPressGestureRecognizer(target: self, action: #selector(longPressInsideMore(gestureRecognizer:)))
-        longPressedGestureMore.minimumPressDuration = 0.5
-        longPressedGestureMore.delegate = self
-        longPressedGestureMore.delaysTouchesBegan = true
-        buttonMore.addGestureRecognizer(longPressedGestureMore)
-
         labelTitle.text = ""
         labelInfo.text = ""
         labelSubinfo.text = ""
     }
 
-    override func prepareForReuse() {
-        super.prepareForReuse()
-        imageItem.backgroundColor = nil
-        accessibilityHint = nil
-        accessibilityLabel = nil
-        accessibilityValue = nil
-    }
-
     override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
         return nil
     }
@@ -133,10 +124,6 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         gridCellDelegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
     }
 
-    @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) {
-        gridCellDelegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
-    }
-
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
         gridCellDelegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
@@ -162,34 +149,16 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         buttonMore.isHidden = status
     }
 
-    func selectMode(_ status: Bool) {
-        if status {
-            imageSelect.isHidden = false
+    func selected(_ status: Bool, isEditMode: Bool) {
+        if isEditMode {
             buttonMore.isHidden = true
             accessibilityCustomActions = nil
         } else {
-            imageSelect.isHidden = true
-            imageVisualEffect.isHidden = true
             buttonMore.isHidden = false
             setA11yActions()
         }
-    }
-
-    func selected(_ status: Bool) {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isInTransfer else {
-            imageSelect.isHidden = true
-            imageVisualEffect.isHidden = true
-            return
-        }
         if status {
-            if traitCollection.userInterfaceStyle == .dark {
-                imageVisualEffect.effect = UIBlurEffect(style: .dark)
-                imageVisualEffect.backgroundColor = .black
-            } else {
-                imageVisualEffect.effect = UIBlurEffect(style: .extraLight)
-                imageVisualEffect.backgroundColor = .lightGray
-            }
-            imageSelect.image = NCImageCache.images.checkedYes
+            imageSelect.isHidden = false
             imageVisualEffect.isHidden = false
         } else {
             imageSelect.isHidden = true
@@ -198,7 +167,6 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     func writeInfoDateSize(date: NSDate, size: Int64) {
-
         let dateFormatter = DateFormatter()
         dateFormatter.dateStyle = .short
         dateFormatter.timeStyle = .none
@@ -224,28 +192,17 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 
 protocol NCGridCellDelegate: AnyObject {
     func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any)
-    func longPressMoreGridItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
     func longPressGridItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
 }
 
-// optional func
-extension NCGridCellDelegate {
-    func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) {}
-    func longPressMoreGridItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
-    func longPressGridItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
-}
-
 // MARK: - Grid Layout
 
 class NCGridLayout: UICollectionViewFlowLayout {
-
     var heightLabelPlusButton: CGFloat = 60
     var marginLeftRight: CGFloat = 10
-    var itemForLine: CGFloat = 3
+    var column: CGFloat = 3
     var itemWidthDefault: CGFloat = 140
 
-    // MARK: - View Life Cycle
-
     override init() {
         super.init()
 
@@ -265,20 +222,15 @@ class NCGridLayout: UICollectionViewFlowLayout {
     override var itemSize: CGSize {
         get {
             if let collectionView = collectionView {
-
                 if collectionView.frame.width < 400 {
-                    itemForLine = 3
+                    column = 3
                 } else {
-                    itemForLine = collectionView.frame.width / itemWidthDefault
+                    column = collectionView.frame.width / itemWidthDefault
                 }
-
-                let itemWidth: CGFloat = (collectionView.frame.width - marginLeftRight * 2 - marginLeftRight * (itemForLine - 1)) / itemForLine
+                let itemWidth: CGFloat = (collectionView.frame.width - marginLeftRight * 2 - marginLeftRight * (column - 1)) / column
                 let itemHeight: CGFloat = itemWidth + heightLabelPlusButton
-
                 return CGSize(width: itemWidth, height: itemHeight)
             }
-
-            // Default fallback
             return CGSize(width: itemWidthDefault, height: itemWidthDefault)
         }
         set {

+ 43 - 69
iOSClient/Main/Collection Common/Cell/NCGridCell.xib

@@ -18,43 +18,18 @@
                 <rect key="frame" x="0.0" y="0.0" width="416" height="529"/>
                 <autoresizingMask key="autoresizingMask"/>
                 <subviews>
+                    <visualEffectView hidden="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="LfR-yV-Xml" userLabel="VisualEffect">
+                        <rect key="frame" x="0.0" y="20" width="416" height="419"/>
+                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="h0r-Oi-xfW">
+                            <rect key="frame" x="0.0" y="0.0" width="416" height="419"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        </view>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <blurEffect style="extraLight"/>
+                    </visualEffectView>
                     <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5Ci-V1-hf5" userLabel="imageItem">
                         <rect key="frame" x="0.0" y="20" width="416" height="419"/>
                     </imageView>
-                    <stackView opaque="NO" contentMode="scaleToFill" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="VRH-IZ-lXO">
-                        <rect key="frame" x="5" y="447" width="406" height="36"/>
-                        <subviews>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="254" horizontalCompressionResistancePriority="756" verticalCompressionResistancePriority="759" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eU3-lY-fKr" userLabel="labelTitle">
-                                <rect key="frame" x="0.0" y="0.0" width="406" height="18"/>
-                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
-                                <nil key="textColor"/>
-                                <nil key="highlightedColor"/>
-                            </label>
-                        </subviews>
-                        <constraints>
-                            <constraint firstAttribute="height" constant="36" id="Soj-7j-hoM"/>
-                        </constraints>
-                    </stackView>
-                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2po-8g-XeS">
-                        <rect key="frame" x="25" y="490" width="366" height="12"/>
-                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
-                        <color key="textColor" systemColor="systemGrayColor"/>
-                        <nil key="highlightedColor"/>
-                    </label>
-                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="12P-pO-DHO" userLabel="Label Size">
-                        <rect key="frame" x="25" y="505" width="366" height="12"/>
-                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
-                        <color key="textColor" systemColor="systemGrayColor"/>
-                        <nil key="highlightedColor"/>
-                    </label>
-                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="star.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cgB-lu-t0g">
-                        <rect key="frame" x="388" y="21" width="26" height="26"/>
-                        <color key="tintColor" systemColor="tertiarySystemFillColor"/>
-                        <constraints>
-                            <constraint firstAttribute="width" constant="26" id="5aP-9X-SuS"/>
-                            <constraint firstAttribute="height" constant="26" id="dat-R5-5vy"/>
-                        </constraints>
-                    </imageView>
                     <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="star.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="AYs-f2-vve" userLabel="imageFavorite">
                         <rect key="frame" x="391" y="24.5" width="20" height="20"/>
                         <color key="tintColor" systemColor="separatorColor"/>
@@ -77,14 +52,6 @@
                             <constraint firstAttribute="width" constant="20" id="xLe-lb-N1p"/>
                         </constraints>
                     </imageView>
-                    <visualEffectView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W0L-HY-al1">
-                        <rect key="frame" x="0.0" y="20" width="416" height="419"/>
-                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="0m6-A2-SwD">
-                            <rect key="frame" x="0.0" y="0.0" width="416" height="419"/>
-                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        </view>
-                        <blurEffect style="extraLight"/>
-                    </visualEffectView>
                     <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DHy-Up-3Bh" userLabel="imageSelect">
                         <rect key="frame" x="5" y="25" width="25" height="25"/>
                         <constraints>
@@ -92,6 +59,32 @@
                             <constraint firstAttribute="width" constant="25" id="cZG-gx-gwt"/>
                         </constraints>
                     </imageView>
+                    <stackView opaque="NO" contentMode="scaleToFill" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="VRH-IZ-lXO">
+                        <rect key="frame" x="5" y="447" width="406" height="36"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="254" horizontalCompressionResistancePriority="756" verticalCompressionResistancePriority="759" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eU3-lY-fKr" userLabel="labelTitle">
+                                <rect key="frame" x="0.0" y="0.0" width="406" height="18"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="36" id="Soj-7j-hoM"/>
+                        </constraints>
+                    </stackView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2po-8g-XeS">
+                        <rect key="frame" x="25" y="490" width="366" height="12"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                        <color key="textColor" systemColor="systemGrayColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="12P-pO-DHO" userLabel="Label Size">
+                        <rect key="frame" x="25" y="505" width="366" height="12"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                        <color key="textColor" systemColor="systemGrayColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EJs-Ro-nbe" userLabel="buttonMoreGrid">
                         <rect key="frame" x="396" y="492" width="20" height="20"/>
                         <constraints>
@@ -109,51 +102,35 @@
             </view>
             <viewLayoutGuide key="safeArea" id="VXh-sQ-LeX"/>
             <constraints>
-                <constraint firstItem="cgB-lu-t0g" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="top" constant="1.5" id="0mO-lw-JH1"/>
                 <constraint firstItem="DHy-Up-3Bh" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="1T3-8p-uIW"/>
                 <constraint firstItem="AYs-f2-vve" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-25" id="3e3-0A-NSl"/>
-                <constraint firstItem="W0L-HY-al1" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="6tC-PK-fYX"/>
                 <constraint firstItem="2po-8g-XeS" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" constant="25" id="9JI-KS-d7J"/>
                 <constraint firstItem="DHy-Up-3Bh" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" constant="5" id="ESV-qE-tbO"/>
                 <constraint firstAttribute="trailing" secondItem="VRH-IZ-lXO" secondAttribute="trailing" constant="5" id="HAS-uF-AuS"/>
                 <constraint firstItem="2po-8g-XeS" firstAttribute="top" secondItem="VRH-IZ-lXO" secondAttribute="bottom" constant="7" id="OPl-xq-XOx"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" id="Ouj-ZD-UFm"/>
-                <constraint firstItem="W0L-HY-al1" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" id="Py6-0z-K3t"/>
                 <constraint firstItem="2po-8g-XeS" firstAttribute="centerX" secondItem="vf1-Kf-9uL" secondAttribute="centerX" id="TTW-HT-yEO"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="leading" secondItem="a0p-rj-jnV" secondAttribute="trailing" constant="-25" id="UtQ-6D-cYc"/>
-                <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="W0L-HY-al1" secondAttribute="trailing" id="VMW-0Y-aOH"/>
+                <constraint firstItem="LfR-yV-Xml" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="leading" id="XVl-U4-maN"/>
+                <constraint firstItem="LfR-yV-Xml" firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="ZT5-7T-eis"/>
                 <constraint firstItem="81G-wH-fjN" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="-25" id="aEb-vq-8sk"/>
                 <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="cHT-cP-NN6"/>
+                <constraint firstItem="LfR-yV-Xml" firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" id="cVe-LC-fxg"/>
                 <constraint firstItem="VRH-IZ-lXO" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="8" id="cj5-Ez-3cy"/>
                 <constraint firstItem="VRH-IZ-lXO" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" constant="5" id="dTF-dl-Awr"/>
                 <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="90" id="eEC-eB-alE"/>
+                <constraint firstItem="LfR-yV-Xml" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="top" id="eLX-1I-820"/>
                 <constraint firstItem="12P-pO-DHO" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" constant="25" id="fTQ-Ye-Xj2"/>
                 <constraint firstItem="12P-pO-DHO" firstAttribute="top" secondItem="2po-8g-XeS" secondAttribute="bottom" constant="3" id="hbc-KL-fRj"/>
-                <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="W0L-HY-al1" secondAttribute="bottom" constant="90" id="jI9-M1-Nl8"/>
                 <constraint firstItem="EJs-Ro-nbe" firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="l6Z-DK-OZi"/>
                 <constraint firstItem="81G-wH-fjN" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-25" id="nFH-Pc-end"/>
                 <constraint firstItem="EJs-Ro-nbe" firstAttribute="top" secondItem="VRH-IZ-lXO" secondAttribute="bottom" constant="9" id="o5n-Oi-Uh7"/>
-                <constraint firstItem="5Ci-V1-hf5" firstAttribute="trailing" secondItem="cgB-lu-t0g" secondAttribute="trailing" constant="2" id="q5r-Pz-Tnq"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="qT3-WD-iTV"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="AYs-f2-vve" secondAttribute="bottom" constant="-25" id="rLL-6g-ypv"/>
                 <constraint firstItem="a0p-rj-jnV" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="-25" id="upV-Ov-WWd"/>
                 <constraint firstItem="12P-pO-DHO" firstAttribute="centerX" secondItem="vf1-Kf-9uL" secondAttribute="centerX" id="xhm-Np-2Ua"/>
             </constraints>
             <size key="customSize" width="416" height="524"/>
-            <variation key="default">
-                <mask key="subviews">
-                    <exclude reference="cgB-lu-t0g"/>
-                    <exclude reference="W0L-HY-al1"/>
-                </mask>
-                <mask key="constraints">
-                    <exclude reference="0mO-lw-JH1"/>
-                    <exclude reference="6tC-PK-fYX"/>
-                    <exclude reference="Py6-0z-K3t"/>
-                    <exclude reference="VMW-0Y-aOH"/>
-                    <exclude reference="jI9-M1-Nl8"/>
-                    <exclude reference="q5r-Pz-Tnq"/>
-                </mask>
-            </variation>
             <connections>
                 <outlet property="buttonMore" destination="EJs-Ro-nbe" id="BdI-ay-LuX"/>
                 <outlet property="imageFavorite" destination="AYs-f2-vve" id="UeH-R7-bZr"/>
@@ -161,7 +138,7 @@
                 <outlet property="imageLocal" destination="81G-wH-fjN" id="bqj-wQ-CBV"/>
                 <outlet property="imageSelect" destination="DHy-Up-3Bh" id="mo9-rP-P4I"/>
                 <outlet property="imageStatus" destination="a0p-rj-jnV" id="6Dg-tf-evd"/>
-                <outlet property="imageVisualEffect" destination="W0L-HY-al1" id="WDW-2d-Npa"/>
+                <outlet property="imageVisualEffect" destination="LfR-yV-Xml" id="KSf-fc-rLn"/>
                 <outlet property="labelInfo" destination="2po-8g-XeS" id="FJ4-wI-9cW"/>
                 <outlet property="labelSubinfo" destination="12P-pO-DHO" id="K6d-7r-FGh"/>
                 <outlet property="labelTitle" destination="eU3-lY-fKr" id="0P7-yM-Asb"/>
@@ -173,16 +150,13 @@
         <image name="ellipsis" catalog="system" width="128" height="37"/>
         <image name="star.fill" catalog="system" width="128" height="116"/>
         <systemColor name="separatorColor">
-            <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
+            <color red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
         <systemColor name="systemGray2Color">
-            <color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <color red="0.68235294120000001" green="0.68235294120000001" blue="0.69803921570000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
         <systemColor name="systemGrayColor">
-            <color red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-        <systemColor name="tertiarySystemFillColor">
-            <color red="0.46274509803921571" green="0.46274509803921571" blue="0.50196078431372548" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
+            <color red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
     </resources>
 </document>

+ 6 - 50
iOSClient/Main/Collection Common/Cell/NCListCell.swift

@@ -24,7 +24,6 @@
 import UIKit
 
 class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
-
     @IBOutlet weak var imageItem: UIImageView!
     @IBOutlet weak var imageSelect: UIImageView!
     @IBOutlet weak var imageStatus: UIImageView!
@@ -86,10 +85,6 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         get { return progressView }
         set { progressView = newValue }
     }
-    var fileSelectImage: UIImageView? {
-        get { return imageSelect }
-        set { imageSelect = newValue }
-    }
     var fileStatusImage: UIImageView? {
         get { return imageStatus }
         set { imageStatus = newValue }
@@ -137,12 +132,6 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         longPressedGesture.delaysTouchesBegan = true
         self.addGestureRecognizer(longPressedGesture)
 
-        let longPressedGestureMore = UILongPressGestureRecognizer(target: self, action: #selector(longPressInsideMore(gestureRecognizer:)))
-        longPressedGestureMore.minimumPressDuration = 0.5
-        longPressedGestureMore.delegate = self
-        longPressedGestureMore.delaysTouchesBegan = true
-        buttonMore.addGestureRecognizer(longPressedGestureMore)
-
         separator.backgroundColor = .separator
         separatorHeightConstraint.constant = 0.5
 
@@ -182,10 +171,6 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         listCellDelegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
     }
 
-    @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) {
-        listCellDelegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
-    }
-
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
         listCellDelegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
@@ -235,8 +220,8 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         separator.isHidden = status
     }
 
-    func selectMode(_ status: Bool) {
-        if status {
+    func selected(_ status: Bool, isEditMode: Bool) {
+        if isEditMode {
             imageItemLeftConstraint.constant = 45
             imageSelect.isHidden = false
             imageShared.isHidden = true
@@ -254,29 +239,13 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
             backgroundView = nil
             setA11yActions()
         }
-    }
-
-    func selected(_ status: Bool) {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isInTransfer else {
-            backgroundView = nil
-            separator.isHidden = false
-            return
-        }
         if status {
-            var blurEffect: UIVisualEffect?
             var blurEffectView: UIView?
-            imageSelect.image = NCImageCache.images.checkedYes
-            if traitCollection.userInterfaceStyle == .dark {
-                blurEffect = UIBlurEffect(style: .dark)
-                blurEffectView = UIVisualEffectView(effect: blurEffect)
-                blurEffectView?.backgroundColor = .black
-            } else {
-                blurEffect = UIBlurEffect(style: .extraLight)
-                blurEffectView = UIVisualEffectView(effect: blurEffect)
-                blurEffectView?.backgroundColor = .lightGray
-            }
+            blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
+            blurEffectView?.backgroundColor = .lightGray
             blurEffectView?.frame = self.bounds
             blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+            imageSelect.image = NCImageCache.images.checkedYes
             backgroundView = blurEffectView
             separator.isHidden = true
         } else {
@@ -284,6 +253,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
             backgroundView = nil
             separator.isHidden = false
         }
+
     }
 
     func writeInfoDateSize(date: NSDate, size: Int64) {
@@ -338,26 +308,14 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 protocol NCListCellDelegate: AnyObject {
     func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any)
     func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any)
-    func longPressMoreListItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
     func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
 }
 
-// optional func
-extension NCListCellDelegate {
-    func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) {}
-    func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) {}
-    func longPressMoreListItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
-    func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
-}
-
 // MARK: - List Layout
 
 class NCListLayout: UICollectionViewFlowLayout {
-
     var itemHeight: CGFloat = 60
 
-    // MARK: - View Life Cycle
-
     override init() {
         super.init()
 
@@ -380,8 +338,6 @@ class NCListLayout: UICollectionViewFlowLayout {
                 let itemWidth: CGFloat = collectionView.frame.width
                 return CGSize(width: itemWidth, height: self.itemHeight)
             }
-
-            // Default fallback
             return CGSize(width: 100, height: 100)
         }
         set {

+ 9 - 9
iOSClient/Main/Collection Common/Cell/NCListCell.xib

@@ -18,6 +18,13 @@
                 <rect key="frame" x="0.0" y="0.0" width="669" height="148"/>
                 <autoresizingMask key="autoresizingMask"/>
                 <subviews>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="AyA-hP-r6w" userLabel="imageSelect">
+                        <rect key="frame" x="10" y="61.666666666666657" width="25" height="25"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="25" id="bIF-gu-6Jj"/>
+                            <constraint firstAttribute="height" constant="25" id="nJa-oj-gcQ"/>
+                        </constraints>
+                    </imageView>
                     <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="w2m-Vw-hpd" userLabel="ImageItem">
                         <rect key="frame" x="57" y="54" width="40" height="40"/>
                         <constraints>
@@ -47,13 +54,6 @@
                             <constraint firstAttribute="height" constant="15" id="mPH-zc-eH5"/>
                         </constraints>
                     </imageView>
-                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="AyA-hP-r6w" userLabel="imageSelect">
-                        <rect key="frame" x="10" y="61.666666666666657" width="25" height="25"/>
-                        <constraints>
-                            <constraint firstAttribute="width" constant="25" id="bIF-gu-6Jj"/>
-                            <constraint firstAttribute="height" constant="25" id="nJa-oj-gcQ"/>
-                        </constraints>
-                    </imageView>
                     <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="H4E-G2-C1H" userLabel="imageLocal">
                         <rect key="frame" x="87" y="84" width="15" height="15"/>
                         <constraints>
@@ -267,10 +267,10 @@
             <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
         </systemColor>
         <systemColor name="systemGray5Color">
-            <color red="0.89803921568627454" green="0.89803921568627454" blue="0.91764705882352937" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <color red="0.8980392157" green="0.8980392157" blue="0.91764705879999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
         <systemColor name="systemGrayColor">
-            <color red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <color red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
     </resources>
 </document>

+ 145 - 0
iOSClient/Main/Collection Common/Cell/NCPhotoCell.swift

@@ -0,0 +1,145 @@
+//
+//  NCPhotoCell.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 13/07/2024.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+
+class NCPhotoCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
+
+    @IBOutlet weak var imageItem: UIImageView!
+    @IBOutlet weak var imageSelect: UIImageView!
+    @IBOutlet weak var imageStatus: UIImageView!
+    @IBOutlet weak var imageVisualEffect: UIVisualEffectView!
+    @IBOutlet weak var labelTitle: UILabel!
+    @IBOutlet weak var imageItemBottom: NSLayoutConstraint!
+
+    var objectId = ""
+    var indexPath = IndexPath()
+    private var user = ""
+
+    weak var photoCellDelegate: NCPhotoCellDelegate?
+    var namedButtonMore = ""
+
+    var fileObjectId: String? {
+        get { return objectId }
+        set { objectId = newValue ?? "" }
+    }
+    var filePreviewImageView: UIImageView? {
+        get { return imageItem }
+        set { imageItem = newValue }
+    }
+    var filePreviewImageBottom: NSLayoutConstraint? {
+        get { return imageItemBottom }
+        set { imageItemBottom = newValue}
+    }
+    var fileUser: String? {
+        get { return user }
+        set { user = newValue ?? "" }
+    }
+    var fileTitleLabel: UILabel? {
+        get { return labelTitle }
+        set { labelTitle = newValue }
+    }
+    var fileInfoLabel: UILabel? {
+        get { return nil }
+        set { }
+    }
+    var fileSubinfoLabel: UILabel? {
+        get { return nil }
+        set { }
+    }
+    var fileStatusImage: UIImageView? {
+        get { return imageStatus }
+        set { imageStatus = newValue }
+    }
+    var fileLocalImage: UIImageView? {
+        get { return nil }
+        set { }
+    }
+    var fileFavoriteImage: UIImageView? {
+        get { return nil }
+        set { }
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        initCell()
+    }
+
+    override func prepareForReuse() {
+        super.prepareForReuse()
+        initCell()
+    }
+
+    func initCell() {
+        accessibilityHint = nil
+        accessibilityLabel = nil
+        accessibilityValue = nil
+
+        imageVisualEffect.clipsToBounds = true
+        imageVisualEffect.alpha = 0.5
+
+        imageSelect.isHidden = true
+        imageSelect.image = NCImageCache.images.checkedYes
+
+        let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:)))
+        longPressedGesture.minimumPressDuration = 0.5
+        longPressedGesture.delegate = self
+        longPressedGesture.delaysTouchesBegan = true
+        self.addGestureRecognizer(longPressedGesture)
+    }
+
+    override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
+        return nil
+    }
+
+    @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
+        photoCellDelegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
+    }
+
+    func selected(_ status: Bool, isEditMode: Bool) {
+        if status {
+            imageSelect.isHidden = false
+            imageVisualEffect.isHidden = false
+        } else {
+            imageSelect.isHidden = true
+            imageVisualEffect.isHidden = true
+        }
+    }
+
+    func setAccessibility(label: String, value: String) {
+        accessibilityLabel = label
+        accessibilityValue = value
+    }
+
+    func setIconOutlines() {
+        if imageStatus.image != nil {
+            imageStatus.makeCircularBackground(withColor: .systemBackground)
+        } else {
+            imageStatus.backgroundColor = .clear
+        }
+    }
+}
+
+protocol NCPhotoCellDelegate: AnyObject {
+    func longPressGridItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
+}

+ 82 - 0
iOSClient/Main/Collection Common/Cell/NCPhotoCell.xib

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="dark"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="photoCell" id="vf1-Kf-9uL" customClass="NCPhotoCell" customModule="Nextcloud" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="400" height="400"/>
+            <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
+                <rect key="frame" x="0.0" y="0.0" width="400" height="400"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5Ci-V1-hf5" userLabel="imageItem">
+                        <rect key="frame" x="0.0" y="0.0" width="400" height="400"/>
+                    </imageView>
+                    <visualEffectView hidden="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="bQY-LA-65a" userLabel="VisualEffect">
+                        <rect key="frame" x="0.0" y="0.0" width="400" height="400"/>
+                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="kdN-fK-b6F">
+                            <rect key="frame" x="0.0" y="0.0" width="400" height="400"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        </view>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <blurEffect style="extraLight"/>
+                    </visualEffectView>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="a0p-rj-jnV" userLabel="imageStatus">
+                        <rect key="frame" x="5" y="383" width="12" height="12"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="12" id="gq1-0a-eLC"/>
+                            <constraint firstAttribute="width" constant="12" id="uJE-4b-Qt7"/>
+                        </constraints>
+                    </imageView>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DHy-Up-3Bh" userLabel="imageSelect">
+                        <rect key="frame" x="5" y="5" width="15" height="15"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="15" id="SoZ-J3-98x"/>
+                            <constraint firstAttribute="width" constant="15" id="cZG-gx-gwt"/>
+                        </constraints>
+                    </imageView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="254" horizontalCompressionResistancePriority="756" verticalCompressionResistancePriority="759" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AKw-Xc-akO" userLabel="labelTitle">
+                        <rect key="frame" x="10" y="382" width="380" height="16"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                        <nil key="textColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstItem="DHy-Up-3Bh" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" constant="5" id="1T3-8p-uIW"/>
+                <constraint firstItem="AKw-Xc-akO" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" constant="10" id="42P-uq-qMZ"/>
+                <constraint firstAttribute="trailing" secondItem="AKw-Xc-akO" secondAttribute="trailing" constant="10" id="78q-3N-aQm"/>
+                <constraint firstAttribute="bottom" secondItem="a0p-rj-jnV" secondAttribute="bottom" constant="5" id="9Qc-jT-y8N"/>
+                <constraint firstItem="bQY-LA-65a" firstAttribute="top" secondItem="vf1-Kf-9uL" secondAttribute="top" id="A1R-of-6Ra"/>
+                <constraint firstItem="DHy-Up-3Bh" firstAttribute="top" secondItem="vf1-Kf-9uL" secondAttribute="top" constant="5" id="ESV-qE-tbO"/>
+                <constraint firstAttribute="bottom" secondItem="bQY-LA-65a" secondAttribute="bottom" id="ET1-N7-SQl"/>
+                <constraint firstAttribute="bottom" secondItem="AKw-Xc-akO" secondAttribute="bottom" constant="2" id="JkG-ef-Hn5"/>
+                <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="vf1-Kf-9uL" secondAttribute="top" id="Ouj-ZD-UFm"/>
+                <constraint firstItem="bQY-LA-65a" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" id="Q47-7m-Z2O"/>
+                <constraint firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="cHT-cP-NN6"/>
+                <constraint firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" id="eEC-eB-alE"/>
+                <constraint firstAttribute="trailing" secondItem="bQY-LA-65a" secondAttribute="trailing" id="q1v-Cn-HZf"/>
+                <constraint firstItem="5Ci-V1-hf5" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" id="qT3-WD-iTV"/>
+                <constraint firstItem="a0p-rj-jnV" firstAttribute="leading" secondItem="vf1-Kf-9uL" secondAttribute="leading" constant="5" id="vjD-yv-602"/>
+            </constraints>
+            <size key="customSize" width="416" height="524"/>
+            <connections>
+                <outlet property="imageItem" destination="5Ci-V1-hf5" id="xky-Nw-NUb"/>
+                <outlet property="imageItemBottom" destination="eEC-eB-alE" id="FyV-lA-B3t"/>
+                <outlet property="imageSelect" destination="DHy-Up-3Bh" id="mo9-rP-P4I"/>
+                <outlet property="imageStatus" destination="a0p-rj-jnV" id="6Dg-tf-evd"/>
+                <outlet property="imageVisualEffect" destination="bQY-LA-65a" id="NMK-Ke-CSu"/>
+                <outlet property="labelTitle" destination="AKw-Xc-akO" id="f1O-Qv-Riz"/>
+            </connections>
+            <point key="canvasLocation" x="233.59999999999999" y="241.97901049475263"/>
+        </collectionViewCell>
+    </objects>
+</document>

+ 528 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift

@@ -0,0 +1,528 @@
+//
+//  NCCollectionViewCommon+CollectionViewDataSource.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 02/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+import NextcloudKit
+
+extension NCCollectionViewCommon: UICollectionViewDataSource {
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return dataSource.numberOfSections()
+    }
+
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return dataSource.numberOfItemsInSection(section)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath),
+              let cell = (cell as? NCCellProtocol) else { return }
+        let existsIcon = utilityFileSystem.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag)
+
+        func downloadAvatar(fileName: String, user: String, dispalyName: String?) {
+            if let image = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) {
+                cell.fileAvatarImageView?.contentMode = .scaleAspectFill
+                cell.fileAvatarImageView?.image = image
+            } else {
+                NCNetworking.shared.downloadAvatar(user: user, dispalyName: dispalyName, fileName: fileName, cell: cell, view: collectionView)
+            }
+        }
+        /// CONTENT MODE
+        cell.filePreviewImageView?.layer.borderWidth = 0
+        if existsIcon {
+            cell.filePreviewImageView?.contentMode = .scaleAspectFill
+        } else {
+            cell.filePreviewImageView?.contentMode = .scaleAspectFit
+        }
+        cell.fileAvatarImageView?.contentMode = .center
+        /// THUMBNAIL
+        if !metadata.directory {
+            if metadata.hasPreviewBorder {
+                cell.filePreviewImageView?.layer.borderWidth = 0.2
+                cell.filePreviewImageView?.layer.borderColor = UIColor.lightGray.cgColor
+            }
+            if metadata.name == NCGlobal.shared.appName {
+                if layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio || layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare {
+                    if let image = NCImageCache.shared.getPreviewImageCache(ocId: metadata.ocId, etag: metadata.etag) {
+                        cell.filePreviewImageView?.image = image
+                    } else if let image = UIImage(contentsOfFile: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)) {
+                        cell.filePreviewImageView?.image = image
+                        NCImageCache.shared.addPreviewImageCache(metadata: metadata, image: image)
+                    }
+                } else {
+                    if let image = NCImageCache.shared.getIconImageCache(ocId: metadata.ocId, etag: metadata.etag) {
+                        cell.filePreviewImageView?.image = image
+                    } else if metadata.hasPreview {
+                        cell.filePreviewImageView?.image = utility.getIcon(metadata: metadata)
+                    }
+                }
+                if cell.filePreviewImageView?.image == nil {
+                    if metadata.iconName.isEmpty {
+                        cell.filePreviewImageView?.image = NCImageCache.images.file
+                    } else {
+                        cell.filePreviewImageView?.image = utility.loadImage(named: metadata.iconName, useTypeIconFile: true)
+                    }
+                    if metadata.hasPreview && metadata.status == NCGlobal.shared.metadataStatusNormal && !existsIcon {
+                        for case let operation as NCCollectionViewDownloadThumbnail in NCNetworking.shared.downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId { return }
+                        NCNetworking.shared.downloadThumbnailQueue.addOperation(NCCollectionViewDownloadThumbnail(metadata: metadata, cell: cell, collectionView: collectionView))
+                    }
+                }
+            } else {
+                /// APP NAME - UNIFIED SEARCH
+                switch metadata.iconName {
+                case let str where str.contains("contacts"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconContacts
+                case let str where str.contains("conversation"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconTalk
+                case let str where str.contains("calendar"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconCalendar
+                case let str where str.contains("deck"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconDeck
+                case let str where str.contains("mail"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconMail
+                case let str where str.contains("talk"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconTalk
+                case let str where str.contains("confirm"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconConfirm
+                case let str where str.contains("pages"):
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconPages
+                default:
+                    cell.filePreviewImageView?.image = NCImageCache.images.iconFile
+                }
+                if !metadata.iconUrl.isEmpty {
+                    if let ownerId = getAvatarFromIconUrl(metadata: metadata) {
+                        let fileName = metadata.userBaseUrl + "-" + ownerId + ".png"
+                        downloadAvatar(fileName: fileName, user: ownerId, dispalyName: nil)
+                    }
+                }
+            }
+        }
+        /// AVATAR
+        if !metadata.ownerId.isEmpty,
+           metadata.ownerId != appDelegate.userId,
+           appDelegate.account == metadata.account {
+            let fileName = metadata.userBaseUrl + "-" + metadata.ownerId + ".png"
+            downloadAvatar(fileName: fileName, user: metadata.ownerId, dispalyName: metadata.ownerDisplayName)
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        if !collectionView.indexPathsForVisibleItems.contains(indexPath) {
+            guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return }
+            for case let operation as NCCollectionViewDownloadThumbnail in NCNetworking.shared.downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId {
+                operation.cancel()
+            }
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        var cell: NCCellProtocol & UICollectionViewCell
+        let permissions = NCPermissions()
+        var isShare = false
+        var isMounted = false
+        var a11yValues: [String] = []
+
+        // LAYOUT PHOTO
+        if layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio || layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare {
+            guard let photoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as? NCPhotoCell else { return NCPhotoCell() }
+            photoCell.photoCellDelegate = self
+            cell = photoCell
+        } else if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+        // LAYOUT GRID
+            guard let gridCell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell else { return NCGridCell() }
+            gridCell.gridCellDelegate = self
+            cell = gridCell
+        } else {
+        // LAYOUT LIST
+            guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else { return NCListCell() }
+            listCell.listCellDelegate = self
+            cell = listCell
+        }
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return cell }
+
+        defer {
+            if NCGlobal.shared.disableSharesView || !metadata.isSharable() {
+                cell.hideButtonShare(true)
+            }
+        }
+
+        if metadataFolder != nil {
+            isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared)
+            isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted)
+        }
+
+        cell.fileStatusImage?.image = nil
+        cell.fileLocalImage?.image = nil
+        cell.fileFavoriteImage?.image = nil
+        cell.fileSharedImage?.image = nil
+        cell.fileMoreImage?.image = nil
+        cell.filePreviewImageView?.image = nil
+        cell.filePreviewImageView?.backgroundColor = nil
+        cell.fileObjectId = metadata.ocId
+        cell.indexPath = indexPath
+        cell.fileUser = metadata.ownerId
+        cell.fileProgressView?.isHidden = true
+        cell.fileProgressView?.progress = 0.0
+        cell.hideButtonShare(false)
+        cell.hideButtonMore(false)
+        cell.titleInfoTrailingDefault()
+
+        if isSearchingMode {
+            cell.fileTitleLabel?.text = metadata.fileName
+            cell.fileTitleLabel?.lineBreakMode = .byTruncatingTail
+            if metadata.name == NCGlobal.shared.appName {
+                cell.fileInfoLabel?.text = NSLocalizedString("_in_", comment: "") + " " + utilityFileSystem.getPath(path: metadata.path, user: metadata.user)
+            } else {
+                cell.fileInfoLabel?.text = metadata.subline
+            }
+            cell.fileSubinfoLabel?.isHidden = true
+        } else {
+            cell.fileSubinfoLabel?.isHidden = false
+            cell.fileTitleLabel?.text = metadata.fileNameView
+            cell.fileTitleLabel?.lineBreakMode = .byTruncatingMiddle
+            cell.writeInfoDateSize(date: metadata.date, size: metadata.size)
+        }
+
+        if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading {
+            cell.fileProgressView?.isHidden = false
+        }
+
+        // Accessibility [shared]
+        if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account {
+            a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName)
+        }
+
+        if metadata.directory {
+            let tableDirectory = NCManageDatabase.shared.getTableDirectory(ocId: metadata.ocId)
+            if metadata.e2eEncrypted {
+                cell.filePreviewImageView?.image = NCImageCache.images.folderEncrypted
+            } else if isShare {
+                cell.filePreviewImageView?.image = NCImageCache.images.folderSharedWithMe
+            } else if !metadata.shareType.isEmpty {
+                metadata.shareType.contains(3) ?
+                (cell.filePreviewImageView?.image = NCImageCache.images.folderPublic) :
+                (cell.filePreviewImageView?.image = NCImageCache.images.folderSharedWithMe)
+            } else if !metadata.shareType.isEmpty && metadata.shareType.contains(3) {
+                cell.filePreviewImageView?.image = NCImageCache.images.folderPublic
+            } else if metadata.mountType == "group" {
+                cell.filePreviewImageView?.image = NCImageCache.images.folderGroup
+            } else if isMounted {
+                cell.filePreviewImageView?.image = NCImageCache.images.folderExternal
+            } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
+                cell.filePreviewImageView?.image = NCImageCache.images.folderAutomaticUpload
+            } else {
+                cell.filePreviewImageView?.image = NCImageCache.images.folder
+            }
+
+            // Local image: offline
+            if let tableDirectory, tableDirectory.offline {
+                cell.fileLocalImage?.image = NCImageCache.images.offlineFlag
+            }
+
+            // color folder
+            cell.filePreviewImageView?.image = cell.filePreviewImageView?.image?.colorizeFolder(metadata: metadata, tableDirectory: tableDirectory)
+        } else {
+            let tableLocalFile = NCManageDatabase.shared.getResultsTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))?.first
+            // image local
+            if let tableLocalFile, tableLocalFile.offline {
+                a11yValues.append(NSLocalizedString("_offline_", comment: ""))
+                cell.fileLocalImage?.image = NCImageCache.images.offlineFlag
+            } else if utilityFileSystem.fileProviderStorageExists(metadata) {
+                cell.fileLocalImage?.image = NCImageCache.images.local
+            }
+        }
+
+        // image Favorite
+        if metadata.favorite {
+            cell.fileFavoriteImage?.image = NCImageCache.images.favorite
+            a11yValues.append(NSLocalizedString("_favorite_short_", comment: ""))
+        }
+
+        // Share image
+        if isShare {
+            cell.fileSharedImage?.image = NCImageCache.images.shared
+        } else if !metadata.shareType.isEmpty {
+            metadata.shareType.contains(3) ?
+            (cell.fileSharedImage?.image = NCImageCache.images.shareByLink) :
+            (cell.fileSharedImage?.image = NCImageCache.images.shared)
+        } else {
+            cell.fileSharedImage?.image = NCImageCache.images.canShare
+        }
+        if appDelegate.account != metadata.account {
+            cell.fileSharedImage?.image = NCImageCache.images.shared
+        }
+
+        // Button More
+        if metadata.isInTransfer || metadata.isWaitingTransfer {
+            cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop)
+        } else if metadata.lock == true {
+            cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCImageCache.images.buttonMoreLock)
+            a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName))
+        } else {
+            cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore)
+        }
+
+        // Write status on Label Info
+        switch metadata.status {
+        case NCGlobal.shared.metadataStatusWaitDownload:
+            cell.fileInfoLabel?.text = utilityFileSystem.transformedSize(metadata.size)
+            cell.fileSubinfoLabel?.text = infoLabelsSeparator + NSLocalizedString("_status_wait_download_", comment: "")
+        case NCGlobal.shared.metadataStatusDownloading:
+            cell.fileInfoLabel?.text = utilityFileSystem.transformedSize(metadata.size)
+            cell.fileSubinfoLabel?.text = infoLabelsSeparator + "↓ …"
+        case NCGlobal.shared.metadataStatusWaitUpload:
+            cell.fileInfoLabel?.text = utilityFileSystem.transformedSize(metadata.size)
+            cell.fileSubinfoLabel?.text = infoLabelsSeparator + NSLocalizedString("_status_wait_upload_", comment: "")
+            cell.fileLocalImage?.image = nil
+        case NCGlobal.shared.metadataStatusUploading:
+            cell.fileInfoLabel?.text = utilityFileSystem.transformedSize(metadata.size)
+            cell.fileSubinfoLabel?.text = infoLabelsSeparator + "↑ …"
+            cell.fileLocalImage?.image = nil
+        case NCGlobal.shared.metadataStatusUploadError:
+            if metadata.sessionError.isEmpty {
+                cell.fileInfoLabel?.text = NSLocalizedString("_status_wait_upload_", comment: "")
+            } else {
+                cell.fileInfoLabel?.text = NSLocalizedString("_status_wait_upload_", comment: "") + " " + metadata.sessionError
+            }
+        default:
+            break
+        }
+
+        // Live Photo
+        if metadata.isLivePhoto {
+            cell.fileStatusImage?.image = NCImageCache.images.livePhoto
+            a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: ""))
+        }
+
+        // URL
+        if metadata.classFile == NKCommon.TypeClassFile.url.rawValue {
+            cell.fileLocalImage?.image = nil
+            cell.hideButtonShare(true)
+            cell.hideButtonMore(true)
+            if let ownerId = getAvatarFromIconUrl(metadata: metadata) {
+                cell.fileUser = ownerId
+            }
+        }
+
+        // Separator
+        if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 || isSearchingMode {
+            cell.cellSeparatorView?.isHidden = true
+        } else {
+            cell.cellSeparatorView?.isHidden = false
+        }
+
+        // Edit mode
+        if selectOcId.contains(metadata.ocId) {
+            cell.selected(true, isEditMode: isEditMode)
+            a11yValues.append(NSLocalizedString("_selected_", comment: ""))
+        } else {
+            cell.selected(false, isEditMode: isEditMode)
+        }
+
+        // Accessibility
+        cell.setAccessibility(label: metadata.fileNameView + ", " + (cell.fileInfoLabel?.text ?? "") + (cell.fileSubinfoLabel?.text ?? ""), value: a11yValues.joined(separator: ", "))
+
+        // Color string find in search
+        cell.fileTitleLabel?.textColor = NCBrandColor.shared.textColor
+        cell.fileTitleLabel?.font = .systemFont(ofSize: 15)
+
+        if isSearchingMode, let literalSearch = self.literalSearch, let title = cell.fileTitleLabel?.text {
+            let longestWordRange = (title.lowercased() as NSString).range(of: literalSearch)
+            let attributedString = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)])
+            attributedString.setAttributes([NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 15), NSAttributedString.Key.foregroundColor: UIColor.systemBlue], range: longestWordRange)
+            cell.fileTitleLabel?.attributedText = attributedString
+        }
+
+        // Layout photo
+        if layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio || layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare {
+            if metadata.directory {
+                cell.filePreviewImageBottom?.constant = 10
+                cell.fileTitleLabel?.text = metadata.fileNameView
+            } else {
+                cell.filePreviewImageBottom?.constant = 0
+                cell.fileTitleLabel?.text = ""
+            }
+        }
+
+        // TAGS
+        cell.setTags(tags: Array(metadata.tags))
+
+        // Hide buttons
+        if metadata.name != NCGlobal.shared.appName {
+            cell.titleInfoTrailingFull()
+            cell.hideButtonShare(true)
+            cell.hideButtonMore(true)
+        }
+
+        cell.setIconOutlines()
+        return cell
+    }
+
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+
+        if kind == UICollectionView.elementKindSectionHeader || kind == mediaSectionHeader {
+
+            if dataSource.getMetadataSourceForAllSections().isEmpty {
+
+                guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() }
+                self.sectionFirstHeaderEmptyData = header
+                header.delegate = self
+
+                if !isSearchingMode, headerMenuTransferView, let ocId = NCNetworking.shared.transferInForegorund?.ocId {
+                    let text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand)
+                    header.setViewTransfer(isHidden: false, ocId: ocId, text: text, progress: NCNetworking.shared.transferInForegorund?.progress)
+                } else {
+                    header.setViewTransfer(isHidden: true)
+                }
+
+                if isSearchingMode {
+                    header.emptyImage.image = utility.loadImage(named: "magnifyingglass", colors: [NCBrandColor.shared.brandElement])
+                    if self.dataSourceTask?.state == .running {
+                        header.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
+                    } else {
+                        header.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "")
+                    }
+                    header.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "")
+                } else if self.dataSourceTask?.state == .running {
+                    header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.brandElement])
+                    header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
+                    header.emptyDescription.text = ""
+                } else {
+                    if serverUrl.isEmpty {
+                        header.emptyImage.image = emptyImage
+                        header.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "")
+                        header.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "")
+                    } else {
+                        header.emptyImage.image = NCImageCache.images.folder
+                        header.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "")
+                        header.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "")
+                    }
+                }
+
+                return header
+
+            } else if indexPath.section == 0 {
+
+                guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() }
+                let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section)
+                self.sectionFirstHeader = header
+                header.delegate = self
+
+                if !isSearchingMode, headerMenuTransferView, let ocId = NCNetworking.shared.transferInForegorund?.ocId {
+                    let text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand)
+                    header.setViewTransfer(isHidden: false, ocId: ocId, text: text, progress: NCNetworking.shared.transferInForegorund?.progress)
+                } else {
+                    header.setViewTransfer(isHidden: true)
+                }
+
+                header.setRichWorkspaceHeight(heightHeaderRichWorkspace)
+                header.setRichWorkspaceText(richWorkspaceText)
+
+                header.setSectionHeight(heightHeaderSection)
+                if heightHeaderSection == 0 {
+                    header.labelSection.text = ""
+                } else {
+                    header.labelSection.text = self.dataSource.getSectionValueLocalization(indexPath: indexPath)
+                }
+                header.labelSection.textColor = NCBrandColor.shared.textColor
+
+                return header
+
+            } else {
+
+                guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as? NCSectionHeader else { return NCSectionHeader() }
+
+                header.labelSection.text = self.dataSource.getSectionValueLocalization(indexPath: indexPath)
+                header.labelSection.textColor = NCBrandColor.shared.textColor
+
+                return header
+            }
+
+        } else {
+
+            guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter else { return NCSectionFooter() }
+            let sections = dataSource.numberOfSections()
+            let section = indexPath.section
+            let metadataForSection = self.dataSource.getMetadataForSection(indexPath.section)
+            let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false
+            let metadatasCount: Int = metadataForSection?.metadatas.count ?? 0
+            let unifiedSearchInProgress = metadataForSection?.unifiedSearchInProgress ?? false
+
+            footer.delegate = self
+            footer.metadataForSection = metadataForSection
+
+            footer.setTitleLabel("")
+            footer.setButtonText(NSLocalizedString("_show_more_results_", comment: ""))
+            footer.separatorIsHidden(true)
+            footer.buttonIsHidden(true)
+            footer.hideActivityIndicatorSection()
+
+            if isSearchingMode {
+                if sections > 1 && section != sections - 1 {
+                    footer.separatorIsHidden(false)
+                }
+
+                // If the number of entries(metadatas) is lower than the cursor, then there are no more entries.
+                // The blind spot in this is when the number of entries is the same as the cursor. If so, we don't have a way of knowing if there are no more entries.
+                // This is as good as it gets for determining last page without server-side flag.
+                let isLastPage = (metadatasCount < metadataForSection?.lastSearchResult?.cursor ?? 0) || metadataForSection?.lastSearchResult?.entries.isEmpty == true
+
+                if isSearchingMode && isPaginated && metadatasCount > 0 && !isLastPage {
+                    footer.buttonIsHidden(false)
+                }
+
+                if unifiedSearchInProgress {
+                    footer.showActivityIndicatorSection()
+                }
+            } else {
+                if sections == 1 || section == sections - 1 {
+                    let info = dataSource.getFooterInformationAllMetadatas()
+                    footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size)
+                } else {
+                    footer.separatorIsHidden(false)
+                }
+            }
+
+            return footer
+        }
+    }
+
+    // MARK: -
+
+    func getAvatarFromIconUrl(metadata: tableMetadata) -> String? {
+        var ownerId: String?
+
+        if metadata.iconUrl.contains("http") && metadata.iconUrl.contains("avatar") {
+            let splitIconUrl = metadata.iconUrl.components(separatedBy: "/")
+            var found: Bool = false
+            for item in splitIconUrl {
+                if found {
+                    ownerId = item
+                    break
+                }
+                if item == "avatar" { found = true}
+            }
+        }
+        return ownerId
+    }
+}

+ 115 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift

@@ -0,0 +1,115 @@
+//
+//  NCCollectionViewCommon+CollectionViewDelegate.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 02/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+import NextcloudKit
+
+extension NCCollectionViewCommon: UICollectionViewDelegate {
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath), !metadata.isInvalidated else { return }
+
+        if isEditMode {
+            if let index = selectOcId.firstIndex(of: metadata.ocId) {
+                selectOcId.remove(at: index)
+            } else {
+                selectOcId.append(metadata.ocId)
+            }
+            collectionView.reloadItems(at: [indexPath])
+            tabBarSelect.update(selectOcId: selectOcId, metadatas: getSelectedMetadatas(), userId: appDelegate.userId)
+            return
+        }
+
+        if metadata.e2eEncrypted {
+            if NCGlobal.shared.capabilityE2EEEnabled {
+                if !NCKeychain().isEndToEndEnabled(account: appDelegate.account) {
+                    let e2ee = NCEndToEndInitialize()
+                    e2ee.delegate = self
+                    e2ee.initEndToEndEncryption(viewController: self.tabBarController, metadata: metadata)
+                    return
+                }
+            } else {
+                NCContentPresenter().showInfo(error: NKError(errorCode: NCGlobal.shared.errorE2EENotEnabled, errorDescription: "_e2e_server_disabled_"))
+                return
+            }
+        }
+
+        if metadata.directory {
+            pushMetadata(metadata)
+        } else {
+            let imageIcon = UIImage(contentsOfFile: utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
+
+            if !metadata.isDirectoryE2EE && (metadata.isImage || metadata.isAudioOrVideo) {
+                var metadatas: [tableMetadata] = []
+                for metadata in dataSource.getMetadataSourceForAllSections() {
+                    if metadata.isImage || metadata.isAudioOrVideo {
+                        metadatas.append(metadata)
+                    }
+                }
+                NCViewer().view(viewController: self, metadata: metadata, metadatas: metadatas, imageIcon: imageIcon)
+                return
+            } else if metadata.isAvailableEditorView || utilityFileSystem.fileProviderStorageExists(metadata) {
+                NCViewer().view(viewController: self, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon)
+            } else if NextcloudKit.shared.isNetworkReachable(),
+                      let metadata = NCManageDatabase.shared.setMetadatasSessionInWaitDownload(metadatas: [metadata],
+                                                                                               session: NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload,
+                                                                                               selector: NCGlobal.shared.selectorLoadFileView,
+                                                                                               sceneIdentifier: (self.tabBarController as? NCMainTabBarController)?.sceneIdentifier) {
+                NCNetworking.shared.download(metadata: metadata, withNotificationProgressTask: true)
+            } else {
+                let error = NKError(errorCode: NCGlobal.shared.errorOffline, errorDescription: "_go_online_")
+                NCContentPresenter().showInfo(error: error)
+            }
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return nil }
+        if isEditMode || metadata.classFile == NKCommon.TypeClassFile.url.rawValue { return nil }
+        let identifier = indexPath as NSCopying
+        var image: UIImage?
+        let cell = collectionView.cellForItem(at: indexPath)
+
+        if cell is NCListCell {
+            image = (cell as? NCListCell)?.imageItem.image
+        } else if cell is NCGridCell {
+            image = (cell as? NCGridCell)?.imageItem.image
+        } else if cell is NCPhotoCell {
+            image = (cell as? NCPhotoCell)?.imageItem.image
+        }
+
+        return UIContextMenuConfiguration(identifier: identifier, previewProvider: {
+            return NCViewerProviderContextMenu(metadata: metadata, image: image)
+        }, actionProvider: { _ in
+            return NCContextMenu().viewMenu(ocId: metadata.ocId, viewController: self, image: image)
+        })
+    }
+
+    func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
+        animator.addCompletion {
+            if let indexPath = configuration.identifier as? IndexPath {
+                self.collectionView(collectionView, didSelectItemAt: indexPath)
+            }
+        }
+    }
+}

+ 35 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift

@@ -0,0 +1,35 @@
+//
+//  NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 19/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+
+extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+        return sizeForHeaderInSection(section: section)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
+       return sizeForFooterInSection(section: section)
+    }
+}

+ 8 - 7
iOSClient/Main/Collection Common/NCCollectionViewCommon+DragDrop.swift

@@ -29,28 +29,29 @@ import JGProgressHUD
 
 extension NCCollectionViewCommon: UICollectionViewDragDelegate {
     func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
-
         if isEditMode {
             return NCDragDrop().performDrag(selectOcId: selectOcId)
         } else if let metadata = dataSource.cellForItemAt(indexPath: indexPath) {
             return NCDragDrop().performDrag(metadata: metadata)
         }
-
         return []
     }
 
     func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
         let previewParameters = UIDragPreviewParameters()
 
-        if layoutForView?.layout == NCGlobal.shared.layoutList, let cell = collectionView.cellForItem(at: indexPath) as? NCListCell {
+        if layoutForView?.layout == NCGlobal.shared.layoutList,
+            let cell = collectionView.cellForItem(at: indexPath) as? NCListCell {
             let width = (collectionView.frame.width / 3) * 2
             previewParameters.visiblePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: width, height: cell.frame.height), cornerRadius: 10)
             return previewParameters
         } else if let cell = collectionView.cellForItem(at: indexPath) as? NCGridCell {
             previewParameters.visiblePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height - 40), cornerRadius: 10)
             return previewParameters
+        } else if let cell = collectionView.cellForItem(at: indexPath) as? NCPhotoCell {
+            previewParameters.visiblePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height), cornerRadius: 10)
+            return previewParameters
         }
-
         return nil
     }
 }
@@ -64,6 +65,7 @@ extension NCCollectionViewCommon: UICollectionViewDropDelegate {
 
     func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
         var destinationMetadata: tableMetadata?
+
         if let destinationIndexPath {
             destinationMetadata = dataSource.cellForItemAt(indexPath: destinationIndexPath)
         }
@@ -102,7 +104,6 @@ extension NCCollectionViewCommon: UICollectionViewDropDelegate {
                 }
             }
         }
-
         return UICollectionViewDropProposal(operation: .copy)
     }
 
@@ -138,20 +139,20 @@ extension NCCollectionViewCommon: UICollectionViewDropDelegate {
     @objc func copyMenuFile() {
         guard let sourceMetadatas = DragDropHover.shared.sourceMetadatas else { return }
         var serverUrl: String = self.serverUrl
+
         if let destinationMetadata = DragDropHover.shared.destinationMetadata, destinationMetadata.directory {
             serverUrl = destinationMetadata.serverUrl + "/" + destinationMetadata.fileName
         }
-
         NCDragDrop().copyFile(metadatas: sourceMetadatas, serverUrl: serverUrl)
     }
 
     @objc func moveMenuFile() {
         guard let sourceMetadatas = DragDropHover.shared.sourceMetadatas else { return }
         var serverUrl: String = self.serverUrl
+
         if let destinationMetadata = DragDropHover.shared.destinationMetadata, destinationMetadata.directory {
             serverUrl = destinationMetadata.serverUrl + "/" + destinationMetadata.fileName
         }
-
         NCDragDrop().moveFile(metadatas: sourceMetadatas, serverUrl: serverUrl)
     }
 }

+ 70 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+EasyTipView.swift

@@ -0,0 +1,70 @@
+//
+//  NCCollectionViewCommon+EasyTipView.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 20/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+import EasyTipView
+
+extension NCCollectionViewCommon: EasyTipViewDelegate {
+    func showTip() {
+        guard !appDelegate.account.isEmpty,
+              self is NCFiles,
+              self.view.window != nil,
+              !NCBrandOptions.shared.disable_multiaccount,
+              self.serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId),
+              let view = self.navigationItem.leftBarButtonItem?.customView,
+              !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest) else { return }
+        var preferences = EasyTipView.Preferences()
+
+        preferences.drawing.foregroundColor = .white
+        preferences.drawing.backgroundColor = NCBrandColor.shared.nextcloud
+        preferences.drawing.textAlignment = .left
+        preferences.drawing.arrowPosition = .top
+        preferences.drawing.cornerRadius = 10
+
+        preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: 100)
+        preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -100)
+        preferences.animating.showInitialAlpha = 0
+        preferences.animating.showDuration = 1.5
+        preferences.animating.dismissDuration = 1.5
+
+        if appDelegate.tipView == nil {
+            appDelegate.tipView = EasyTipView(text: NSLocalizedString("_tip_accountrequest_", comment: ""), preferences: preferences, delegate: self)
+            appDelegate.tipView?.show(forView: view)
+        }
+    }
+
+    func easyTipViewDidTap(_ tipView: EasyTipView) {
+        NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest)
+    }
+
+    func easyTipViewDidDismiss(_ tipView: EasyTipView) { }
+
+    func dismissTip() {
+        if !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest) {
+            NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest)
+        }
+        appDelegate.tipView?.dismiss()
+        appDelegate.tipView = nil
+    }
+}

+ 33 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+EndToEndInitialize.swift

@@ -0,0 +1,33 @@
+//
+//  NCCollectionViewCommon+EndToEndInitialize.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 20/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+
+extension NCCollectionViewCommon: NCEndToEndInitializeDelegate {
+    func endToEndInitializeSuccess(metadata: tableMetadata?) {
+        if let metadata {
+            pushMetadata(metadata)
+        }
+    }
+}

+ 78 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+MediaLayout.swift

@@ -0,0 +1,78 @@
+//
+//  NCCollectionViewCommon+MediaLayout.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 13/07/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import UIKit
+import NextcloudKit
+
+extension NCCollectionViewCommon: NCMediaLayoutDelegate {
+    func getColumnCount() -> Int {
+        if let column = self.layoutForView?.columnPhoto, column > 0 {
+            return column
+        }
+        self.layoutForView?.columnPhoto = 3
+        return 3
+    }
+
+    func getLayout() -> String? {
+        return NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: NCGlobal.shared.layoutViewFiles, serverUrl: serverUrl)?.layout
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, heightForHeaderInSection section: Int) -> Float {
+        return Float(sizeForHeaderInSection(section: section).height)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, heightForFooterInSection section: Int) -> Float {
+        return Float(sizeForFooterInSection(section: section).height)
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForSection section: Int) -> UIEdgeInsets {
+        return .zero
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForHeaderInSection section: Int) -> UIEdgeInsets {
+        return .zero
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, insetForFooterInSection section: Int) -> UIEdgeInsets {
+        return .zero
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, minimumInteritemSpacingForSection section: Int) -> Float {
+        return 1.0
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath, columnCount: Int, typeLayout: String) -> CGSize {
+        let size = CGSize(width: collectionView.frame.width / CGFloat(columnCount), height: collectionView.frame.width / CGFloat(columnCount))
+        if typeLayout == NCGlobal.shared.layoutPhotoRatio {
+            let metadata = self.dataSource.metadatas[indexPath.row]
+
+            if metadata.imageSize != CGSize.zero {
+                return metadata.imageSize
+            } else if let size = NCImageCache.shared.getPreviewSizeCache(ocId: metadata.ocId, etag: metadata.etag) {
+                return size
+            }
+        }
+        return size
+    }
+}

+ 1 - 42
iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBarDelegate.swift → iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift

@@ -1,5 +1,5 @@
 //
-//  NCCollectionViewCommon+SelectTabBarDelegate.swift
+//  NCCollectionViewCommon+SelectTabBar.swift
 //  Nextcloud
 //
 //  Created by Milen on 01.03.24.
@@ -26,40 +26,6 @@ import Foundation
 import NextcloudKit
 
 extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate {
-    func onListSelected() {
-        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
-            layoutForView?.layout = NCGlobal.shared.layoutList
-            NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
-            self.groupByField = "name"
-            if self.dataSource.groupByField != self.groupByField {
-                self.dataSource.changeGroupByField(self.groupByField)
-            }
-
-            self.collectionView.reloadData()
-            self.collectionView.collectionViewLayout.invalidateLayout()
-            self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) {_ in self.isTransitioning = false }
-        }
-    }
-
-    func onGridSelected() {
-        if layoutForView?.layout == NCGlobal.shared.layoutList {
-            layoutForView?.layout = NCGlobal.shared.layoutGrid
-            NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
-            if isSearchingMode {
-                self.groupByField = "name"
-            } else {
-                self.groupByField = "classFile"
-            }
-            if self.dataSource.groupByField != self.groupByField {
-                self.dataSource.changeGroupByField(self.groupByField)
-            }
-
-            self.collectionView.reloadData()
-            self.collectionView.collectionViewLayout.invalidateLayout()
-            self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) {_ in self.isTransitioning = false }
-        }
-    }
-
     func selectAll() {
         if !selectOcId.isEmpty, dataSource.getMetadataSourceForAllSections().count == selectOcId.count {
             selectOcId = []
@@ -79,7 +45,6 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate {
 
         if canDeleteServer {
             let copyMetadatas = metadatas
-
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in
                 Task {
                     var error = NKError()
@@ -159,12 +124,6 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate {
         setEditMode(false)
     }
 
-    func saveLayout(_ layoutForView: NCDBLayoutForView) {
-        NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
-        setNavigationRightItems()
-    }
-
     func getSelectedMetadatas() -> [tableMetadata] {
         var selectedMetadatas: [tableMetadata] = []
         for ocId in selectOcId {

File diff suppressed because it is too large
+ 235 - 673
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift


+ 1 - 8
iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift

@@ -25,10 +25,9 @@ import Foundation
 import UIKit
 import Queuer
 import NextcloudKit
-import Realm
+import RealmSwift
 
 class NCCollectionViewDownloadThumbnail: ConcurrentOperation {
-
     var metadata: tableMetadata
     var cell: NCCellProtocol?
     var collectionView: UICollectionView?
@@ -46,7 +45,6 @@ class NCCollectionViewDownloadThumbnail: ConcurrentOperation {
 
     override func start() {
         guard !isCancelled else { return self.finish() }
-
         var etagResource: String?
         let sizePreview = NCUtility().getSizePreview(width: metadata.width, height: metadata.height)
 
@@ -62,7 +60,6 @@ class NCCollectionViewDownloadThumbnail: ConcurrentOperation {
                                             sizeIcon: NCGlobal.shared.sizeIcon,
                                             etag: etagResource,
                                             options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, imageIcon, _, etag, error in
-
             if error == .success, let imageIcon {
                 NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag)
                 DispatchQueue.main.async {
@@ -81,10 +78,6 @@ class NCCollectionViewDownloadThumbnail: ConcurrentOperation {
                         self.collectionView?.reloadData()
                     }
                 }
-                let fileSizeIcon = self.utilityFileSystem.getFileSize(filePath: self.fileNameIconLocalPath)
-                if NCImageCache.shared.hasIconImageEnoughSize(fileSizeIcon) {
-                    NCImageCache.shared.setIconImage(ocId: self.metadata.ocId, etag: self.metadata.etag, image: imageIcon)
-                }
             }
             self.finish()
         }

+ 1 - 3
iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift

@@ -24,10 +24,9 @@
 import Foundation
 import Queuer
 import NextcloudKit
-import Realm
+import RealmSwift
 
 class NCCollectionViewUnifiedSearch: ConcurrentOperation {
-
     var collectionViewCommon: NCCollectionViewCommon
     var metadatas: [tableMetadata]
     var searchResult: NKSearchResult
@@ -48,7 +47,6 @@ class NCCollectionViewUnifiedSearch: ConcurrentOperation {
     }
 
     override func start() {
-
         guard !isCancelled else { return self.finish() }
 
         self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult)

+ 10 - 142
iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.swift → iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift

@@ -1,5 +1,5 @@
 //
-//  NCSectionHeaderFooter.swift
+//  NCSectionFirstHeader.swift
 //  Nextcloud
 //
 //  Created by Marino Faggiana on 09/10/2018.
@@ -24,7 +24,12 @@
 import UIKit
 import MarkdownKit
 
-class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate {
+protocol NCSectionFirstHeaderDelegate: AnyObject {
+    func tapButtonTransfer(_ sender: Any)
+    func tapRichWorkspace(_ sender: Any)
+}
+
+class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegate {
 
     @IBOutlet weak var buttonTransfer: UIButton!
     @IBOutlet weak var imageButtonTransfer: UIImageView!
@@ -42,7 +47,7 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint!
     @IBOutlet weak var transferSeparatorBottomHeightConstraint: NSLayoutConstraint!
 
-    weak var delegate: NCSectionHeaderMenuDelegate?
+    weak var delegate: NCSectionFirstHeaderDelegate?
     let utility = NCUtility()
     private var markdownParser = MarkdownParser()
     private var richWorkspaceText: String?
@@ -103,7 +108,6 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     // MARK: - RichWorkspace
 
     func setRichWorkspaceHeight(_ size: CGFloat) {
-
         viewRichWorkspaceHeightConstraint.constant = size
         if size == 0 {
             viewRichWorkspace.isHidden = true
@@ -113,7 +117,6 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     }
 
     func setInterfaceColor() {
-
         if traitCollection.userInterfaceStyle == .dark {
             gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor]
         } else {
@@ -133,7 +136,6 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     // MARK: - Transfer
 
     func setViewTransfer(isHidden: Bool, ocId: String? = nil, text: String? = nil, progress: Float? = nil) {
-
         labelTransfer.text = text
         viewTransfer.isHidden = isHidden
         progressTransfer.progress = 0
@@ -144,14 +146,13 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
             var image: UIImage?
             if let ocId,
                let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                image = utility.createFilePreviewImage(ocId: metadata.ocId, etag: metadata.etag, fileNameView: metadata.fileNameView, classFile: metadata.classFile, status: metadata.status, createPreviewMedia: true)?.darken()
+                image = utility.getIcon(metadata: metadata)?.darken()
                 if image == nil {
                     image = utility.loadImage(named: metadata.iconName, useTypeIconFile: true)
                     buttonTransfer.backgroundColor = .lightGray
                 } else {
                     buttonTransfer.backgroundColor = .clear
                 }
-                buttonTransfer.setImage(image, for: .normal)
             }
             viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer
             if let progress {
@@ -163,8 +164,8 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     // MARK: - Section
 
     func setSectionHeight(_ size: CGFloat) {
-
         viewSectionHeightConstraint.constant = size
+
         if size == 0 {
             viewSection.isHidden = true
         } else {
@@ -183,139 +184,6 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     }
 }
 
-protocol NCSectionHeaderMenuDelegate: AnyObject {
-    func tapButtonTransfer(_ sender: Any)
-    func tapRichWorkspace(_ sender: Any)
-}
-
-// optional func
-extension NCSectionHeaderMenuDelegate {
-    func tapButtonTransfer(_ sender: Any) {}
-    func tapRichWorkspace(_ sender: Any) {}
-}
-
-class NCSectionHeader: UICollectionReusableView {
-
-    @IBOutlet weak var labelSection: UILabel!
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        self.backgroundColor = UIColor.clear
-        self.labelSection.text = ""
-    }
-}
-
-class NCSectionFooter: UICollectionReusableView, NCSectionFooterDelegate {
-
-    @IBOutlet weak var buttonSection: UIButton!
-    @IBOutlet weak var activityIndicatorSection: UIActivityIndicatorView!
-    @IBOutlet weak var labelSection: UILabel!
-    @IBOutlet weak var separator: UIView!
-    @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
-    @IBOutlet weak var buttonSectionHeightConstraint: NSLayoutConstraint!
-
-    weak var delegate: NCSectionFooterDelegate?
-    var metadataForSection: NCMetadataForSection?
-    let utilityFileSystem = NCUtilityFileSystem()
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        self.backgroundColor = .clear
-        labelSection.textColor = NCBrandColor.shared.textColor2
-        labelSection.text = ""
-
-        separator.backgroundColor = .separator
-        separatorHeightConstraint.constant = 0.5
-
-        buttonIsHidden(true)
-        activityIndicatorSection.isHidden = true
-        activityIndicatorSection.color = NCBrandColor.shared.textColor
-    }
-
-    func setTitleLabel(directories: Int, files: Int, size: Int64) {
-
-        var foldersText = ""
-        var filesText = ""
-
-        if directories > 1 {
-            foldersText = "\(directories) " + NSLocalizedString("_folders_", comment: "")
-        } else if directories == 1 {
-            foldersText = "1 " + NSLocalizedString("_folder_", comment: "")
-        }
-
-        if files > 1 {
-            filesText = "\(files) " + NSLocalizedString("_files_", comment: "") + " • " + utilityFileSystem.transformedSize(size)
-        } else if files == 1 {
-            filesText = "1 " + NSLocalizedString("_file_", comment: "") + " • " + utilityFileSystem.transformedSize(size)
-        }
-
-        if foldersText.isEmpty {
-            labelSection.text = filesText
-        } else if filesText.isEmpty {
-            labelSection.text = foldersText
-        } else {
-            labelSection.text = foldersText + " • " + filesText
-        }
-    }
-
-    func setTitleLabel(_ text: String) {
-
-        labelSection.text = text
-    }
-
-    func setButtonText(_ text: String) {
-
-        buttonSection.setTitle(text, for: .normal)
-    }
-
-    func separatorIsHidden(_ isHidden: Bool) {
-
-        separator.isHidden = isHidden
-    }
-
-    func buttonIsHidden(_ isHidden: Bool) {
-
-        buttonSection.isHidden = isHidden
-        if isHidden {
-            buttonSectionHeightConstraint.constant = 0
-        } else {
-            buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton
-        }
-    }
-
-    func showActivityIndicatorSection() {
-
-        buttonSection.isHidden = true
-        buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton
-
-        activityIndicatorSection.isHidden = false
-        activityIndicatorSection.startAnimating()
-    }
-
-    func hideActivityIndicatorSection() {
-
-        activityIndicatorSection.stopAnimating()
-        activityIndicatorSection.isHidden = true
-    }
-
-    // MARK: - Action
-
-    @IBAction func touchUpInsideButton(_ sender: Any) {
-        delegate?.tapButtonSection(sender, metadataForSection: metadataForSection)
-    }
-}
-
-protocol NCSectionFooterDelegate: AnyObject {
-    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?)
-}
-
-// optional func
-extension NCSectionFooterDelegate {
-    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {}
-}
-
 // https://stackoverflow.com/questions/16278463/darken-an-uiimage
 public extension UIImage {
 

+ 1 - 1
iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderMenu.xib → iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib

@@ -11,7 +11,7 @@
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
-        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeaderMenu" id="tys-A2-nDX" customClass="NCSectionHeaderMenu" customModule="Nextcloud" customModuleProvider="target">
+        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFirstHeader" id="tys-A2-nDX" customClass="NCSectionFirstHeader" customModule="Nextcloud" customModuleProvider="target">
             <rect key="frame" x="0.0" y="0.0" width="574" height="438"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>

+ 5 - 7
iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderEmptyData.swift → iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift

@@ -1,5 +1,5 @@
 //
-//  NCSectionHeaderEmptyData.swift
+//  NCSectionFirstHeaderEmptyData.swift
 //  Nextcloud
 //
 //  Created by Marino Faggiana on 09/10/2018.
@@ -24,11 +24,11 @@
 import UIKit
 import MarkdownKit
 
-protocol NCSectionHeaderEmptyDataDelegate: AnyObject {
+protocol NCSectionFirstHeaderEmptyDataDelegate: AnyObject {
     func tapButtonTransfer(_ sender: Any)
 }
 
-class NCSectionHeaderEmptyData: UICollectionReusableView {
+class NCSectionFirstHeaderEmptyData: UICollectionReusableView {
 
     @IBOutlet weak var viewTransfer: UIView!
     @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint!
@@ -43,7 +43,7 @@ class NCSectionHeaderEmptyData: UICollectionReusableView {
     @IBOutlet weak var emptyTitle: UILabel!
     @IBOutlet weak var emptyDescription: UILabel!
 
-    weak var delegate: NCSectionHeaderEmptyDataDelegate?
+    weak var delegate: NCSectionFirstHeaderEmptyDataDelegate?
 
     override func awakeFromNib() {
         super.awakeFromNib()
@@ -84,7 +84,6 @@ class NCSectionHeaderEmptyData: UICollectionReusableView {
     // MARK: - Transfer
 
     func setViewTransfer(isHidden: Bool, ocId: String? = nil, text: String? = nil, progress: Float? = nil) {
-
         labelTransfer.text = text
         viewTransfer.isHidden = isHidden
         progressTransfer.progress = 0
@@ -95,14 +94,13 @@ class NCSectionHeaderEmptyData: UICollectionReusableView {
             var image: UIImage?
             if let ocId,
                let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                image = NCUtility().createFilePreviewImage(ocId: metadata.ocId, etag: metadata.etag, fileNameView: metadata.fileNameView, classFile: metadata.classFile, status: metadata.status, createPreviewMedia: true)?.darken()
+                image = NCUtility().getIcon(metadata: metadata)?.darken()
                 if image == nil {
                     image = NCUtility().loadImage(named: metadata.iconName, useTypeIconFile: true)
                     buttonTransfer.backgroundColor = .lightGray
                 } else {
                     buttonTransfer.backgroundColor = .clear
                 }
-                buttonTransfer.setImage(image, for: .normal)
             }
             viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer
             if let progress {

+ 2 - 2
iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeaderEmptyData.xib → iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.xib

@@ -10,7 +10,7 @@
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
-        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeaderEmptyData" id="tys-A2-nDX" customClass="NCSectionHeaderEmptyData" customModule="Nextcloud" customModuleProvider="target">
+        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFirstHeaderEmptyData" id="tys-A2-nDX" customClass="NCSectionFirstHeaderEmptyData" customModule="Nextcloud" customModuleProvider="target">
             <rect key="frame" x="0.0" y="0.0" width="656" height="623"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
@@ -121,7 +121,7 @@
     <resources>
         <image name="stop.circle" catalog="system" width="128" height="123"/>
         <systemColor name="systemGrayColor">
-            <color red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <color red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
     </resources>
 </document>

Some files were not shown because too many files changed in this diff