فهرست منبع

Merge pull request #2055 from nextcloud/improvedSearch

Improved search
Marino Faggiana 2 سال پیش
والد
کامیت
3fdd2735d9

+ 1 - 1
Cartfile.resolved

@@ -1,3 +1,3 @@
-github "krzyzanowskim/OpenSSL" "1.1.1300"
+github "krzyzanowskim/OpenSSL" "1.1.1501"
 github "marinofaggiana/KTVHTTPCache" "2.0.5"
 github "marinofaggiana/TOPasscodeViewController" "a1b9d1058b2648e636525fc368e220a0cfddb42a"

+ 12 - 5
Nextcloud.xcodeproj/project.pbxproj

@@ -2091,7 +2091,6 @@
 				TargetAttributes = {
 					2C33C47E23E2C475005F963B = {
 						CreatedOnToolsVersion = 11.3.1;
-						DevelopmentTeam = 6JLRKY9ZV7;
 						ProvisioningStyle = Automatic;
 					};
 					AF8ED1F82757821000B8DBC4 = {
@@ -2099,7 +2098,6 @@
 						TestTargetID = F77B0DEB1D118A16002130FE;
 					};
 					F71459B41D12E3B700CAFEEC = {
-						DevelopmentTeam = 6JLRKY9ZV7;
 						LastSwiftMigration = 1020;
 						SystemCapabilities = {
 							com.apple.ApplicationGroups.iOS = {
@@ -2112,7 +2110,6 @@
 					};
 					F771E3CF20E2392D00AFB62D = {
 						CreatedOnToolsVersion = 9.4.1;
-						DevelopmentTeam = 6JLRKY9ZV7;
 						LastSwiftMigration = 1020;
 						ProvisioningStyle = Automatic;
 					};
@@ -2815,6 +2812,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2832,6 +2830,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2850,6 +2849,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_HARDENED_RUNTIME = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
@@ -2863,6 +2863,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_HARDENED_RUNTIME = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
@@ -2875,6 +2876,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2892,6 +2894,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2909,6 +2912,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2926,6 +2930,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2944,6 +2949,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2956,6 +2962,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2990,7 +2997,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 2;
+				CURRENT_PROJECT_VERSION = 4;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -3052,7 +3059,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 2;
+				CURRENT_PROJECT_VERSION = 4;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;

+ 3 - 0
iOSClient/AppDelegate.swift

@@ -40,6 +40,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     @objc var userId: String = ""
     @objc var password: String = ""
 
+    var deletePasswordSession: Bool = false
     var activeAppConfigView: NCAppConfigView?
     var activeFiles: NCFiles?
     var activeFileViewInFolder: NCFileViewInFolder?
@@ -189,6 +190,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // L' applicazione entrerà in primo piano (attivo sempre)
     func applicationDidBecomeActive(_ application: UIApplication) {
 
+        self.deletePasswordSession = false
+
         if !NCAskAuthorization.shared.isRequesting {
             // Privacy
             hidePrivacyProtectionWindow()

+ 3 - 3
iOSClient/Brand/iOSClient.plist

@@ -45,7 +45,7 @@
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<true/>
 	<key>ITSEncryptionExportComplianceCode</key>
-	<string>3b2bb0b1-fa12-43cb-a78f-0f7e1afd33df</string>
+	<string>8e9f9874-938e-460b-a9be-f82cb3393971</string>
 	<key>LSApplicationQueriesSchemes</key>
 	<array>
 		<string>nextcloudtalk</string>
@@ -75,6 +75,8 @@
 	<string>Photo library access is required to upload your photos and videos to your cloud.</string>
 	<key>NSPhotoLibraryUsageDescription</key>
 	<string>Photo library access is required to upload your photos and videos to your cloud.</string>
+	<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
+	<true/>
 	<key>UIAppFonts</key>
 	<array>
 		<string>Inconsolata-Light.ttf</string>
@@ -85,8 +87,6 @@
 		<string>Inconsolata-ExtraBold.ttf</string>
 		<string>Inconsolata-Black.ttf</string>
 	</array>
-	<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
-	<true/>
 	<key>UIBackgroundModes</key>
 	<array>
 		<string>audio</string>

+ 110 - 76
iOSClient/Data/NCDataSource.swift

@@ -58,14 +58,11 @@ class NCDataSource: NSObject {
         self.favoriteOnTop = favoriteOnTop ?? true
         self.filterLivePhoto = filterLivePhoto ?? true
         self.groupByField = groupByField
+        // unified search
         self.providers = providers
         self.searchResults = searchResults
 
         createSections()
-
-        for sectionValue in self.sectionsValue {
-            createMetadataForSection(sectionValue: sectionValue)
-        }
     }
 
     // MARK: -
@@ -75,10 +72,26 @@ class NCDataSource: NSObject {
         self.metadatasSource.removeAll()
         self.metadatasForSection.removeAll()
         self.sectionsValue.removeAll()
+        self.providers = nil
+        self.searchResults = nil
+        self.shares.removeAll()
+        self.localFiles.removeAll()
+    }
+
+    func addSection(metadatas: [tableMetadata], searchResult: NCCSearchResult?) {
+
+        self.metadatasSource.append(contentsOf: metadatas)
+
+        if let searchResult = searchResult {
+            self.searchResults?.append(searchResult)
+        }
+
+        createSections()
     }
 
     internal func createSections() {
 
+        // get all Section
         for metadata in metadatasSource {
             // skipped livePhoto
             if filterLivePhoto && metadata.livePhoto && metadata.ext == "mov" {
@@ -90,6 +103,7 @@ class NCDataSource: NSObject {
             }
         }
 
+        // Unified search
         if let providers = self.providers, !providers.isEmpty {
             var sectionsDictionary: [String:Int] = [:]
             for section in self.sectionsValue {
@@ -107,7 +121,10 @@ class NCDataSource: NSObject {
                     self.sectionsValue.append(section.key)
                 }
             }
+
         } else {
+
+        // normal
             let directory = NSLocalizedString("directory", comment: "").lowercased().firstUppercased
             self.sectionsValue = self.sectionsValue.sorted {
                 if directoryOnTop && $0 == directory {
@@ -122,6 +139,13 @@ class NCDataSource: NSObject {
                 }
             }
         }
+
+        for sectionValue in self.sectionsValue {
+            if !existsMetadataForSection(sectionValue: sectionValue) {
+                print("DATASOURCE: create metadata for section: " + sectionValue)
+                createMetadataForSection(sectionValue: sectionValue)
+            }
+        }
     }
 
     internal func createMetadataForSection(sectionValue: String) {
@@ -135,7 +159,7 @@ class NCDataSource: NSObject {
                                                             metadatas: metadatas,
                                                             shares: self.shares,
                                                             localFiles: self.localFiles,
-                                                            searchResult: searchResult,
+                                                            lastSearchResult: searchResult,
                                                             sort: self.sort,
                                                             ascending: self.ascending,
                                                             directoryOnTop: self.directoryOnTop,
@@ -146,10 +170,30 @@ class NCDataSource: NSObject {
 
     // MARK: -
 
+    func appendMetadatasToSection(_ metadatas: [tableMetadata], metadataForSection: NCMetadataForSection, lastSearchResult: NCCSearchResult) -> [IndexPath] {
+        
+        guard let sectionIndex =  getSectionIndex(metadataForSection.sectionValue) else { return [] }
+        var indexPaths: [IndexPath] = []
+
+        self.metadatasSource.append(contentsOf: metadatas)
+        metadataForSection.metadatas.append(contentsOf: metadatas)
+        metadataForSection.lastSearchResult = lastSearchResult
+        metadataForSection.createMetadatas()
+
+        for metadata in metadatas {
+            if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
+                indexPaths.append(IndexPath(row: rowIndex, section: sectionIndex))
+            }
+        }
+
+        return indexPaths
+    }
+
     @discardableResult
     func addMetadata(_ metadata: tableMetadata) -> (indexPath: IndexPath?, sameSections: Bool) {
 
         let numberOfSections = self.numberOfSections()
+        let sectionValue = getSectionValue(metadata: metadata)
 
         // ADD metadatasSource
         if let rowIndex = self.metadatasSource.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
@@ -159,14 +203,13 @@ class NCDataSource: NSObject {
         }
 
         // ADD metadataForSection
-        if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == self.getSectionValue(metadata: metadata) }) {
-            let metadataForSection = metadatasForSection[sectionIndex]
+        if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionIndex) {
             if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
                 metadataForSection.metadatas[rowIndex] = metadata
                 return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
             } else {
                 metadataForSection.metadatas.append(metadata)
-                metadataForSection.createMetadatasForSection()
+                metadataForSection.createMetadatas()
                 if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
                     return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
                 }
@@ -175,11 +218,8 @@ class NCDataSource: NSObject {
         } else {
             // NEW section
             createSections()
-            let sectionValue = getSectionValue(metadata: metadata)
-            createMetadataForSection(sectionValue: sectionValue)
             // get IndexPath of new section
-            if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == sectionValue }) {
-                let metadataForSection = metadatasForSection[sectionIndex]
+            if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionIndex) {
                 if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
                     return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
                 }
@@ -193,37 +233,32 @@ class NCDataSource: NSObject {
 
         let numberOfSections = self.numberOfSections()
         var indexPathReturn: IndexPath?
-        var removeMetadataForSection = false
         var sectionValue = ""
 
         // DELETE metadataForSection (IMPORTANT FIRST)
         let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId)
-        if let indexPath = indexPath, let metadataForSection = metadataForSection {
+        if let indexPath = indexPath, let metadataForSection = metadataForSection, indexPath.row < metadataForSection.metadatas.count {
             metadataForSection.metadatas.remove(at: indexPath.row)
             if metadataForSection.metadatas.count == 0 {
+                // REMOVE sectionsValue / metadatasForSection
                 sectionValue = metadataForSection.sectionValue
-                removeMetadataForSection = true
+                if let sectionIndex = getSectionIndex(sectionValue) {
+                    self.sectionsValue.remove(at: sectionIndex)
+                }
+                if let index = getIndexMetadatasForSection(sectionValue) {
+                    self.metadatasForSection.remove(at: index)
+                }
             } else {
-                metadataForSection.createMetadatasForSection()
+                metadataForSection.createMetadatas()
             }
             indexPathReturn = indexPath
-        }
+        } else { return (nil, false) }
 
         // DELETE metadatasSource (IMPORTANT LAST)
         if let rowIndex = self.metadatasSource.firstIndex(where: {$0.ocId == ocId}) {
             self.metadatasSource.remove(at: rowIndex)
         }
 
-        // REMOVE sectionsValue / metadatasForSection
-        if removeMetadataForSection {
-            if let index = self.sectionsValue.firstIndex(where: {$0 == sectionValue }) {
-                self.sectionsValue.remove(at: index)
-            }
-            if let index = self.metadatasForSection.firstIndex(where: {$0.sectionValue == sectionValue }) {
-                self.metadatasForSection.remove(at: index)
-            }
-        }
-
         return (indexPathReturn, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
     }
 
@@ -232,8 +267,6 @@ class NCDataSource: NSObject {
 
         let numberOfSections = self.numberOfSections()
         var ocIdSearch = ocId
-        var indexPath: IndexPath?
-        var metadataForSection: NCMetadataForSection?
 
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) }
 
@@ -242,10 +275,10 @@ class NCDataSource: NSObject {
         }
 
         // UPDATE metadataForSection (IMPORTANT FIRST)
-        (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocIdSearch)
+        let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocIdSearch)
         if let indexPath = indexPath, let metadataForSection = metadataForSection {
             metadataForSection.metadatas[indexPath.row] = metadata
-            metadataForSection.createMetadatasForSection()
+            metadataForSection.createMetadatas()
         }
 
         // UPDATE metadatasSource (IMPORTANT LAST)
@@ -253,69 +286,45 @@ class NCDataSource: NSObject {
             self.metadatasSource[rowIndex] = metadata
         }
 
-        return (indexPath, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
+        let result = self.getIndexPathMetadata(ocId: ocId)
+        return (result.indexPath, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
     }
 
     // MARK: -
 
     func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadataForSection?) {
-
-        if let metadata = metadatasSource.filter({ $0.ocId == ocId}).first {
-            let sectionValue = getSectionValue(metadata: metadata)
-            if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == sectionValue}) {
-                for metadataForSection in self.metadatasForSection {
-                    if metadataForSection.sectionValue == sectionValue {
-                        if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == ocId}) {
-                            return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
-                        }
-                    }
-                }
-            }
-        }
-
-        return (nil, nil)
+        guard let metadata = metadatasSource.filter({ $0.ocId == ocId}).first else { return (nil, nil) }
+        let sectionValue = getSectionValue(metadata: metadata)
+        guard let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionValue), let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == ocId}) else { return (nil, nil) }
+        return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
     }
 
     func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
