Browse Source

Merge branch 'develop' of https://github.com/nextcloud/ios into 2460-add-other-nextcloud-apps-to-settings-as-suggestions

Milen Pivchev 1 year ago
parent
commit
2650a51a52

+ 6 - 6
Nextcloud.xcodeproj/project.pbxproj

@@ -184,7 +184,6 @@
 		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 */; };
-		F7148059262ED52200693E51 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */; };
 		F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
 		F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7239876253D86D300257F49 /* NCEmptyView.xib */; };
 		F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F717402B24F699A5000C87D5 /* NCFavorite.storyboard */; };
@@ -483,7 +482,6 @@
 		F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4921903F850088454D /* NCTrashListCell.xib */; };
 		F78ACD52219046DC0088454D /* NCSectionHeaderMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */; };
 		F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
-		F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */; };
 		F78C6FDE296D677300C952C3 /* NCContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78C6FDD296D677300C952C3 /* NCContextMenu.swift */; };
 		F78E2D6529AF02DB0024D4F3 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78E2D6429AF02DB0024D4F3 /* Database.swift */; };
 		F78E2D6629AF02DB0024D4F3 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78E2D6429AF02DB0024D4F3 /* Database.swift */; };
@@ -553,6 +551,8 @@
 		F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCLoginWeb.swift */; };
 		F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */; };
 		F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; };
+		F7B398422A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */; };
+		F7B398432A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.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 */; };
 		F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F7B8B82F25681C3400967775 /* GoogleService-Info.plist */; };
@@ -1113,7 +1113,6 @@
 		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>"; };
 		F78ACD53219047D40088454D /* NCSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionFooter.xib; sourceTree = "<group>"; };
-		F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionHeaderMenu.xib; sourceTree = "<group>"; };
 		F78C6FDD296D677300C952C3 /* NCContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCContextMenu.swift; sourceTree = "<group>"; };
 		F78D6F461F0B7CB9002F9619 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F78D6F4D1F0B7CE4002F9619 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -1202,6 +1201,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>"; };
 		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>"; };
 		F7B8B82F25681C3400967775 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; };
@@ -1996,7 +1996,7 @@
 			children = (
 				F78ACD53219047D40088454D /* NCSectionFooter.xib */,
 				F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */,
-				F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */,
+				F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */,
 				F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */,
 			);
 			path = "Section Header Footer";
@@ -3096,11 +3096,11 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				F7148059262ED52200693E51 /* NCSectionHeaderMenu.xib in Resources */,
 				F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */,
 				F7148054262ED51000693E51 /* NCListCell.xib in Resources */,
 				F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */,
 				AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */,
+				F7B398432A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */,
 				F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */,
 				AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */,
 				F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */,
@@ -3213,7 +3213,7 @@
 				F73CB3B222E072A000AD728E /* NCShareHeaderView.xib in Resources */,
 				F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */,
 				F7EDE514262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib in Resources */,
-				F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */,
+				F7B398422A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */,
 				F7501C322212E57500FB1415 /* NCMedia.storyboard in Resources */,
 				F74DE14425135B6800917068 /* NCTransfers.storyboard in Resources */,
 				F77910A525DD517B00CEDB9E /* Settings.bundle in Resources */,

+ 2 - 0
Share/NCShareExtension.swift

