Pārlūkot izejas kodu

Merge pull request #1850 from nextcloud/feature/select-offline

Set available offline enhancements
Marino Faggiana 3 gadi atpakaļ
vecāks
revīzija
aa5cf41277

+ 16 - 15
iOSClient/Data/NCDataSource.swift

@@ -114,16 +114,13 @@ class NCDataSource: NSObject {
             }
 
             // is Local / offline
-            if !metadata.directory {
-                let size = CCUtility.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView)
-                if size > 0 {
-                    let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                    if tableLocalFile == nil && size == metadata.size {
-                        NCManageDatabase.shared.addLocalFile(metadata: metadata)
-                    }
-                    if tableLocalFile?.offline ?? false {
-                        metadataOffLine.append(metadata.ocId)
-                    }
+            if !metadata.directory, CCUtility.fileProviderStorageExists(metadata) {
+                let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                if tableLocalFile == nil {
+                    NCManageDatabase.shared.addLocalFile(metadata: metadata)
+                }
+                if tableLocalFile?.offline ?? false {
+                    metadataOffLine.append(metadata.ocId)
                 }
             }
 
@@ -183,15 +180,19 @@ class NCDataSource: NSObject {
 
         var index: Int?
 
-        if ocIdTemp != nil {
-            index = self.getIndexMetadata(ocId: ocIdTemp!)
+        if let ocIdTemp = ocIdTemp {
+            index = self.getIndexMetadata(ocId: ocIdTemp)
         } else {
             index = self.getIndexMetadata(ocId: ocId)
         }
 
-        if index != nil {
-            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                metadatas[index!] = metadata
+        guard let index = index, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return nil }
+        metadatas[index] = metadata
+
+        if CCUtility.fileProviderStorageExists(metadata) {
+            let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+            if tableLocalFile?.offline ?? false {
+                metadataOffLine.append(metadata.ocId)
             }
         }
 

+ 1 - 1
iOSClient/Data/NCManageDatabase.swift

@@ -1131,7 +1131,7 @@ class NCManageDatabase: NSObject {
         do {
             try realm.safeWrite {
 
-                let addObject = tableLocalFile()
+                let addObject = getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) ?? tableLocalFile()
 
                 addObject.account = metadata.account
                 addObject.etag = metadata.etag

+ 9 - 7
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -450,13 +450,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     @objc func downloadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let _ = userInfo["errorCode"] as? Int, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if let row = dataSource.reloadMetadata(ocId: metadata.ocId) {
-                let indexPath = IndexPath(row: row, section: 0)
-                if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
-                    collectionView?.reloadItems(at: [indexPath])
-                }
-            }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let _ = userInfo["errorCode"] as? Int,
+              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              let row = dataSource.reloadMetadata(ocId: metadata.ocId)
+        else { return }
+        let indexPath = IndexPath(row: row, section: 0)
+        if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
+            collectionView?.reloadItems(at: [indexPath])
         }
     }
 

+ 18 - 2
iOSClient/Main/Collection Common/NCSelectableNavigationView.swift

@@ -42,10 +42,13 @@ protocol NCSelectableNavigationView: AnyObject {
     var titleCurrentFolder: String { get }
     var navigationItem: UINavigationItem { get }
 
+    var selectActions: [NCMenuAction] { get }
+
+    func reloadDataSource()
+    func setNavigationItem()
+
     func tapSelectMenu()
     func tapSelect()
-    func setNavigationItem()
-    var selectActions: [NCMenuAction] { get }
 }
 
 extension NCSelectableNavigationView {
@@ -91,6 +94,7 @@ extension NCSelectableNavigationView where Self: UIViewController {
         guard !selectOcId.isEmpty else { return actions }
         var selectedMetadatas: [tableMetadata] = []
         var selectedMediaMetadatas: [tableMetadata] = []
+        var isAnyOffline = false
 
         for ocId in selectOcId {
             guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue }
@@ -98,6 +102,14 @@ extension NCSelectableNavigationView where Self: UIViewController {
             if [NCCommunicationCommon.typeClassFile.image.rawValue, NCCommunicationCommon.typeClassFile.video.rawValue].contains(metadata.classFile) {
                 selectedMediaMetadatas.append(metadata)
             }
+
+            guard !isAnyOffline else { continue }
+            if metadata.directory,
+               let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) {
+                isAnyOffline = directory.offline
+            } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
+                isAnyOffline = localFile.offline
+            } // else: file is not offline, continue
         }
 
         actions.append(.openInAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
@@ -105,6 +117,10 @@ extension NCSelectableNavigationView where Self: UIViewController {
         if !selectedMediaMetadatas.isEmpty {
             actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, completion: tapSelect))
         }
+        actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: {
+            self.reloadDataSource()
+            self.tapSelect()
+        }))
 
         actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, completion: tapSelect))
         actions.append(.copyAction(selectOcId: selectOcId, hudView: self.view, completion: tapSelect))