-        if self.metadatasForSection.count == 0 { return false }
+        guard self.metadatasForSection.count > 0 else { return false }
         return numberOfSections == self.numberOfSections()
     }
 
     func numberOfSections() -> Int {
-
-        if self.metadatasForSection.count == 0 {
-            return 1
-        } else {
-            return self.metadatasForSection.count
-        }
+        guard self.sectionsValue.count > 0 else { return 1 }
+        return self.sectionsValue.count
     }
     
     func numberOfItemsInSection(_ section: Int) -> Int {
-
-        if self.metadatasForSection.count == 0 || self.metadatasSource.count == 0 { return 0 }
-        return self.metadatasForSection[section].metadatas.count
+        guard self.sectionsValue.count > 0 && self.metadatasSource.count > 0, let metadataForSection = getMetadataForSection(section) else { return 0}
+        return metadataForSection.metadatas.count
     }
 
     func cellForItemAt(indexPath: IndexPath) -> tableMetadata? {
-
-        let metadatasForSection = self.metadatasForSection[indexPath.section]
-        return metadatasForSection.metadatas[indexPath.row]
-    }
-
-    func getMetadataForSection(_ section: Int) -> NCMetadataForSection? {
-
-        if metadatasForSection.count == 0 { return nil }
-        return self.metadatasForSection[section]
+        guard metadatasForSection.count > 0 && indexPath.section < metadatasForSection.count, let metadataForSection = getMetadataForSection(indexPath.section), indexPath.row < metadataForSection.metadatas.count else { return nil }
+        return metadataForSection.metadatas[indexPath.row]
     }
 
     func getSectionValue(indexPath: IndexPath) -> String {
-
-        if metadatasForSection.count == 0 { return "" }
-        let metadataForSection = self.metadatasForSection[indexPath.section]
+        guard metadatasForSection.count > 0 , let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""}
         return metadataForSection.sectionValue
     }
 