@@ -339,6 +339,8 @@ extension NCShareExtension {
         metadata.classFile = results.classFile
         // CHUNCK
         metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
+        // E2EE
+        metadata.e2eEncrypted = metadata.isDirectoryE2EE
 
         hud.textLabel.text = NSLocalizedString("_upload_file_", comment: "") + " \(counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(filesName.count)"
         hud.show(in: self.view)

+ 6 - 84
iOSClient/Data/NCDataSource.swift

@@ -51,7 +51,7 @@ class NCDataSource: NSObject {
         super.init()
 
         self.metadatas = metadatas.filter({
-            !NCGlobal.shared.includeHiddenFiles.contains($0.fileNameView)
+            !(NCGlobal.shared.includeHiddenFiles.contains($0.fileNameView) || $0.isTransferInForeground)
         })
         self.directory = directory
         self.localFiles = NCManageDatabase.shared.getTableLocalFile(account: account)
@@ -218,79 +218,6 @@ class NCDataSource: NSObject {
         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.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
-            self.metadatas[rowIndex] = metadata
-        } else {
-            self.metadatas.append(metadata)
-        }
-
-        // ADD metadataForSection
-        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.createMetadatas()
-                if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
-                    return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-                }
-                return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-            }
-        } else {
-            // NEW section
-            createSections()
-            // get IndexPath of new section
-            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))
-                }
-            }
-        }
-
-        return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-    }
-
-    func deleteMetadata(ocId: String) -> (indexPath: IndexPath?, sameSections: Bool) {
-
-        let numberOfSections = self.numberOfSections()
-        var indexPathReturn: IndexPath?
-        var sectionValue = ""
-
-        // DELETE metadataForSection (IMPORTANT FIRST)
-        let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId)
-        if let indexPath = indexPath, let metadataForSection = metadataForSection, indexPath.row < metadataForSection.metadatas.count {
-            metadataForSection.metadatas.remove(at: indexPath.row)
-            if metadataForSection.metadatas.isEmpty {
-                // REMOVE sectionsValue / metadatasForSection
-                sectionValue = metadataForSection.sectionValue
-                if let sectionIndex = getSectionIndex(sectionValue) {
-                    self.sectionsValue.remove(at: sectionIndex)
-                }
-                if let index = getIndexMetadatasForSection(sectionValue) {
-                    self.metadatasForSection.remove(at: index)
-                }
-            } else {
-                metadataForSection.createMetadatas()
-            }
-            indexPathReturn = indexPath
-        } else { return (nil, false) }
-
-        // DELETE metadatasSource (IMPORTANT LAST)
-        if let rowIndex = self.metadatas.firstIndex(where: {$0.ocId == ocId}) {
-            self.metadatas.remove(at: rowIndex)
-        }
-
-        return (indexPathReturn, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-    }
-
     @discardableResult
     func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> (indexPath: IndexPath?, sameSections: Bool) {
 
@@ -328,11 +255,6 @@ class NCDataSource: NSObject {
         return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
     }
 
-    func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
-        guard !self.metadatasForSection.isEmpty else { return false }
-        return numberOfSections == self.numberOfSections()
-    }
-
     func numberOfSections() -> Int {
         guard !self.sectionsValue.isEmpty else { return 1 }
         return self.sectionsValue.count
@@ -348,11 +270,6 @@ class NCDataSource: NSObject {
         return metadataForSection.metadatas[indexPath.row]
     }
 
-    func getSectionValue(indexPath: IndexPath) -> String {
-        guard !metadatasForSection.isEmpty, let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""}
-        return metadataForSection.sectionValue
-    }
-
     func getSectionValueLocalization(indexPath: IndexPath) -> String {
         guard !metadatasForSection.isEmpty, let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""}
         if let searchResults = self.searchResults, let searchResult = searchResults.filter({ $0.id == metadataForSection.sectionValue}).first {
@@ -378,6 +295,11 @@ class NCDataSource: NSObject {
 
     // MARK: -
 
+    internal func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
+        guard !self.metadatasForSection.isEmpty else { return false }
+        return numberOfSections == self.numberOfSections()
+    }
+
     internal func getSectionValue(metadata: tableMetadata) -> String {
 
         switch self.groupByField {

+ 22 - 3
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -211,10 +211,18 @@ extension tableMetadata {
         return classFile == NKCommon.TypeClassFile.document.rawValue && editors.contains(NCGlobal.shared.editorText) && ((editors.contains(NCGlobal.shared.editorOnlyoffice) || isRichDocument))
     }
 
-    var isDownloadUpload: Bool {
+    var isWaitingTransfer: Bool {
+        status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploadError
+    }
+
+    var isInTransfer: Bool {
         status == NCGlobal.shared.metadataStatusInDownload || status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusInUpload || status == NCGlobal.shared.metadataStatusUploading
     }
 
+    var isTransferInForeground: Bool {
+        (status > 0 && (chunk || e2eEncrypted))
+    }
+
     var isDownload: Bool {
         status == NCGlobal.shared.metadataStatusInDownload || status == NCGlobal.shared.metadataStatusDownloading
     }
@@ -416,7 +424,6 @@ extension NCManageDatabase {
         let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
 
         metadata.account = account
-        metadata.chunk = false
         metadata.creationDate = Date() as NSDate
         metadata.date = Date() as NSDate
         metadata.hasPreview = true
@@ -867,9 +874,9 @@ extension NCManageDatabase {
 
     func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
 
+        guard let ocId else { return nil }
         do {
             let realm = try Realm()
-            guard let ocId = ocId else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
@@ -879,6 +886,18 @@ extension NCManageDatabase {
         return nil
     }
 
+    func getTableMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
+
+        guard let ocId else { return nil }
+        do {
+            let realm = try Realm()
+            return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access to database: \(error)")
+        }
+        return nil
+    }
+
     func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
 
         do {

+ 1 - 0
iOSClient/Files/NCFiles.swift

@@ -40,6 +40,7 @@ class NCFiles: NCCollectionViewCommon {
         enableSearchBar = true
         headerMenuButtonsView = true
         headerRichWorkspaceDisable = false
+        headerMenuTransferView = true
         emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = "_no_file_pull_down_"

+ 77 - 39
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -72,6 +72,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     internal var titleCurrentFolder = ""
     internal var titlePreviusFolder: String?
     internal var enableSearchBar: Bool = false
+    internal var headerMenuTransferView = false
     internal var headerMenuButtonsView: Bool = true
     internal var headerRichWorkspaceDisable:Bool = false
     internal var emptyImage: UIImage?
@@ -481,38 +482,56 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         guard let userInfo = notification.userInfo as NSDictionary?,
               let ocId = userInfo["ocId"] as? String,
               let serverUrl = userInfo["serverUrl"] as? String,
-              serverUrl == self.serverUrl,
-              let account = userInfo["account"] as? String,
-              account == appDelegate.account
+              let account = userInfo["account"] as? String
         else { return }
 
         guard !isSearchingMode, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return }
-        dataSource.addMetadata(metadata)
-        self.collectionView?.reloadData()
+
+        // Header view trasfer
+        if metadata.isTransferInForeground {
+            NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: 0)
+            self.collectionView?.reloadData()
+        }
+
+        if serverUrl == self.serverUrl, account == appDelegate.account {
+            reloadDataSource()
+        }
     }
 
     @objc func uploadedFile(_ notification: NSNotification) {
 
         guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
               let serverUrl = userInfo["serverUrl"] as? String,
-              serverUrl == self.serverUrl,
-              let account = userInfo["account"] as? String,
-              account == appDelegate.account
+              let account = userInfo["account"] as? String
         else { return }
 
-        reloadDataSource()
+        if ocId == NCNetworking.shared.transferInForegorund?.ocId {
+            NCNetworking.shared.transferInForegorund = nil
+            self.collectionView?.reloadData()
+        }
+
+        if account == appDelegate.account, serverUrl == self.serverUrl {
+            reloadDataSource()
+        }
     }
 
     @objc func uploadCancelFile(_ notification: NSNotification) {
 
         guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
               let serverUrl = userInfo["serverUrl"] as? String,
-              serverUrl == self.serverUrl,
-              let account = userInfo["account"] as? String,
-              account == appDelegate.account
+              let account = userInfo["account"] as? String
         else { return }
 
-        reloadDataSource()
+        if ocId == NCNetworking.shared.transferInForegorund?.ocId {
+            NCNetworking.shared.transferInForegorund = nil
+            self.collectionView?.reloadData()
+        }
+
+        if account == appDelegate.account, serverUrl == self.serverUrl {
+            reloadDataSource()
+        }
     }
 
     @objc func triggerProgressTask(_ notification: NSNotification) {
@@ -522,11 +541,25 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
               let totalBytes = userInfo["totalBytes"] as? Int64,
               let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64,
               let ocId = userInfo["ocId"] as? String,
-              let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: ocId) as? (IndexPath, NCMetadataForSection?)
+              let chunk = userInfo["chunk"] as? Bool,
+              let e2eEncrypted = userInfo["e2eEncrypted"] as? Bool
         else { return }
 
+        // Header Transfer
+        if headerMenuTransferView && (chunk || e2eEncrypted) {
+            if NCNetworking.shared.transferInForegorund?.ocId == ocId {
+                NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
+            } else {
+                NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: progressNumber.floatValue)
+                collectionView.reloadData()
+            }
+            self.headerMenu?.progressTransfer.progress = progressNumber.floatValue
+        }
+
         let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
-        if let cell = collectionView?.cellForItem(at: indexPath) {
+
+        if let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: ocId) as? (IndexPath, NCMetadataForSection?),
+           let cell = collectionView?.cellForItem(at: indexPath) {
             if let cell = cell as? NCCellProtocol {
                 if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
                     cell.fileProgressView?.isHidden = true
@@ -778,29 +811,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
     }
 
-    func tapButton1(_ sender: Any) {
-
-        NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: self) { hasPermission in
-            if hasPermission {
-                NCPhotosPickerViewController.init(viewController: self, maxSelectedAssets: 0, singleSelectedMode: false)
-            }
-        }
-    }
-
-    func tapButton2(_ sender: Any) {
-
-        guard !appDelegate.activeServerUrl.isEmpty else { return }
-        let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate)
-        appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil)
-    }
-
-    func tapButton3(_ sender: Any) {
-
-        if let viewController = appDelegate.window?.rootViewController {
-            NCDocumentCamera.shared.openScannerDocument(viewController: viewController)
-        }
-    }
-
     func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) {
         tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: image, sender: sender)
     }