+ 106 - 126
iOSClient/Main/NCFunctionCenter.swift

@@ -29,10 +29,8 @@ import JGProgressHUD
 @objc class NCFunctionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelectDelegate {
     @objc public static let shared: NCFunctionCenter = {
         let instance = NCFunctionCenter()
-
         NotificationCenter.default.addObserver(instance, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
         NotificationCenter.default.addObserver(instance, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
-
         return instance
     }()
 
@@ -44,122 +42,121 @@ import JGProgressHUD
 
     @objc func downloadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let selector = userInfo["selector"] as? String, let errorCode = userInfo["errorCode"] as? Int, let errorDescription = userInfo["errorDescription"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-
-                if metadata.account != appDelegate.account { return }
-
-                if errorCode == 0 {
-
-                    switch selector {
-                    case NCGlobal.shared.selectorLoadFileQuickLook:
-
-                        let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
-                        CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath)
-
-                        var editingMode = false
-                        if #available(iOS 13.0, *) {
-                            editingMode = true
-                        }
-
-                        let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), editingMode: editingMode, metadata: metadata)
-                        let navigationController = UINavigationController(rootViewController: viewerQuickLook)
-                        navigationController.modalPresentationStyle = .overFullScreen
-
-                        self.appDelegate.window?.rootViewController?.present(navigationController, animated: true)
-
-                    case NCGlobal.shared.selectorLoadFileView:
-
-                        if UIApplication.shared.applicationState == UIApplication.State.active {
-
-                            if metadata.contentType.contains("opendocument") && !NCUtility.shared.isRichDocument(metadata) {
-
-                                self.openDocumentController(metadata: metadata)
-
-                            } else if metadata.classFile == NCCommunicationCommon.typeClassFile.compress.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue {
-
-                                self.openDocumentController(metadata: metadata)
-
-                            } else {
-
-                                if let viewController = self.appDelegate.activeViewController {
-                                    let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
-                                    NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon)
-                                }
-                            }
-                        }
-
-                    case NCGlobal.shared.selectorOpenIn:
-
-                        if UIApplication.shared.applicationState == UIApplication.State.active {
-
-                            self.openDocumentController(metadata: metadata)
-                        }
-
-                    case NCGlobal.shared.selectorLoadOffline:
-
-                        NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: true)
-
-                    case NCGlobal.shared.selectorPrint:
-
-                        printDocument(metadata: metadata)
-
-                    case NCGlobal.shared.selectorSaveAlbum:
-
-                        saveAlbum(metadata: metadata)
-
-                    case NCGlobal.shared.selectorSaveBackground:
-
-                        saveBackground(metadata: metadata)
-
-                    case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV:
-
-                        var metadata = metadata
-                        var metadataMOV = metadata
-                        guard let metadataTMP = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) else { break }
-
-                        if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoIMG {
-                            metadataMOV = metadataTMP
-                        }
-
-                        if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoMOV {
-                            metadata = metadataTMP
-                        }
-
-                        if CCUtility.fileProviderStorageExists(metadata) && CCUtility.fileProviderStorageExists(metadataMOV) {
-                            saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
-                        }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let selector = userInfo["selector"] as? String,
+              let errorCode = userInfo["errorCode"] as? Int,
+              let errorDescription = userInfo["errorDescription"] as? String,
+              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              metadata.account == appDelegate.account
+        else { return }
+
+        guard errorCode == 0 else {
+            // File do not exists on server, remove in local
+            if errorCode == NCGlobal.shared.errorResourceNotFound || errorCode == NCGlobal.shared.errorBadServerResponse {
+                do {
+                    try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
+                } catch { }
+                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                
+            } else {
+                NCContentPresenter.shared.messageNotification("_download_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
+            }
+            return
+        }
+        
+        switch selector {
+        case NCGlobal.shared.selectorLoadFileQuickLook:
+            let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
+            CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath)
+
+            var editingMode = false
+            if #available(iOS 13.0, *) {
+                editingMode = true
+            }
 
-                    case NCGlobal.shared.selectorSaveAsScan:
+            let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), editingMode: editingMode, metadata: metadata)
+            let navigationController = UINavigationController(rootViewController: viewerQuickLook)
+            navigationController.modalPresentationStyle = .overFullScreen
 