-    func getFooterInformation() -> (directories: Int, files: Int, size: Int64) {
+    func getFooterInformationAllMetadatas() -> (directories: Int, files: Int, size: Int64) {
 
         var directories: Int = 0
         var files: Int = 0
@@ -330,6 +339,8 @@ class NCDataSource: NSObject {
         return (directories, files, size)
     }
 
+    // MARK: -
+
     internal func getSectionValue(metadata: tableMetadata) -> String {
 
         switch self.groupByField {
@@ -341,15 +352,39 @@ class NCDataSource: NSObject {
             return NSLocalizedString(metadata.name, comment: "").lowercased().firstUppercased
         }
     }
+
+    internal func getIndexMetadatasForSection(_ sectionValue: String) -> Int? {
+        return self.metadatasForSection.firstIndex(where: {$0.sectionValue == sectionValue })
+    }
+
+    internal func getSectionIndex(_ sectionValue: String) -> Int? {
+         return self.sectionsValue.firstIndex(where: {$0 == sectionValue })
+    }
+
+    internal func existsMetadataForSection(sectionValue: String) -> Bool {
+        return !self.metadatasForSection.filter({ $0.sectionValue == sectionValue }).isEmpty
+    }
+
+    internal func getMetadataForSection(_ section: Int) -> NCMetadataForSection? {
+        guard section < sectionsValue.count, let metadataForSection = self.metadatasForSection.filter({ $0.sectionValue == sectionsValue[section]}).first else { return nil }
+        return metadataForSection
+    }
+
+    internal func getMetadataForSection(_ sectionValue: String) -> NCMetadataForSection? {
+        guard let metadataForSection = self.metadatasForSection.filter({ $0.sectionValue == sectionValue }).first else { return nil }
+        return metadataForSection
+    }
 }
 
+// MARK: -
+
 class NCMetadataForSection: NSObject {
 
     var sectionValue: String
     var metadatas: [tableMetadata]
     var shares: [tableShare]
     var localFiles: [tableLocalFile]
-    var searchResult: NCCSearchResult?
+    var lastSearchResult: NCCSearchResult?
     var unifiedSearchInProgress: Bool = false
 
     private var sort : String
@@ -370,14 +405,13 @@ class NCMetadataForSection: NSObject {
     public var metadataShare: [String: tableShare] = [:]
     public var metadataOffLine: [String] = []
 
-
-    init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], searchResult: NCCSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
+    init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], lastSearchResult: NCCSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
 
         self.sectionValue = sectionValue
         self.metadatas = metadatas
         self.shares = shares
         self.localFiles = localFiles
-        self.searchResult = searchResult
+        self.lastSearchResult = lastSearchResult
         self.sort = sort
         self.ascending = ascending
         self.directoryOnTop = directoryOnTop
@@ -386,10 +420,10 @@ class NCMetadataForSection: NSObject {
 
         super.init()
 
-        createMetadatasForSection()
+        createMetadatas()
     }
 
-    func createMetadatasForSection() {
+    func createMetadatas() {
 
         // Clear
         //

+ 5 - 5
iOSClient/Files/NCFiles.swift

@@ -73,11 +73,11 @@ class NCFiles: NCCollectionViewCommon {
     override func reloadDataSource() {
         super.reloadDataSource()
 
-        if !self.isSearching && !self.appDelegate.account.isEmpty && !self.appDelegate.urlBase.isEmpty {
-            self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
-            if self.metadataFolder == nil {
-                self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, serverUrl: self.serverUrl)
-            }
+        guard !self.isSearching, !self.appDelegate.account.isEmpty, !self.appDelegate.urlBase.isEmpty else { return }
+
+        self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+        if self.metadataFolder == nil {
+            self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, serverUrl: self.serverUrl)
         }
 
         self.dataSource = NCDataSource(

+ 89 - 94
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -257,8 +257,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         if appDelegate.account == "" { return }
 
         // Search
-        if searchController?.isActive ?? false {
+        if searchController?.isActive ?? false || isSearching {
             searchController?.isActive = false
+            isSearching = false
         }
 
         // Select
@@ -599,7 +600,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
         if let cell = collectionView?.cellForItem(at: indexPath) {
             if let cell = cell as? NCCellProtocol {
-                if progressNumber.floatValue == 1 {
+                if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
                     cell.fileProgressView?.isHidden = true
                     cell.fileProgressView?.progress = .zero
                     cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
@@ -751,7 +752,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         self.isSearching = true
 
         self.providers?.removeAll()
-        self.searchResults?.removeAll()
         self.metadatasSource.removeAll()
         self.dataSource.clearDataSource()
 
@@ -768,14 +768,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
 
-        NCNetworking.shared.cancelUnifiedSearchFiles()
-        
-        self.isSearching = false
-        self.literalSearch = ""
-        self.providers?.removeAll()
-        self.searchResults?.removeAll()
+        DispatchQueue.global().async {
+            NCNetworking.shared.cancelUnifiedSearchFiles()
 
-        reloadDataSource()
+            self.isSearching = false
+            self.literalSearch = ""
+            self.providers?.removeAll()
+            self.dataSource.clearDataSource()
+
+            self.reloadDataSource()
+        }
     }
 
     // MARK: - TAP EVENT
@@ -822,6 +824,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         }
+        
         reloadDataSource()
     }
 
@@ -894,35 +897,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {
 
-        if let metadataForSection = metadataForSection, let searchResult = metadataForSection.searchResult, let cursor = searchResult.cursor, let term = literalSearch {
-
-            metadataForSection.unifiedSearchInProgress = true
-            self.collectionView?.reloadData()
-
-            NCNetworking.shared.unifiedSearchFilesProvider(urlBase: appDelegate, id: searchResult.id, term: term, limit: 5, cursor: cursor) { searchResult, metadatas, errorCode, ErrorDescription in
-
-                metadataForSection.unifiedSearchInProgress = false
-                self.collectionView?.reloadData()
-
-                guard let searchResult = searchResult, let metadatas = metadatas else {
-                    return
-                }
-                metadataForSection.searchResult = searchResult
-                var indexPaths: [IndexPath] = []
-                for metadata in metadatas {
-                    self.metadatasSource.append(metadata)
-                    let (indexPath, sameSections) = self.dataSource.addMetadata(metadata)
-                    if let indexPath = indexPath, sameSections {
-                        indexPaths.append(indexPath)
-                    }
-                }
-                self.collectionView?.performBatchUpdates({
-                    self.collectionView?.insertItems(at: indexPaths)
-                }, completion: { _ in
-                    self.collectionView?.reloadData()
-                })
-            }
-        }
+        unifiedSearchMore(metadataForSection: metadataForSection)
     }
 
     func longPressListItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) {
@@ -1062,65 +1037,89 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     @objc func networkSearch() {
         guard !appDelegate.account.isEmpty, let literalSearch = literalSearch, !literalSearch.isEmpty
         else {
-            DispatchQueue.main.async { self.refreshControl.endRefreshing() }
+            self.refreshControl.endRefreshing()
             return
         }
 
         isReloadDataSourceNetworkInProgress = true
         self.metadatasSource.removeAll()
         self.dataSource.clearDataSource()
-        collectionView?.reloadData()
-        
+        self.refreshControl.beginRefreshing()
+        self.collectionView.reloadData()
+
         let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
         if serverVersionMajor >= NCGlobal.shared.nextcloudVersion20 {
-            self.refreshControl.beginRefreshing()
             NCNetworking.shared.unifiedSearchFiles(urlBase: appDelegate, literal: literalSearch) { allProviders in
                 self.providers = allProviders
-            } update: { searchResults, metadatas in
-                guard let metadatas = metadatas, metadatas.count > 0 else { return }
-
-                DispatchQueue.main.async {
-                    if self.searchController?.isActive == true {
-                        self.searchResults = searchResults
-                        self.metadatasSource = metadatas
-                        self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
-                                                       account: self.appDelegate.account,
-                                                       sort: self.layoutForView?.sort,
-                                                       ascending: self.layoutForView?.ascending,
-                                                       directoryOnTop: self.layoutForView?.directoryOnTop,
-                                                       favoriteOnTop: true,
-                                                       filterLivePhoto: true,
-                                                       providers: self.providers,
-                                                       searchResults: self.searchResults)
-                        self.collectionView.reloadData()
-                    }
-                }
-            } completion: { searchResults, metadatas, errorCode, errorDescription in
-                DispatchQueue.main.async {
-                    if self.searchController?.isActive == true, errorCode == 0, let metadatas = metadatas {
-                        self.searchResults = searchResults
-                        self.metadatasSource = metadatas
-                    }
-                    self.refreshControl.endRefreshing()
-                    self.isReloadDataSourceNetworkInProgress = false
-                    self.reloadDataSource()
-                }
+                self.searchResults = []
+                self.dataSource = NCDataSource(
+                    metadatasSource: self.metadatasSource,
+                    account: self.appDelegate.account,
+                    sort: self.layoutForView?.sort,
+                    ascending: self.layoutForView?.ascending,
+                    directoryOnTop: self.layoutForView?.directoryOnTop,
+                    favoriteOnTop: true,
+                    filterLivePhoto: true,
+                    providers: self.providers,
+                    searchResults: self.searchResults)
+            } update: { id, searchResult, metadatas in
+                guard let metadatas = metadatas, metadatas.count > 0, self.isSearching , let searchResult = searchResult else { return }
+                NCOperationQueue.shared.dataSourceAddSection(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult)
+            } completion: {errorCode, errorDescription in
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+                self.collectionView.reloadData()
             }
         } else {
             NCNetworking.shared.searchFiles(urlBase: appDelegate, literal: literalSearch) { metadatas, errorCode, errorDescription in
+                if  self.isSearching, errorCode == 0, let metadatas = metadatas {
+                    self.metadatasSource = metadatas
+                }
+                self.dataSource = NCDataSource(
+                    metadatasSource: self.metadatasSource,
+                    account: self.appDelegate.account,
+                    sort: self.layoutForView?.sort,
+                    ascending: self.layoutForView?.ascending,
+                    directoryOnTop: self.layoutForView?.directoryOnTop,
+                    favoriteOnTop: true,
+                    filterLivePhoto: true,
+                    groupByField: self.groupByField,
+                    providers: self.providers,
+                    searchResults: self.searchResults)
+                self.isReloadDataSourceNetworkInProgress = false
                 DispatchQueue.main.async {
-                    if self.searchController?.isActive == true, errorCode == 0, let metadatas = metadatas {
-                        self.searchResults = nil
-                        self.metadatasSource = metadatas
-                    }
                     self.refreshControl.endRefreshing()
-                    self.isReloadDataSourceNetworkInProgress = false
-                    self.reloadDataSource()
+                    self.collectionView.reloadData()
                 }
             }
         }
     }
 
+    func unifiedSearchMore(metadataForSection: NCMetadataForSection?) {
+
+        guard let metadataForSection = metadataForSection, let searchResult = metadataForSection.lastSearchResult, let cursor = searchResult.cursor, let term = literalSearch else { return }
+
+        metadataForSection.unifiedSearchInProgress = true
+        self.collectionView?.reloadData()
+
+        NCNetworking.shared.unifiedSearchFilesProvider(urlBase: appDelegate, id: searchResult.id, term: term, limit: 5, cursor: cursor) { searchResult, metadatas, errorCode, ErrorDescription in
+
+            metadataForSection.unifiedSearchInProgress = false
+            guard let searchResult = searchResult, let metadatas = metadatas else { return }
+
+            self.metadatasSource.append(contentsOf: metadatas)
+            let indexPaths = self.dataSource.appendMetadatasToSection(metadatas, metadataForSection: metadataForSection, lastSearchResult: searchResult)
+
+            DispatchQueue.main.async {
+                self.collectionView?.performBatchUpdates({
+                    self.collectionView?.insertItems(at: indexPaths)
+                }, completion: { _ in
+                    self.collectionView?.reloadData()
+                })
+            }
+        }
+    }
+
     @objc func networkReadFolder(forced: Bool, completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasUpdate: [tableMetadata]?, _ metadatasDelete: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) {
 
         var tableDirectory: tableDirectory?
@@ -1529,7 +1528,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
             cell = gridCell
         }
 
-        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return UICollectionViewCell() }
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return cell }
 
         let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId]
         var isShare = false
@@ -1808,8 +1807,8 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
             let sections = dataSource.numberOfSections()
             let section = indexPath.section
             let metadataForSection = self.dataSource.getMetadataForSection(indexPath.section)
-            let isPaginated = metadataForSection?.searchResult?.isPaginated ?? false
-            let entriesCount: Int = metadataForSection?.searchResult?.entries.count ?? 0
+            let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false
+            let metadatasCount: Int = metadataForSection?.metadatas.count ?? 0
             let unifiedSearchInProgress = metadataForSection?.unifiedSearchInProgress ?? false
 
             footer.delegate = self
@@ -1825,7 +1824,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
                 if sections > 1 && section != sections - 1 {
                     footer.separatorIsHidden(false)
                 }
-                if isSearching && isPaginated && entriesCount > 0 {
+                if isSearching && isPaginated && metadatasCount > 0 {
                     footer.buttonIsHidden(false)
                 }
                 if unifiedSearchInProgress {
@@ -1833,7 +1832,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
                 }
             } else {
                 if sections == 1 || section == sections - 1 {
-                    let info = dataSource.getFooterInformation()
+                    let info = dataSource.getFooterInformationAllMetadatas()
                     footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size)
                 } else {
                     footer.separatorIsHidden(false)
@@ -1872,18 +1871,14 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
             }
         }
 
-        if section == 0 && dataSource.numberOfSections() > 1 {
-            return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection)
-        } else if section == 0 && dataSource.numberOfSections() == 1 {
-            if collectionView.collectionViewLayout == gridLayout {
+        if isSearching || collectionView.collectionViewLayout == gridLayout || dataSource.numberOfSections() > 1 {
+            if section == 0 {
                 return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection)
             } else {
-                return (getHeaderHeight(), headerRichWorkspace, 0)
+                return (0, 0, NCGlobal.shared.heightSection)
             }
-        } else if section > 0 && dataSource.numberOfSections() > 1 {
-            return (0, 0, NCGlobal.shared.heightSection)
         } else {
-            return (0, 0, 0)
+            return (getHeaderHeight(), headerRichWorkspace, 0)
         }
     }
 
@@ -1899,8 +1894,8 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
 
         let sections = dataSource.numberOfSections()
         let metadataForSection = self.dataSource.getMetadataForSection(section)
-        let isPaginated = metadataForSection?.searchResult?.isPaginated ?? false
-        let entriesCount: Int = metadataForSection?.searchResult?.entries.count ?? 0
+        let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false
+        let metadatasCount: Int = metadataForSection?.lastSearchResult?.entries.count ?? 0
         var size = CGSize(width: collectionView.frame.width, height: 0)
 
         if section == sections - 1 {
@@ -1909,7 +1904,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
             size.height += NCGlobal.shared.heightFooter
         }
 
-        if isSearching && isPaginated && entriesCount > 0 {
+        if isSearching && isPaginated && metadatasCount > 0 {
             size.height += NCGlobal.shared.heightFooterButton
         }
 

+ 20 - 40
iOSClient/Networking/NCNetworking.swift

@@ -418,7 +418,7 @@ import Queuer
     // MARK: - Upload
 
     @objc func upload(metadata: tableMetadata, start: @escaping () -> Void, completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> Void) {
-        
+
         func uploadMetadata(_ metadata: tableMetadata) {
 
             // DETECT IF CHUNCK
@@ -931,17 +931,14 @@ import Queuer
 
     /// Unified Search (NC>=20)
     ///
-    func unifiedSearchFiles(urlBase: NCUserBaseUrl, literal: String, providers: @escaping ([NCCSearchProvider]?) -> Void, update: @escaping ([NCCSearchResult]?, [tableMetadata]?) -> Void, completion: @escaping ([NCCSearchResult]?, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
+    func unifiedSearchFiles(urlBase: NCUserBaseUrl, literal: String, providers: @escaping ([NCCSearchProvider]?) -> Void, update: @escaping (_ id: String, NCCSearchResult?, [tableMetadata]?) -> Void, completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> ()) {
 
-        var searchResults: [NCCSearchResult] = []
-        var searchFiles: [tableMetadata] = []
         var errorCode = 0
         var errorDescription = ""
-        let concurrentQueue = DispatchQueue(label: "com.nextcloud.requestUnifiedSearch.concurrentQueue", attributes: .concurrent)
         let dispatchGroup = DispatchGroup()
         dispatchGroup.enter()
         dispatchGroup.notify(queue: .main) {
-            completion(searchResults, Array(searchFiles), errorCode, errorDescription)
+            completion(errorCode, errorDescription)
         }
 
         NCCommunication.shared.unifiedSearch(term: literal, timeout: 30, timeoutProvider: 90) { provider in
@@ -956,21 +953,17 @@ import Queuer
             providers(allProviders)
         } update: { partialResult, provider, errorCode, errorDescription in
             guard let partialResult = partialResult else { return }
-            searchResults.append(partialResult)
+            var metadatas: [tableMetadata] = []
 
             switch provider.id {
             case "files":
                 partialResult.entries.forEach({ entry in
                     if let fileId = entry.fileId,
-                       let newMetadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && fileId == %@", urlBase.userAccount, String(fileId))) {
-                        concurrentQueue.async(flags: .barrier) {
-                            searchFiles.append(newMetadata)
-                        }
+                       let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && fileId == %@", urlBase.userAccount, String(fileId))) {
+                        metadatas.append(metadata)
                     } else if let filePath = entry.filePath {
-                        self.loadMetadata(urlBase: urlBase, filePath: filePath, dispatchGroup: dispatchGroup) { newMetadata in
-                            concurrentQueue.async(flags: .barrier) {
-                                searchFiles.append(newMetadata)
-                            }
+                        self.loadMetadata(urlBase: urlBase, filePath: filePath, dispatchGroup: dispatchGroup) { metadata in
+                            metadatas.append(metadata)
                         }
                     } else { print(#function, "[ERROR]: File search entry has no path: \(entry)") }
                 })
@@ -981,39 +974,30 @@ import Queuer
                 partialResult.entries.forEach({ entry in
                     let url = URLComponents(string: entry.resourceURL)
                     guard let dir = url?.queryItems?["dir"]?.value, let filename = url?.queryItems?["scrollto"]?.value else { return }
-                    if let newMetadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(
+                    if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(
                               format: "account == %@ && path == %@ && fileName == %@",
                               urlBase.userAccount,
                               "/remote.php/dav/files/" + urlBase.user + dir,
                               filename)) {
-                        concurrentQueue.async(flags: .barrier) {
-                            searchFiles.append(newMetadata)
-                        }
+                        metadatas.append(metadata)
                     } else {
-                        self.loadMetadata(urlBase: urlBase, filePath: dir + filename, dispatchGroup: dispatchGroup) { newMetadata in
-                            concurrentQueue.async(flags: .barrier) {
-                                searchFiles.append(newMetadata)
-                            }
+                        self.loadMetadata(urlBase: urlBase, filePath: dir + filename, dispatchGroup: dispatchGroup) { metadata in
+                            metadatas.append(metadata)
                         }
                     }
                 })
             default:
                 partialResult.entries.forEach({ entry in
-                    concurrentQueue.async(flags: .barrier) {
-                        let newMetadata = NCManageDatabase.shared.createMetadata(account: urlBase.account, user: urlBase.user, userId: urlBase.userId, fileName: entry.title, fileNameView: entry.title, ocId: NSUUID().uuidString, serverUrl: urlBase.urlBase, urlBase: urlBase.urlBase, url: entry.resourceURL, contentType: "", isUrl: true, name: partialResult.name.lowercased(), subline: entry.subline, iconName: entry.icon, iconUrl: entry.thumbnailURL)
-                        searchFiles.append(newMetadata)
-                    }
+                    let metadata = NCManageDatabase.shared.createMetadata(account: urlBase.account, user: urlBase.user, userId: urlBase.userId, fileName: entry.title, fileNameView: entry.title, ocId: NSUUID().uuidString, serverUrl: urlBase.urlBase, urlBase: urlBase.urlBase, url: entry.resourceURL, contentType: "", isUrl: true, name: partialResult.name.lowercased(), subline: entry.subline, iconName: entry.icon, iconUrl: entry.thumbnailURL)
+                    metadatas.append(metadata)
                 })
             }
-            update(searchResults, searchFiles)
-        } completion: { results, code, description in
+            update(provider.id, partialResult, metadatas)
+        } completion: { err, description in
             self.requestsUnifiedSearch.removeAll()
-            dispatchGroup.leave()
-            if let results = results {
-                searchResults = results
-            }
-            errorCode = code
+            errorCode = err
             errorDescription = description
+            dispatchGroup.leave()
         }
     }
 
@@ -1023,9 +1007,7 @@ import Queuer
 
         let request = NCCommunication.shared.searchProvider(id, term: term, limit: limit, cursor: cursor, timeout: 60) { searchResult, errorCode, errorDescription in
             guard let searchResult = searchResult else {
-                DispatchQueue.main.async {
-                    completion(nil, metadatas, errorCode, errorDescription)
-                }
+                completion(nil, metadatas, errorCode, errorDescription)
                 return
             }
 
@@ -1062,9 +1044,7 @@ import Queuer
                 })
             }
 
-            DispatchQueue.main.async {
-                completion(searchResult, metadatas, errorCode, errorDescription)
-            }
+            completion(searchResult, metadatas, errorCode, errorDescription)
         }
         if let request = request {
             requestsUnifiedSearch.append(request)

+ 2 - 1
iOSClient/Networking/NCNetworkingCheckRemoteUser.swift

@@ -66,10 +66,11 @@ import NCCommunication
 
                     } else {
 
-                        if UIApplication.shared.applicationState == .active &&  NCCommunication.shared.isNetworkReachable() {
+                        if UIApplication.shared.applicationState == .active && NCCommunication.shared.isNetworkReachable() && !CCUtility.getPassword(account).isEmpty && !self.appDelegate.deletePasswordSession {
                             let description = String.localizedStringWithFormat(NSLocalizedString("_error_check_remote_user_", comment: ""), tableAccount.user, tableAccount.urlBase)
                             NCContentPresenter.shared.messageNotification("_error_", description: description, delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
                             CCUtility.setPassword(account, password: nil)
+                            self.appDelegate.deletePasswordSession = true
                         }
                     }
 

+ 6 - 0
iOSClient/Networking/NCNetworkingProcessUpload.swift

@@ -85,6 +85,12 @@ class NCNetworkingProcessUpload: NSObject {
 
                     for metadata in metadatas {
 
+                        // Different account
+                        if self.appDelegate.account != metadata.account {
+                            NCCommunicationCommon.shared.writeLog("Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView) on account: \(metadata.account), because the actual account is \(self.appDelegate.account).")
+                            continue
+                        }
+
                         // Is already in upload background? skipped
                         if listOcId.contains(metadata.ocId) {
                             NCCommunicationCommon.shared.writeLog("Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView), because is already in session.")

+ 56 - 0
iOSClient/Networking/NCOperationQueue.swift

@@ -37,6 +37,7 @@ import NCCommunication
     private let synchronizationQueue = Queuer(name: "synchronizationQueue", maxConcurrentOperationCount: 1, qualityOfService: .default)
     private let downloadThumbnailQueue = Queuer(name: "downloadThumbnailQueue", maxConcurrentOperationCount: 10, qualityOfService: .default)
     private let downloadAvatarQueue = Queuer(name: "downloadAvatarQueue", maxConcurrentOperationCount: 10, qualityOfService: .default)
+    private let dataSourceQueue = Queuer(name: "dataSourceQueue", maxConcurrentOperationCount: 1, qualityOfService: .default)
 
     private var timerReadFileForMediaQueue: Timer?
 
@@ -47,6 +48,7 @@ import NCCommunication
         synchronizationCancelAll()
         downloadThumbnailCancelAll()
         downloadAvatarCancelAll()
+        dataSourceAddSectionCancelAll()
     }
 
     // Download file
@@ -196,6 +198,20 @@ import NCCommunication
     @objc func downloadAvatarCancelAll() {
         downloadAvatarQueue.cancelAll()
     }
+
+    // Datasource
+
+    func dataSourceAddSection(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NCCSearchResult) {
+        dataSourceQueue.addOperation(NCOperationDataSource.init(collectionViewCommon: collectionViewCommon, metadatas: metadatas, searchResult: searchResult))
+    }
+
+    @objc func dataSourceAddSectionCancelAll() {
+        dataSourceQueue.cancelAll()
+    }
+
+    func dataSourceAddSectionCount() -> Int {
+        return dataSourceQueue.operationCount
+    }
 }
 
 // MARK: -
@@ -523,3 +539,43 @@ class NCOperationDownloadAvatar: ConcurrentOperation {
         }
     }
 }
+
+// MARK: -
+
+class NCOperationDataSource: ConcurrentOperation {
+
+    var collectionViewCommon: NCCollectionViewCommon
+    var metadatas: [tableMetadata]
+    var searchResult: NCCSearchResult
+
+    init(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NCCSearchResult) {
+        self.collectionViewCommon = collectionViewCommon
+        self.metadatas = metadatas
+        self.searchResult = searchResult
+    }
+
+    func reloadDataThenPerform(_ closure: @escaping (() -> Void)) {
+        DispatchQueue.main.async {
+            CATransaction.begin()
+            CATransaction.setCompletionBlock(closure)
+            self.collectionViewCommon.collectionView.reloadData()
+            CATransaction.commit()
+        }
+    }
+
+    override func start() {
+
+        if isCancelled {
+            self.finish()
+        } else {
+            self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult)
+            for metadata in self.metadatas {
+                self.collectionViewCommon.metadatasSource.append(metadata)
+            }
+            self.collectionViewCommon.searchResults?.append(self.searchResult)
+            reloadDataThenPerform {
+                self.finish()
+            }
+        }
+    }
+}

+ 7 - 9
iOSClient/Select/NCSelect.swift

@@ -298,6 +298,8 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         }
+
+        reloadDataSource()
     }
 
     func tapButtonOrder(_ sender: Any) {
@@ -655,7 +657,7 @@ extension NCSelect: UICollectionViewDataSource {
             footer.separatorIsHidden(true)
 
             if sections == 1 || section == sections - 1 {
-                let info = dataSource.getFooterInformation()
+                let info = dataSource.getFooterInformationAllMetadatas()
                 footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size)
             } else {
                 footer.separatorIsHidden(false)
@@ -679,18 +681,14 @@ extension NCSelect: UICollectionViewDelegateFlowLayout {
             }
         }
 
-        if section == 0 && dataSource.numberOfSections() > 1 {
-            return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, NCGlobal.shared.heightSection)
-        } else if section == 0 && dataSource.numberOfSections() == 1 {
-            if collectionView.collectionViewLayout == gridLayout {
+        if isSearching || collectionView.collectionViewLayout == gridLayout || dataSource.numberOfSections() > 1 {
+            if section == 0 {
                 return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, NCGlobal.shared.heightSection)
             } else {
-                return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, 0)
+                return (0, 0, NCGlobal.shared.heightSection)
             }
-        } else if section > 0 && dataSource.numberOfSections() > 1 {
-            return (0, 0, NCGlobal.shared.heightSection)
         } else {
-            return (0, 0, 0)
+            return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, 0)
         }
     }
 

+ 1 - 0
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -891,6 +891,7 @@
 "_subtitle_not_found_"      = "Subtitle not found";
 "_disable_"                 = "Disable";
 "_subtitle_not_dowloaded_"  = "There are subtitles not downloaded locally";
+"_user_"                    = "User";
 
 // Tip
 "_tip_pdf_thumbnails_"      = "Swipe left from the right edge of the screen to show the thumbnails.";

+ 5 - 0
iOSClient/Transfers/NCTransferCell.swift

@@ -124,6 +124,11 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP
         delegate?.longPressListItem(with: objectId, gestureRecognizer: gestureRecognizer)
     }
 
+    func hideButtonMore(_ status: Bool) {
+        imageMore.isHidden = status
+        buttonMore.isHidden = status
+    }
+
     func setButtonMore(named: String, image: UIImage) {
         namedButtonMore = named
         imageMore.image = image

+ 22 - 6
iOSClient/Transfers/NCTransfers.swift

@@ -167,7 +167,7 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
         cell.fileUser = metadata.ownerId
         cell.indexPath = indexPath
 
-        cell.imageItem.image = nil
+        cell.imageItem.image = NCBrandColor.cacheImages.file
         cell.imageItem.backgroundColor = nil
 
         cell.labelTitle.text = metadata.fileNameView
@@ -182,15 +182,28 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
 
         cell.progressView.progress = 0.0
 
-        if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
-            cell.imageItem.image =  UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
-        } else if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) {
-            cell.imageItem.image =  UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
+        /*
+        let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+        let iconImagePath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.fileNameView)!
+
+        if FileManager().fileExists(atPath: iconImagePath) {
+            cell.imageItem.image =  UIImage(contentsOfFile:iconImagePath)
+        } else if FileManager().fileExists(atPath: imagePath) {
+            if let image = UIImage(contentsOfFile: imagePath) {
+                let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon), isAspectRation: true)
+                if let data = image?.jpegData(compressionQuality: 0.5) {
+                    do {
+                        try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic)
+                        cell.imageItem.image = image
+                    } catch {
+                    }
+                }
+            }
         }
-
         if cell.imageItem.image == nil {
             cell.imageItem.image = NCBrandColor.cacheImages.file
         }
+        */
 
         cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size)
 
@@ -244,6 +257,9 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
             cell.labelInfo.text = ""
             break
         }
+        if self.appDelegate.account != metadata.account {
+            cell.labelInfo.text = NSLocalizedString("_user_", comment: "") + ": \(metadata.userId) " + NSLocalizedString("_in_", comment: "") + " \(metadata.urlBase)"
+        }
         cell.accessibilityLabel = metadata.fileNameView + ", " + (cell.labelInfo.text ?? "")
 
         // Remove last separator

+ 2 - 0
iOSClient/Trash/NCTrash.swift

@@ -157,6 +157,8 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "", layout: layoutForView?.layout)
         }
+
+        reloadDataSource()
     }
 
     func tapButtonOrder(_ sender: Any) {