@@ -844,6 +854,13 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         unifiedSearchMore(metadataForSection: metadataForSection)
     }
 
+    func tapButtonTransfer(_ sender: Any) {
+        if let ocId = NCNetworking.shared.transferInForegorund?.ocId,
+           let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+            NCNetworking.shared.cancelTransferMetadata(metadata) { }
+        }
+    }
+
     func longPressListItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) {
     }
 
@@ -1515,7 +1532,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
         }
 
         // Button More
-        if metadata.isDownloadUpload {
+        if metadata.isInTransfer || metadata.isWaitingTransfer {
             cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
         } else if metadata.lock == true {
             cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock)
@@ -1537,12 +1554,15 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
             break
         case NCGlobal.shared.metadataStatusWaitUpload:
             cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_upload_", comment: "")
+            cell.fileLocalImage?.image = nil
             break
         case NCGlobal.shared.metadataStatusInUpload:
             cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_upload_", comment: "")
+            cell.fileLocalImage?.image = nil
             break
         case NCGlobal.shared.metadataStatusUploading:
             cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - ↑ …"
+            cell.fileLocalImage?.image = nil
             break
         case NCGlobal.shared.metadataStatusUploadError:
             if metadata.sessionError != "" {
@@ -1648,6 +1668,13 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
 
                 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 headerMenuButtonsView {
                     header.setStatusButtonsView(enable: !dataSource.getMetadataSourceForAllSections().isEmpty)
                     header.setButtonsView(height: NCGlobal.shared.heightButtonsView)
@@ -1736,6 +1763,17 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
 
         var size: CGFloat = 0
 
+        // transfer in progress
+        if headerMenuTransferView,
+           let metadata = NCManageDatabase.shared.getMetadataFromOcId(NCNetworking.shared.transferInForegorund?.ocId),
+            metadata.isTransferInForeground {
+            if !isSearchingMode {
+                size += NCGlobal.shared.heightHeaderTransfer
+            }
+        } else {
+            NCNetworking.shared.transferInForegorund = nil
+        }
+
         if headerMenuButtonsView {
             size += NCGlobal.shared.heightButtonsView
         }

+ 2 - 2
iOSClient/Main/Collection Common/NCGridCell.swift

@@ -99,7 +99,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         imageVisualEffect.clipsToBounds = true
         imageVisualEffect.alpha = 0.5
 
-        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.tintColor = NCBrandColor.shared.brand
         progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5)
         progressView.trackTintColor = .clear
 
@@ -180,7 +180,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     func selected(_ status: Bool) {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isDownloadUpload else {
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isInTransfer else {
             imageSelect.isHidden = true
             imageVisualEffect.isHidden = true
             return

+ 2 - 2
iOSClient/Main/Collection Common/NCListCell.swift

@@ -120,7 +120,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         accessibilityValue = nil
         isAccessibilityElement = true
 
-        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.tintColor = NCBrandColor.shared.brand
         progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5)
         progressView.trackTintColor = .clear
 
@@ -240,7 +240,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     func selected(_ status: Bool) {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isDownloadUpload else {
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isInTransfer else {
             backgroundView = nil
             separator.isHidden = false
             return

+ 1 - 1
iOSClient/Main/NCActionCenter.swift

@@ -339,7 +339,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
         let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadata.count, hudView: appDelegate.window?.rootViewController?.view)
         for (metadata, url) in downloadMetadata {
             processor.execute { completion in
-                NCNetworking.shared.download(metadata: metadata, selector: "", notificationCenterProgressTask: false, completion: { _, _ in
+                NCNetworking.shared.download(metadata: metadata, selector: "", completion: { _, _ in
                     if CCUtility.fileProviderStorageExists(metadata) { items.append(url) }
                     completion()
                 })

+ 112 - 20
iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.swift

@@ -29,19 +29,25 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
     @IBOutlet weak var buttonSwitch: UIButton!
     @IBOutlet weak var buttonOrder: UIButton!
     @IBOutlet weak var buttonMore: UIButton!
-
+    @IBOutlet weak var buttonTransfer: UIButton!
+    @IBOutlet weak var imageButtonTransfer: UIImageView!
+    @IBOutlet weak var labelTransfer: UILabel!
+    @IBOutlet weak var progressTransfer: UIProgressView!
+    @IBOutlet weak var transferSeparatorBottom: UIView!
+    @IBOutlet weak var textViewRichWorkspace: UITextView!
+    @IBOutlet weak var labelSection: UILabel!
+    @IBOutlet weak var viewTransfer: UIView!
     @IBOutlet weak var viewButtonsView: UIView!
     @IBOutlet weak var viewSeparator: UIView!
     @IBOutlet weak var viewRichWorkspace: UIView!
     @IBOutlet weak var viewSection: UIView!
 
+    @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint!
     @IBOutlet weak var viewButtonsViewHeightConstraint: NSLayoutConstraint!
     @IBOutlet weak var viewSeparatorHeightConstraint: NSLayoutConstraint!
     @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint!
     @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint!
-
-    @IBOutlet weak var textViewRichWorkspace: UITextView!
-    @IBOutlet weak var labelSection: UILabel!
+    @IBOutlet weak var transferSeparatorBottomHeightConstraint: NSLayoutConstraint!
 
     weak var delegate: NCSectionHeaderMenuDelegate?
 
@@ -82,6 +88,19 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
 
         labelSection.text = ""
         viewSectionHeightConstraint.constant = 0
+
+        buttonTransfer.backgroundColor = .clear
+        buttonTransfer.setImage(nil, for: .normal)
+        buttonTransfer.layer.cornerRadius = 6
+        buttonTransfer.layer.masksToBounds = true
+        imageButtonTransfer.image = UIImage(systemName: "stop.circle")
+        imageButtonTransfer.tintColor = .white
+        labelTransfer.text = ""
+        progressTransfer.progress = 0
+        progressTransfer.tintColor = NCBrandColor.shared.brand
+        progressTransfer.trackTintColor = NCBrandColor.shared.brand.withAlphaComponent(0.2)
+        transferSeparatorBottom.backgroundColor = .separator
+        transferSeparatorBottomHeightConstraint.constant = 0.5
     }
 
     override func layoutSublayers(of layer: CALayer) {
@@ -167,6 +186,36 @@ 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
+
+        if isHidden {
+            viewTransferHeightConstraint.constant = 0
+        } else {
+            var image: UIImage?
+            if let ocId,
+               let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                image = NCUtility.shared.createFilePreviewImage(ocId: metadata.ocId, etag: metadata.etag, fileNameView: metadata.fileNameView, classFile: metadata.classFile, status: metadata.status, createPreviewMedia: true)?.darken()
+                if image == nil {
+                    image = UIImage(named: metadata.iconName)
+                    buttonTransfer.backgroundColor = .lightGray
+                } else {
+                    buttonTransfer.backgroundColor = .clear
+                }
+                buttonTransfer.setImage(image, for: .normal)
+            }
+            viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer
+            if let progress {
+                progressTransfer.progress = progress
+            }
+        }
+    }
+
     // MARK: - Section
 
     func setSectionHeight(_ size: CGFloat) {
@@ -193,16 +242,8 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
         delegate?.tapButtonMore(sender)
     }
 
-    @IBAction func touchUpInsideButton1(_ sender: Any) {
-       delegate?.tapButton1(sender)
-    }
-
-    @IBAction func touchUpInsideButton2(_ sender: Any) {
-        delegate?.tapButton2(sender)
-    }
-
-    @IBAction func touchUpInsideButton3(_ sender: Any) {
-        delegate?.tapButton3(sender)
+    @IBAction func touchUpTransfer(_ sender: Any) {
+       delegate?.tapButtonTransfer(sender)
     }
 
     @objc func touchUpInsideViewRichWorkspace(_ sender: Any) {
@@ -214,9 +255,7 @@ protocol NCSectionHeaderMenuDelegate: AnyObject {
     func tapButtonSwitch(_ sender: Any)
     func tapButtonOrder(_ sender: Any)
     func tapButtonMore(_ sender: Any)
-    func tapButton1(_ sender: Any)
-    func tapButton2(_ sender: Any)
-    func tapButton3(_ sender: Any)
+    func tapButtonTransfer(_ sender: Any)
     func tapRichWorkspace(_ sender: Any)
 }
 
@@ -225,9 +264,7 @@ extension NCSectionHeaderMenuDelegate {
     func tapButtonSwitch(_ sender: Any) {}
     func tapButtonOrder(_ sender: Any) {}
     func tapButtonMore(_ sender: Any) {}
-    func tapButton1(_ sender: Any) {}
-    func tapButton2(_ sender: Any) {}
-    func tapButton3(_ sender: Any) {}
+    func tapButtonTransfer(_ sender: Any) {}
     func tapRichWorkspace(_ sender: Any) {}
 }
 
@@ -351,3 +388,58 @@ protocol NCSectionFooterDelegate: AnyObject {
 extension NCSectionFooterDelegate {
     func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {}
 }
+
+// https://stackoverflow.com/questions/16278463/darken-an-uiimage
+public extension UIImage {
+
+    private enum BlendMode {
+        case multiply // This results in colors that are at least as dark as either of the two contributing sample colors
+        case screen // This results in colors that are at least as light as either of the two contributing sample colors
+    }
+
+    // A level of zero yeilds the original image, a level of 1 results in black
+    func darken(level: CGFloat = 0.5) -> UIImage? {
+        return blend(mode: .multiply, level: level)
+    }
+
+    // A level of zero yeilds the original image, a level of 1 results in white
+    func lighten(level: CGFloat = 0.5) -> UIImage? {
+        return blend(mode: .screen, level: level)
+    }
+
+    private func blend(mode: BlendMode, level: CGFloat) -> UIImage? {
+        let context = CIContext(options: nil)
+
+        var level = level
+        if level < 0 {
+            level = 0
+        } else if level > 1 {
+            level = 1
+        }
+
+        let filterName: String
+        switch mode {
+        case .multiply: // As the level increases we get less white
+            level = abs(level - 1.0)
+            filterName = "CIMultiplyBlendMode"
+        case .screen: // As the level increases we get more white
+            filterName = "CIScreenBlendMode"
+        }
+
+        let blender = CIFilter(name: filterName)!
+        let backgroundColor = CIColor(color: UIColor(white: level, alpha: 1))
+
+        guard let inputImage = CIImage(image: self) else { return nil }
+        blender.setValue(inputImage, forKey: kCIInputImageKey)
+
+        guard let backgroundImageGenerator = CIFilter(name: "CIConstantColorGenerator") else { return nil }
+        backgroundImageGenerator.setValue(backgroundColor, forKey: kCIInputColorKey)
+        guard let backgroundImage = backgroundImageGenerator.outputImage?.cropped(to: CGRect(origin: CGPoint.zero, size: self.size)) else { return nil }
+        blender.setValue(backgroundImage, forKey: kCIInputBackgroundImageKey)
+
+        guard let blendedImage = blender.outputImage else { return nil }
+
+        guard let cgImage = context.createCGImage(blendedImage, from: blendedImage.extent) else { return nil }
+        return UIImage(cgImage: cgImage)
+    }
+}

+ 98 - 28
iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib

@@ -3,7 +3,7 @@
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
         <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"/>
@@ -12,7 +12,7 @@
         <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">
-            <rect key="frame" x="0.0" y="0.0" width="574" height="211"/>
+            <rect key="frame" x="0.0" y="0.0" width="574" height="438"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
                 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s4I-Jo-yCE">
@@ -24,8 +24,7 @@
                                 <constraint firstAttribute="width" constant="25" id="D76-X9-Tw9"/>
                                 <constraint firstAttribute="height" constant="25" id="izT-Ru-XYG"/>
                             </constraints>
-                            <color key="tintColor" systemColor="systemGrayColor"/>
-                            <state key="normal" image="list.bullet" catalog="system"/>
+                            <state key="normal" image="switchList"/>
                             <connections>
                                 <action selector="touchUpInsideSwitch:" destination="tys-A2-nDX" eventType="touchUpInside" id="iT8-1j-fib"/>
                             </connections>
@@ -33,7 +32,9 @@
                         <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k">
                             <rect key="frame" x="45" y="11" width="163" height="28"/>
                             <fontDescription key="fontDescription" type="system" pointSize="13"/>
-                            <state key="normal" title="Sort by name (from A to Z)"/>
+                            <state key="normal" title="Sort by name (from A to Z)">
+                                <color key="titleColor" systemColor="darkTextColor"/>
+                            </state>
                             <connections>
                                 <action selector="touchUpInsideOrder:" destination="tys-A2-nDX" eventType="touchUpInside" id="oiL-3O-hMQ"/>
                             </connections>
@@ -68,20 +69,8 @@
                         <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/>
                     </constraints>
                 </view>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f9U-NY-4OS">
-                    <rect key="frame" x="0.0" y="191" width="574" height="20"/>
-                    <constraints>
-                        <constraint firstAttribute="height" constant="20" id="ZcL-Wd-xhN"/>
-                    </constraints>
-                </view>
-                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mB5-5n-AL9">
-                    <rect key="frame" x="10" y="193" width="554" height="18"/>
-                    <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
-                    <nil key="textColor"/>
-                    <nil key="highlightedColor"/>
-                </label>
                 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NC1-5C-E5z" userLabel="View RichWorkspace">
-                    <rect key="frame" x="0.0" y="141" width="574" height="50"/>
+                    <rect key="frame" x="0.0" y="318" width="574" height="50"/>
                     <subviews>
                         <textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="pYo-pF-MGv">
                             <rect key="frame" x="5" y="0.0" width="564" height="50"/>
@@ -99,31 +88,109 @@
                         <constraint firstAttribute="bottom" secondItem="pYo-pF-MGv" secondAttribute="bottom" id="t4r-dA-VyW"/>
                     </constraints>
                 </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="I6b-6a-TKg" userLabel="View Transfer">
+                    <rect key="frame" x="0.0" y="368" width="574" height="50"/>
+                    <subviews>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="McE-3D-mc5">
+                            <rect key="frame" x="0.0" y="49" width="574" height="1"/>
+                            <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="1" id="bJs-JY-WbC"/>
+                            </constraints>
+                        </view>
+                        <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aS9-DV-CXI">
+                            <rect key="frame" x="10" y="8" width="30" height="30"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="30" id="AkI-Uq-9rJ"/>
+                                <constraint firstAttribute="width" constant="30" id="S1K-Qo-eU9"/>
+                            </constraints>
+                            <connections>
+                                <action selector="touchUpTransfer:" destination="tys-A2-nDX" eventType="touchUpInside" id="8Vb-xV-6eT"/>
+                            </connections>
+                        </button>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="stop.circle" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="Pgk-le-540">
+                            <rect key="frame" x="15" y="13.5" width="20" height="19"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="20" id="3SW-CS-jiT"/>
+                                <constraint firstAttribute="height" constant="20" id="xVb-tv-en7"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="text" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eER-Zj-8iK">
+                            <rect key="frame" x="50" y="14" width="514" height="18"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FOe-YO-km8">
+                            <rect key="frame" x="-1" y="46" width="576" height="4"/>
+                        </progressView>
+                    </subviews>
+                    <constraints>
+                        <constraint firstItem="Pgk-le-540" firstAttribute="centerX" secondItem="aS9-DV-CXI" secondAttribute="centerX" id="3fo-qC-duA"/>
+                        <constraint firstAttribute="trailing" secondItem="FOe-YO-km8" secondAttribute="trailing" constant="-1" id="3gk-sW-WeV"/>
+                        <constraint firstAttribute="bottom" secondItem="McE-3D-mc5" secondAttribute="bottom" id="697-ky-07J"/>
+                        <constraint firstAttribute="height" constant="50" id="86k-97-oGl"/>
+                        <constraint firstItem="Pgk-le-540" firstAttribute="centerY" secondItem="aS9-DV-CXI" secondAttribute="centerY" id="9Lm-Ql-nt0"/>
+                        <constraint firstAttribute="bottom" secondItem="FOe-YO-km8" secondAttribute="bottom" id="ESd-Gt-Xcc"/>
+                        <constraint firstItem="eER-Zj-8iK" firstAttribute="centerY" secondItem="aS9-DV-CXI" secondAttribute="centerY" id="Ko8-gC-6Zd"/>
+                        <constraint firstItem="aS9-DV-CXI" firstAttribute="centerY" secondItem="I6b-6a-TKg" secondAttribute="centerY" constant="-2" id="Mli-mT-whp"/>
+                        <constraint firstAttribute="trailing" secondItem="eER-Zj-8iK" secondAttribute="trailing" constant="10" id="QyZ-Z4-0tw"/>
+                        <constraint firstItem="McE-3D-mc5" firstAttribute="leading" secondItem="I6b-6a-TKg" secondAttribute="leading" id="TRt-jh-ZEo"/>
+                        <constraint firstAttribute="trailing" secondItem="McE-3D-mc5" secondAttribute="trailing" id="fjz-bk-gcP"/>
+                        <constraint firstItem="eER-Zj-8iK" firstAttribute="leading" secondItem="aS9-DV-CXI" secondAttribute="trailing" constant="10" id="idn-9t-2Ap"/>
+                        <constraint firstItem="aS9-DV-CXI" firstAttribute="leading" secondItem="I6b-6a-TKg" secondAttribute="leading" constant="10" id="jIP-Fr-dnx"/>
+                        <constraint firstItem="FOe-YO-km8" firstAttribute="leading" secondItem="I6b-6a-TKg" secondAttribute="leading" constant="-1" id="oDR-51-azX"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f9U-NY-4OS">
+                    <rect key="frame" x="0.0" y="418" width="574" height="20"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mB5-5n-AL9">
+                            <rect key="frame" x="10" y="2" width="554" height="18"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="mB5-5n-AL9" secondAttribute="trailing" constant="10" id="Cct-8N-ghQ"/>
+                        <constraint firstAttribute="height" constant="20" id="ZcL-Wd-xhN"/>
+                        <constraint firstItem="mB5-5n-AL9" firstAttribute="leading" secondItem="f9U-NY-4OS" secondAttribute="leading" constant="10" id="xQp-zk-G00"/>
+                        <constraint firstAttribute="bottom" secondItem="mB5-5n-AL9" secondAttribute="bottom" id="ySZ-Z1-BQ1"/>
+                    </constraints>
+                </view>
             </subviews>
             <viewLayoutGuide key="safeArea" id="pm7-uW-mZE"/>
             <constraints>
                 <constraint firstItem="f9U-NY-4OS" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="7kv-IL-kwZ"/>
                 <constraint firstItem="s4I-Jo-yCE" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="CaM-Eb-nHq"/>
                 <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="CyS-jg-0vc"/>
-                <constraint firstAttribute="bottom" secondItem="mB5-5n-AL9" secondAttribute="bottom" id="EFG-nD-yUb"/>
                 <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="f9U-NY-4OS" secondAttribute="trailing" id="GbG-un-mCe"/>
+                <constraint firstItem="f9U-NY-4OS" firstAttribute="top" secondItem="I6b-6a-TKg" secondAttribute="bottom" id="JKM-HM-WpK"/>
                 <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="NiW-2m-3HS"/>
-                <constraint firstAttribute="trailing" secondItem="mB5-5n-AL9" secondAttribute="trailing" constant="10" id="OO6-Qd-6hP"/>
                 <constraint firstItem="NC1-5C-E5z" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="QpF-nE-s7J"/>
                 <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="NC1-5C-E5z" secondAttribute="trailing" id="UH6-8N-JUD"/>
-                <constraint firstItem="mB5-5n-AL9" firstAttribute="leading" secondItem="tys-A2-nDX" secondAttribute="leading" constant="10" id="bDt-8i-Gxr"/>
+                <constraint firstItem="s4I-Jo-yCE" firstAttribute="top" secondItem="pm7-uW-mZE" secondAttribute="top" id="bSn-X7-YZH"/>
+                <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="I6b-6a-TKg" secondAttribute="trailing" id="eYb-BW-clZ"/>
                 <constraint firstItem="LZu-Te-clJ" firstAttribute="top" secondItem="s4I-Jo-yCE" secondAttribute="bottom" constant="-1" id="ede-24-v8F"/>
                 <constraint firstItem="pm7-uW-mZE" firstAttribute="bottom" secondItem="f9U-NY-4OS" secondAttribute="bottom" id="eyu-CE-rTX"/>
                 <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="s4I-Jo-yCE" secondAttribute="trailing" id="oCg-UW-8TQ"/>
-                <constraint firstItem="NC1-5C-E5z" firstAttribute="bottom" secondItem="f9U-NY-4OS" secondAttribute="top" id="pmY-5s-Pv2"/>
-                <constraint firstItem="s4I-Jo-yCE" firstAttribute="top" secondItem="pm7-uW-mZE" secondAttribute="top" id="pzr-LC-JPk"/>
+                <constraint firstItem="I6b-6a-TKg" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="pap-j1-yYG"/>
+                <constraint firstItem="NC1-5C-E5z" firstAttribute="bottom" secondItem="I6b-6a-TKg" secondAttribute="top" id="pmY-5s-Pv2"/>
             </constraints>
             <connections>
                 <outlet property="buttonMore" destination="D0O-wK-14O" id="eEx-3R-zCS"/>
                 <outlet property="buttonOrder" destination="0bo-yl-t5k" id="Kbw-BG-73C"/>
                 <outlet property="buttonSwitch" destination="1LD-cd-zhc" id="Ec2-cM-CoY"/>
+                <outlet property="buttonTransfer" destination="aS9-DV-CXI" id="Qsu-aQ-Vh7"/>
+                <outlet property="imageButtonTransfer" destination="Pgk-le-540" id="ljU-AW-YSt"/>
                 <outlet property="labelSection" destination="mB5-5n-AL9" id="uxf-bN-nZA"/>
+                <outlet property="labelTransfer" destination="eER-Zj-8iK" id="ARz-bB-Hg9"/>
+                <outlet property="progressTransfer" destination="FOe-YO-km8" id="vyd-rg-H9B"/>
                 <outlet property="textViewRichWorkspace" destination="pYo-pF-MGv" id="2h4-LP-T1z"/>
+                <outlet property="transferSeparatorBottom" destination="McE-3D-mc5" id="kJU-kh-04F"/>
+                <outlet property="transferSeparatorBottomHeightConstraint" destination="bJs-JY-WbC" id="P9i-Em-ycA"/>
                 <outlet property="viewButtonsView" destination="s4I-Jo-yCE" id="FOI-ZK-1oj"/>
                 <outlet property="viewButtonsViewHeightConstraint" destination="vvG-dH-6c1" id="SEQ-Tn-EE0"/>
                 <outlet property="viewRichWorkspace" destination="NC1-5C-E5z" id="NyN-tr-sJl"/>
@@ -132,18 +199,21 @@
                 <outlet property="viewSectionHeightConstraint" destination="ZcL-Wd-xhN" id="RDs-yy-I6W"/>
                 <outlet property="viewSeparator" destination="LZu-Te-clJ" id="rz1-2Q-vEK"/>
                 <outlet property="viewSeparatorHeightConstraint" destination="VuP-sT-hUI" id="QHV-oY-E5w"/>
+                <outlet property="viewTransfer" destination="I6b-6a-TKg" id="Hqx-QM-daQ"/>
+                <outlet property="viewTransferHeightConstraint" destination="86k-97-oGl" id="Pjb-mP-5dn"/>
             </connections>
-            <point key="canvasLocation" x="368" y="55.322338830584712"/>
+            <point key="canvasLocation" x="345.60000000000002" y="56.671664167916049"/>
         </collectionReusableView>
     </objects>
     <resources>
-        <image name="list.bullet" catalog="system" width="128" height="87"/>
         <image name="moreBig" width="50" height="50"/>
+        <image name="stop.circle" catalog="system" width="128" height="123"/>
+        <image name="switchList" width="25" height="25"/>
+        <systemColor name="darkTextColor">
+            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
         <systemColor name="labelColor">
             <color red="0.0" green="0.0" blue="0.0" 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>
     </resources>
 </document>

+ 2 - 1
iOSClient/NCGlobal.swift

@@ -167,6 +167,7 @@ class NCGlobal: NSObject {
     // Standard height sections header/footer
     //
     let heightButtonsView: CGFloat                  = 50
+    let heightHeaderTransfer: CGFloat               = 50
     let heightSection: CGFloat                      = 30
     let heightFooter: CGFloat                       = 1
     let heightFooterButton: CGFloat                 = 30
@@ -347,7 +348,7 @@ class NCGlobal: NSObject {
     @objc let notificationCenterUploadedFile                    = "uploadedFile"                    // userInfo: ocId, serverUrl, account, fileName, ocIdTemp, error
     let notificationCenterUploadCancelFile                      = "uploadCancelFile"                // userInfo: ocId, serverUrl, account
 
-    let notificationCenterProgressTask                          = "progressTask"                    // userInfo: account, ocId, serverUrl, status, progress, totalBytes, totalBytesExpected
+    let notificationCenterProgressTask                          = "progressTask"                    // userInfo: account, ocId, serverUrl, status, chunk, e2eEncrypted, progress, totalBytes, totalBytesExpected
 
     let notificationCenterCreateFolder                          = "createFolder"                    // userInfo: ocId, serverUrl, account, e2ee, withPush
     let notificationCenterDeleteFile                            = "deleteFile"                      // userInfo: account, ocIds, error

+ 0 - 1
iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift

@@ -186,7 +186,6 @@ class NCNetworkingE2EEUpload: NSObject {
         return await withCheckedContinuation({ continuation in
             NCNetworking.shared.uploadFile(metadata: metadata, fileNameLocalPath: fileNameLocalPath, withUploadComplete: false, addCustomHeaders: ["e2e-token": e2eToken]) {
                 uploadE2EEDelegate?.start()
-                NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_e2ee_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: NCGlobal.shared.dismissAfterSecond, priority: .max)
             } progressHandler: { totalBytesExpected, totalBytes, fractionCompleted in
                 uploadE2EEDelegate?.uploadE2EEProgress(totalBytesExpected, totalBytes, fractionCompleted)
             } completion: { account, ocId, etag, date, size, allHeaderFields, afError, error in

+ 19 - 21
iOSClient/Networking/NCNetworking.swift

@@ -45,6 +45,11 @@ class NCNetworking: NSObject, NKCommonDelegate {
         return instance
     }()
 
+    public struct TransferInForegorund {
+        var ocId: String
+        var progress: Float
+    }
+
     weak var delegate: NCNetworkingDelegate?
 
     var lastReachability: Bool = true
@@ -52,6 +57,7 @@ class NCNetworking: NSObject, NKCommonDelegate {
     let downloadRequest = ThreadSafeDictionary<String,DownloadRequest>()
     let uploadRequest = ThreadSafeDictionary<String,UploadRequest>()
     let uploadMetadataInBackground = ThreadSafeDictionary<String,tableMetadata>()
+    var transferInForegorund: TransferInForegorund?
 
     lazy var nkBackground: NKBackground = {
         let nckb = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance)
@@ -340,7 +346,7 @@ class NCNetworking: NSObject, NKCommonDelegate {
             NCManageDatabase.shared.addMetadata(tableMetadata.init(value: metadata))
         }
 
-        if metadata.isDownloadUpload { return }
+        if metadata.isInTransfer { return }
 
         NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload, sessionError: "", sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusInDownload)
 
@@ -351,16 +357,14 @@ class NCNetworking: NSObject, NKCommonDelegate {
             self.downloadRequest[fileNameLocalPath] = request
 
             NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusDownloading)
-            if notificationCenterProgressTask {
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadStartFile, userInfo: ["ocId":metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account])
-            }
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadStartFile, userInfo: ["ocId":metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account])
             
         }, taskHandler: { (_) in
             
         }, progressHandler: { (progress) in
             
             if notificationCenterProgressTask {
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, object: nil, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":metadata.serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInDownload), "progress":NSNumber(value: progress.fractionCompleted), "totalBytes":NSNumber(value: progress.totalUnitCount), "totalBytesExpected":NSNumber(value: progress.completedUnitCount)])
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, object: nil, userInfo: ["account": metadata.account, "ocId": metadata.ocId, "fileName": metadata.fileName, "serverUrl": metadata.serverUrl, "chunk": metadata.chunk, "e2eEncrypted": metadata.e2eEncrypted, "status": NSNumber(value: NCGlobal.shared.metadataStatusInDownload), "progress": NSNumber(value: progress.fractionCompleted), "totalBytes": NSNumber(value: progress.totalUnitCount), "totalBytesExpected": NSNumber(value: progress.completedUnitCount)])
             }
             progressHandler(progress)
                                         
@@ -371,9 +375,7 @@ class NCNetworking: NSObject, NKCommonDelegate {
             if afError?.isExplicitlyCancelledError ?? false {
 
                 NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: "", sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal)
-                if notificationCenterProgressTask {
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account])
-                }
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account])
 
             } else if error == .success {
 
@@ -385,16 +387,12 @@ class NCNetworking: NSObject, NKCommonDelegate {
                 }
                 CCUtility.setExif(metadata) { _, _, _, _, _ in }
 #endif
-                if notificationCenterProgressTask {
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "selector": selector, "error": error])
-                }
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "selector": selector, "error": error])
 
             } else {
 
                 NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: error.errorDescription, sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusDownloadError)
-                if notificationCenterProgressTask {
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "selector": selector, "error": error])
-                }
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "selector": selector, "error": error])
             }
 
             DispatchQueue.main.async { completion(afError, error) }