-                        saveAsScan(metadata: metadata)
+            self.appDelegate.window?.rootViewController?.present(navigationController, animated: true)
 
-                    case NCGlobal.shared.selectorOpenDetail:
+        case NCGlobal.shared.selectorLoadFileView:
+            guard UIApplication.shared.applicationState == UIApplication.State.active else { break }
 
-                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId])
+            if metadata.contentType.contains("opendocument") && !NCUtility.shared.isRichDocument(metadata) {
+                self.openDocumentController(metadata: metadata)
+            } else if metadata.classFile == NCCommunicationCommon.typeClassFile.compress.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue {
+                self.openDocumentController(metadata: metadata)
+            } else {
+                if let viewController = self.appDelegate.activeViewController {
+                    let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
+                    NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: imageIcon)
+                }
+            }
+            
+        case NCGlobal.shared.selectorOpenIn:
+            if UIApplication.shared.applicationState == UIApplication.State.active {
+                self.openDocumentController(metadata: metadata)
+            }
+            
+        case NCGlobal.shared.selectorLoadOffline:
+            NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: true)
+            
+        case NCGlobal.shared.selectorPrint:
+            printDocument(metadata: metadata)
+            
+        case NCGlobal.shared.selectorSaveAlbum:
+            saveAlbum(metadata: metadata)
+            
+        case NCGlobal.shared.selectorSaveBackground:
+            saveBackground(metadata: metadata)
+            
+        case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV:
 
-                    default:
+            var metadata = metadata
+            var metadataMOV = metadata
+            guard let metadataTMP = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) else { break }
 
-                        break
-                    }
+            if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoIMG {
+                metadataMOV = metadataTMP
+            }
 
-                } else {
+            if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoMOV {
+                metadata = metadataTMP
+            }
 
-                    // File do not exists on server, remove in local
-                    if errorCode == NCGlobal.shared.errorResourceNotFound || errorCode == NCGlobal.shared.errorBadServerResponse {
+            if CCUtility.fileProviderStorageExists(metadata) && CCUtility.fileProviderStorageExists(metadataMOV) {
+                saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
+            }
 
-                        do {
-                            try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
-                        } catch { }
+        case NCGlobal.shared.selectorSaveAsScan:
+            saveAsScan(metadata: metadata)
 
-                        NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                        NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+        case NCGlobal.shared.selectorOpenDetail:
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId])
 
-                    } else {
+        default:
+            break
+        }
+    }
 
-                        NCContentPresenter.shared.messageNotification("_download_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
-                    }
-                }
+    func setMetadataAvalableOffline(_ metadata: tableMetadata, isOffline: Bool) {
+        let serverUrl = metadata.serverUrl + "/" + metadata.fileName
+        if isOffline {
+            if metadata.directory {
+                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: false, account: self.appDelegate.account)
+            } else {
+                NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: false)
+            }
+        } else if metadata.directory {
+            NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: self.appDelegate.account)
+            NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
+        } else {
+            NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
+            if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
+                NCNetworking.shared.download(metadata: metadataLivePhoto, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
             }
         }
     }
@@ -638,26 +635,9 @@ import JGProgressHUD
         }
 
         let offline = UIAction(title: titleOffline, image: UIImage(systemName: "tray.and.arrow.down")) { _ in
-            if isOffline {
-                if metadata.directory {
-                    NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: false, account: self.appDelegate.account)
-                } else {
-                    NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: false)
-                }
-            } else {
-                if metadata.directory {
-                    NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: self.appDelegate.account)
-                    NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
-                } else {
-                    NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                    if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                        NCNetworking.shared.download(metadata: metadataLivePhoto, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                    }
-                }
-            }
-
-            if viewController is NCCollectionViewCommon {
-                (viewController as! NCCollectionViewCommon).reloadDataSource()
+            self.setMetadataAvalableOffline(metadata, isOffline: isOffline)
+            if let viewController = viewController as? NCCollectionViewCommon {
+                viewController.reloadDataSource()
             }
         }
 

+ 7 - 36
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -40,17 +40,13 @@ extension NCCollectionViewCommon {
         let serverUrl = metadata.serverUrl + "/" + metadata.fileName
         let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
         let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
-        var isOffline = false
+        let isOffline: Bool
 
-        if metadata.directory {
-            if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
-                isOffline = directory.offline
-            }
-        } else {
-            if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
-                isOffline = localFile.offline
-            }
-        }
+        if metadata.directory, let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
+            isOffline = directory.offline
+        } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
+            isOffline = localFile.offline
+        } else { isOffline = false }
 
         let editors = NCUtility.shared.isDirectEditing(account: metadata.account, contentType: metadata.contentType)
         let isRichDocument = NCUtility.shared.isRichDocument(metadata)