@@ -639,6 +637,8 @@ class NCNetworking: NSObject, NKCommonDelegate {
                         "fileName": metadata.fileName,
                         "serverUrl": serverUrl,
                         "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload),
+                        "chunk": metadata.chunk,
+                        "e2eEncrypted": metadata.e2eEncrypted,
                         "progress": NSNumber(value: progress),
                         "totalBytes": NSNumber(value: totalBytes),
                         "totalBytesExpected": NSNumber(value: totalBytesExpected)])
@@ -682,15 +682,13 @@ class NCNetworking: NSObject, NKCommonDelegate {
 
         if metadata.session == NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload || metadata.chunk {
 
-            guard let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) else { return }
-
-            if let request = uploadRequest[fileNameLocalPath] {
+            CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
+            if let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView),
+               let request = uploadRequest[fileNameLocalPath] {
                 request.cancel()
-            } else {
-                CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
-                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account])
             }
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account])
 
             return completion()
         }

+ 11 - 2
iOSClient/Networking/NCNetworkingChunkedUpload.swift

@@ -43,7 +43,6 @@ extension NCNetworking {
 
         var filesNames = NCManageDatabase.shared.getChunks(account: metadata.account, ocId: metadata.ocId)
         if filesNames.count == 0 {
-            NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_chunk_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: .infinity, priority: .max)
             filesNames = NextcloudKit.shared.nkCommonInstance.chunkedFile(inputDirectory: directoryProviderStorageOcId, outputDirectory: directoryProviderStorageOcId, fileName: metadata.fileName, chunkSizeMB: chunkSize)
             if filesNames.count > 0 {
                 NCManageDatabase.shared.addChunks(account: metadata.account, ocId: metadata.ocId, chunkFolder: chunkFolder, fileNames: filesNames)
@@ -75,6 +74,14 @@ extension NCNetworking {
             for fileName in filesNames {
 
                 let serverUrlFileName = chunkFolderPath + "/" + fileName
+                let fileSize = CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: fileName)
+                // ops! the upload has probably been cancelled
+                if fileSize == 0 {
+                    CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
+                    NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId)
+                    NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                    return completion(NKError())
+                }
                 let fileNameChunkLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)!
 
                 var size: Int64?
@@ -97,7 +104,7 @@ extension NCNetworking {
 
                     if let size = size {
                         let totalBytesExpected = metadata.size
-                        let totalBytes = size + progress.completedUnitCount
+                        let totalBytes = size
                         let fractionCompleted = Double(totalBytes) / Double(totalBytesExpected)
 
                         NotificationCenter.default.postOnMainThread(
@@ -109,6 +116,8 @@ extension NCNetworking {
                                 "fileName": metadata.fileName,
                                 "serverUrl": metadata.serverUrl,
                                 "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload),
+                                "chunk": metadata.chunk,
+                                "e2eEncrypted": metadata.e2eEncrypted,
                                 "progress": NSNumber(value: fractionCompleted),
                                 "totalBytes": NSNumber(value: totalBytes),
                                 "totalBytesExpected": NSNumber(value: totalBytesExpected)])

+ 3 - 0
iOSClient/Networking/NCNetworkingProcessUpload.swift

@@ -312,6 +312,9 @@ class NCNetworkingProcessUpload: NSObject {
 
         // metadataStatusUploading OR metadataStatusInUpload (FOREGROUND)
         let metadatasUploading = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "session == %@ AND (status == %d OR status == %d)", NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload, NCGlobal.shared.metadataStatusUploading, NCGlobal.shared.metadataStatusInUpload))
+        if metadatasUploading.isEmpty {
+            NCNetworking.shared.transferInForegorund = nil
+        }
         for metadata in metadatasUploading {
             let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
             if NCNetworking.shared.uploadRequest[fileNameLocalPath] == nil {

+ 2 - 0
iOSClient/Select/NCSelect.swift

@@ -606,6 +606,8 @@ extension NCSelect: UICollectionViewDataSource {
                 header.setRichWorkspaceHeight(heightHeaderRichWorkspace)
                 header.setRichWorkspaceText(richWorkspaceText)
 
+                header.setViewTransfer(isHidden: true)
+
                 header.setSectionHeight(heightHeaderSection)
                 if heightHeaderSection == 0 {
                     header.labelSection.text = ""

BIN
iOSClient/Supporting Files/ar.lproj/Localizable.strings


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

@@ -192,8 +192,6 @@
 "_print_"                   = "Print";
 "_alias_"                   = "Alias";
 "_alias_footer_"            = "Give your account names a descriptive name such as Home, Office, School …";
-"_upload_chunk_"            = "The file you are uploading is large, keep the app in the foreground until complete";
-"_upload_e2ee_"             = "The file you are uploading is encrypted, keep the app in the foreground until complete";
 "_chunk_size_mb_"           = "Chunk size in MB";
 "_chunk_footer_title_"      = "Chunked file upload (0 is disabled)\nImportant: the chunked upload works only when the app is \"active\".";
 "_privacy_legal_"           = "Privacy and Legal Policy";
@@ -969,6 +967,7 @@
 "_add_subtitle_"            = "Add an external subtitle";
 "_add_audio_"               = "Add an external audio";
 "_maintenance_mode_"        = "Server is currently in maintenance mode";
+"_upload_foreground_msg_"   = "Do not close %@ to complete the transfer …";
 
 // Tip
 "_tip_pdf_thumbnails_"      = "Swipe left from the right edge of the screen to show the thumbnails.";

BIN
iOSClient/Supporting Files/gl.lproj/Localizable.strings


+ 1 - 1
iOSClient/Transfers/NCTransferCell.swift

@@ -80,7 +80,7 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP
         imageItem.layer.cornerRadius = 6
         imageItem.layer.masksToBounds = true
 
-        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.tintColor = NCBrandColor.shared.brand
         progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5)
         progressView.trackTintColor = .clear
 

+ 1 - 0
iOSClient/Transfers/NCTransfers.swift

@@ -39,6 +39,7 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
         enableSearchBar = false
         headerMenuButtonsView = false
         headerRichWorkspaceDisable = true
+        headerMenuTransferView = true
         emptyImage = NCUtility.shared.loadImage(named: "arrow.left.arrow.right", color: .gray, size: UIScreen.main.bounds.width)
         emptyTitle = "_no_transfer_"
         emptyDescription = "_no_transfer_sub_"

+ 1 - 0
iOSClient/Trash/NCTrash+CollectionView.swift

@@ -159,6 +159,7 @@ extension NCTrash: UICollectionViewDataSource {
             header.setButtonsView(height: NCGlobal.shared.heightButtonsView)
             header.setRichWorkspaceHeight(0)
             header.setSectionHeight(0)
+            header.setViewTransfer(isHidden: true)
 
             return header
 

+ 12 - 0
iOSClient/Utility/NCCameraRoll.swift

@@ -48,6 +48,10 @@ class NCCameraRoll: NSObject {
                 metadataSource.date = date
             }
             metadataSource.chunk = chunckSize != 0 && metadata.size > chunckSize
+            metadataSource.e2eEncrypted = metadata.isDirectoryE2EE
+            if metadataSource.chunk || metadataSource.e2eEncrypted {
+                metadataSource.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
+            }
             metadataSource.isExtractFile = true
             if let metadata = NCManageDatabase.shared.addMetadata(metadataSource) {
                 metadatas.append(metadata)
@@ -95,6 +99,10 @@ class NCCameraRoll: NSObject {
                 var metadataReturn = metadata
                 if modifyMetadataForUpload {
                     metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
+                    metadata.e2eEncrypted = metadata.isDirectoryE2EE
+                    if metadata.chunk || metadata.e2eEncrypted {
+                        metadata.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
+                    }
                     metadata.isExtractFile = true
                     if let metadata = NCManageDatabase.shared.addMetadata(metadata) {
                         metadataReturn = metadata
@@ -260,6 +268,10 @@ class NCCameraRoll: NSObject {
                 metadataLivePhoto.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
                 metadataLivePhoto.status = metadata.status
                 metadataLivePhoto.chunk = chunckSize != 0 && metadata.size > chunckSize
+                metadataLivePhoto.e2eEncrypted = metadata.isDirectoryE2EE
+                if metadataLivePhoto.chunk || metadataLivePhoto.e2eEncrypted {
+                    metadataLivePhoto.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
+                }
                 metadataLivePhoto.creationDate = metadata.creationDate
                 metadataLivePhoto.date = metadata.date
                 metadataLivePhoto.uploadDate = metadata.uploadDate

+ 1 - 1
iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift

@@ -113,7 +113,7 @@ class NCViewerMediaPage: UIViewController {
         pageViewController.view.addGestureRecognizer(singleTapGestureRecognizer)
         pageViewController.view.addGestureRecognizer(longtapGestureRecognizer)
 
-        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.tintColor = NCBrandColor.shared.brand
         progressView.trackTintColor = .clear
         progressView.progress = 0