@@ -111,32 +107,7 @@ extension NCCollectionViewCommon {
         // OFFLINE
         //
         if !isFolderEncrypted {
-            actions.append(
-                NCMenuAction(
-                    title: isOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: ""),
-                    icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
-                    action: { _ in
-                        if isOffline {
-                            if metadata.directory {
-                                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: false, account: self.appDelegate.account)
-                            } else {
-                                NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: false)
-                            }
-                        } else {
-                            if metadata.directory {
-                                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: self.appDelegate.account)
-                                NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
-                            } else {
-                                NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                                if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                                    NCNetworking.shared.download(metadata: metadataLivePhoto, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                                }
-                            }
-                        }
-                        self.reloadDataSource()
-                    }
-                )
-            )
+            actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: self, completion: self.reloadDataSource))
         }
 
         //

+ 25 - 0
iOSClient/Menu/NCMenuAction.swift

@@ -165,6 +165,31 @@ extension NCMenuAction {
         )
     }
 
+    /// Set (or remove) a file as *available offline*. Downloads the file if not downloaded already
+    static func setAvailableOfflineAction(selectedMetadatas: [tableMetadata], isAnyOffline: Bool, viewController: UIViewController, completion: (() -> Void)? = nil) -> NCMenuAction {
+        NCMenuAction(
+            title: isAnyOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: ""),
+            icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
+            action: { _ in
+                if !isAnyOffline, selectedMetadatas.count > 3 {
+                    let alert = UIAlertController(
+                        title: NSLocalizedString("_set_available_offline_", comment: ""),
+                        message: NSLocalizedString("_select_offline_warning_", comment: ""),
+                        preferredStyle: .alert)
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
+                        selectedMetadatas.forEach { NCFunctionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
+                        completion?()
+                    }))
+                    alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
+                    viewController.present(alert, animated: true)
+                } else {
+                    selectedMetadatas.forEach { NCFunctionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
+                    completion?()
+                }
+            }
+        )
+    }
+
     /// Open view that lets the user move or copy the files within Nextcloud
     static func moveOrCopyAction(selectedMetadatas: [tableMetadata], completion: (() -> Void)? = nil) -> NCMenuAction {
         NCMenuAction(

+ 2 - 21
iOSClient/Menu/NCViewer+Menu.swift

@@ -35,15 +35,8 @@ extension NCViewer {
         var titleFavorite = NSLocalizedString("_add_favorites_", comment: "")
         if metadata.favorite { titleFavorite = NSLocalizedString("_remove_favorites_", comment: "") }
         let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-
-        var titleOffline = ""
-        if localFile == nil || localFile!.offline == false {
-            titleOffline = NSLocalizedString("_set_available_offline_", comment: "")
-        } else {
-            titleOffline = NSLocalizedString("_remove_available_offline_", comment: "")
-        }
-
         let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
+        let isOffline = localFile?.offline == true
 
         //
         // FAVORITE
@@ -81,19 +74,7 @@ extension NCViewer {
         // OFFLINE
         //
         if metadata.session == "" && !webView {
-            actions.append(
-                NCMenuAction(
-                    title: titleOffline,
-                    icon: NCUtility.shared.loadImage(named: "tray.and.arrow.down"),
-                    action: { _ in
-                        if (localFile == nil || !CCUtility.fileProviderStorageExists(metadata)) && metadata.session == "" {
-                            NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _ in }
-                        } else {
-                            NCManageDatabase.shared.setLocalFile(ocId: metadata.ocId, offline: !localFile!.offline)
-                        }
-                    }
-                )
-            )
+            actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: viewController))
         }
 
         //

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

@@ -334,8 +334,7 @@
 "_user_employee_"               = "Employee";
 "_user_contractor_"             = "Contractor";
 "_user_editprofile_"            = "Edit profile";
-"_favorite_offline_"            = "Favorites available offline";
-"_favorite_offline_footer_"     = "Making all favorites available offline may take a while and use a lot of memory while doing it.";
+"_select_offline_warning_"      = "Making multiple files and folders available offline may take a while and use a lot of memory while doing so.";
 "_advanced_"                    = "Advanced";
 "_disable_files_app_"           = "Disable Files App integration";
 "_disable_files_app_footer_"    = "Do not permit the access of files via the iOS Files application";