Эх сурвалжийг харах

Refactor `NCFC.copyPasteboard` & fix memory issue when copying un-downloaded files

- don't use global `pasteboardOcIds`, but pass them as parameter for better consistency and encapsulation
- use a dispatchGroup to wait until all items are downloaded, then copy to pasteboard
- use completion to not wait synchronously

(cherry picked from commit b74f42373075c205c5d28dce6bea6b380a308bf7)
Signed-off-by: Henrik Storch <henrik.storch@nextcloud.com>
Henrik Storch 3 жил өмнө
parent
commit
5260cb876a

+ 0 - 1
iOSClient/AppDelegate.swift

@@ -59,7 +59,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var disableSharesView: Bool = false
     var disableSharesView: Bool = false
     var documentPickerViewController: NCDocumentPickerViewController?
     var documentPickerViewController: NCDocumentPickerViewController?
     var networkingProcessUpload: NCNetworkingProcessUpload?
     var networkingProcessUpload: NCNetworkingProcessUpload?
-    var pasteboardOcIds: [String] = []
     var shares: [tableShare] = []
     var shares: [tableShare] = []
     var timerErrorNetworking: Timer?
     var timerErrorNetworking: Timer?
     
     

+ 4 - 0
iOSClient/Data/NCDatabase.swift

@@ -413,6 +413,10 @@ class tableMetadata: Object, NCUserBaseUrl {
     }
     }
 }
 }
 
 
+extension tableMetadata {
+    var fileExtension: String { (fileNameView as NSString).pathExtension }
+}
+
 class tablePhotoLibrary: Object {
 class tablePhotoLibrary: Object {
 
 
     @objc dynamic var account = ""
     @objc dynamic var account = ""

+ 29 - 27
iOSClient/Main/NCFunctionCenter.swift

@@ -39,6 +39,7 @@ import JGProgressHUD
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
     var viewerQuickLook: NCViewerQuickLook?
     var viewerQuickLook: NCViewerQuickLook?
     var documentController: UIDocumentInteractionController?
     var documentController: UIDocumentInteractionController?
+    var copyDispatchGroup: DispatchGroup?
 
 
     // MARK: - Download
     // MARK: - Download
 
 
@@ -97,8 +98,7 @@ import JGProgressHUD
                         }
                         }
 
 
                     case NCGlobal.shared.selectorLoadCopy:
                     case NCGlobal.shared.selectorLoadCopy:
-
-                        copyPasteboard()
+                        copyDispatchGroup?.leave()
 
 
                     case NCGlobal.shared.selectorLoadOffline:
                     case NCGlobal.shared.selectorLoadOffline:
 
 
@@ -418,37 +418,26 @@ import JGProgressHUD
 
 
     // MARK: - Copy & Paste
     // MARK: - Copy & Paste
 
 
-    func copyPasteboard() {
-
-        var metadatas: [tableMetadata] = []
+    func copyPasteboard(pasteboardOcIds: [String], completion: @escaping () -> Void) {
+        var metadatas: [tableMetadata] = pasteboardOcIds.compactMap(NCManageDatabase.shared.getMetadataFromOcId)
         var items = [[String: Any]]()
         var items = [[String: Any]]()
-
-        for ocId in appDelegate.pasteboardOcIds {
-            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                metadatas.append(metadata)
-            }
-        }
+        copyDispatchGroup = DispatchGroup()
 
 
         for metadata in metadatas {
         for metadata in metadatas {
-
-            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                do {
-                    // Get Data
-                    let data = try Data(contentsOf: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)))
-                    // Pasteboard item
-                    if let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (metadata.fileNameView as NSString).pathExtension as CFString, nil) {
-                        let fileUTI = unmanagedFileUTI.takeRetainedValue() as String
-                        items.append([fileUTI: data])
-                    }
-                } catch {
-                    print("error")
-                }
+            if let pasteboardItem = metadata.toPasteBoardItem() {
+                metadatas.removeAll(where: { $0 == metadata })
+                items.append(pasteboardItem)
             } else {
             } else {
+                copyDispatchGroup?.enter()
                 NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadCopy) { _ in }
                 NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadCopy) { _ in }
             }
             }
         }
         }
 
 
-        UIPasteboard.general.setItems(items, options: [:])
+        copyDispatchGroup?.notify(queue: .main, execute: {
+            items.append(contentsOf: metadatas.compactMap({ $0.toPasteBoardItem() }))
+            UIPasteboard.general.setItems(items, options: [:])
+            completion()
+        })
     }
     }
 
 
     func pastePasteboard(serverUrl: String) {
     func pastePasteboard(serverUrl: String) {
@@ -658,8 +647,7 @@ import JGProgressHUD
         let titleOffline = isOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: "")
         let titleOffline = isOffline ? NSLocalizedString("_remove_available_offline_", comment: "") :  NSLocalizedString("_set_available_offline_", comment: "")
 
 
         let copy = UIAction(title: NSLocalizedString("_copy_file_", comment: ""), image: UIImage(systemName: "doc.on.doc")) { _ in
         let copy = UIAction(title: NSLocalizedString("_copy_file_", comment: ""), image: UIImage(systemName: "doc.on.doc")) { _ in
-            self.appDelegate.pasteboardOcIds = [metadata.ocId]
-            self.copyPasteboard()
+            self.copyPasteboard(pasteboardOcIds: [metadata.ocId], completion: { })
         }
         }
 
 
         let copyPath = UIAction(title: NSLocalizedString("_copy_path_", comment: ""), image: UIImage(systemName: "doc.on.clipboard")) { _ in
         let copyPath = UIAction(title: NSLocalizedString("_copy_path_", comment: ""), image: UIImage(systemName: "doc.on.clipboard")) { _ in
@@ -833,3 +821,17 @@ import JGProgressHUD
         return UIMenu(title: "", children: [detail, submenu])
         return UIMenu(title: "", children: [detail, submenu])
     }
     }
 }
 }
+
+fileprivate extension tableMetadata {
+    func toPasteBoardItem() -> [String: Any]? {
+        // Get Data
+        let fileUrl = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView))
+        guard CCUtility.fileProviderStorageExists(ocId, fileNameView: fileNameView),
+              let data = try? Data(contentsOf: fileUrl),
+              let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil)
+        else { return nil }
+        // Pasteboard item
+        let fileUTI = unmanagedFileUTI.takeRetainedValue() as String
+        return [fileUTI: data]
+    }
+}

+ 129 - 2
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -320,8 +320,7 @@ extension NCCollectionViewCommon {
                     title: NSLocalizedString("_copy_file_", comment: ""),
                     title: NSLocalizedString("_copy_file_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
                     icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
                     action: { _ in
                     action: { _ in
-                        self.appDelegate.pasteboardOcIds = [metadata.ocId]
-                        NCFunctionCenter.shared.copyPasteboard()
+                        NCFunctionCenter.shared.copyPasteboard(pasteboardOcIds: [metadata.ocId], completion: { })
                     }
                     }
                 )
                 )
             )
             )
@@ -446,4 +445,132 @@ extension NCCollectionViewCommon {
 
 
         presentMenu(with: actions)
         presentMenu(with: actions)
     }
     }
+
+    func toggleMenuSelect() {
+
+        var actions = [NCMenuAction]()
+
+        //
+        // SELECT ALL
+        //
+        actions.append(
+            NCMenuAction(
+                title: NSLocalizedString("_select_all_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "checkmark.circle.fill"),
+                action: { _ in
+                    self.collectionViewSelectAll()
+                }
+            )
+        )
+
+        //
+        // OPEN IN
+        //
+        actions.append(
+            NCMenuAction(
+                title: NSLocalizedString("_open_in_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
+                action: { _ in
+                    NCFunctionCenter.shared.openActivityViewController(selectOcId: self.selectOcId)
+                    self.tapSelect(sender: self)
+                }
+            )
+        )
+
+        //
+        // SAVE TO PHOTO GALLERY
+        //
+        actions.append(
+            NCMenuAction(
+                title: NSLocalizedString("_save_selected_files_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "square.and.arrow.down"),
+                action: { _ in
+                    for ocId in self.selectOcId {
+                        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                            if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
+                                if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
+                                    NCFunctionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
+                                } else {
+                                    if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+                                        NCFunctionCenter.shared.saveAlbum(metadata: metadata)
+                                    } else {
+                                        NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    self.tapSelect(sender: self)
+                }
+            )
+        )
+
+        //
+        // COPY - MOVE
+        //
+        actions.append(
+            NCMenuAction(
+                title: NSLocalizedString("_move_or_copy_selected_files_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
+                action: { _ in
+                    var meradatasSelect = [tableMetadata]()
+                    for ocId in self.selectOcId {
+                        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                            meradatasSelect.append(metadata)
+                        }
+                    }
+                    if meradatasSelect.count > 0 {
+                        NCFunctionCenter.shared.openSelectView(items: meradatasSelect, viewController: self)
+                    }
+                    self.tapSelect(sender: self)
+                }
+            )
+        )
+
+        //
+        // COPY
+        //
+        actions.append(
+            NCMenuAction(
+                title: NSLocalizedString("_copy_file_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
+                action: { _ in
+                    NCFunctionCenter.shared.copyPasteboard(pasteboardOcIds: self.selectOcId, completion: { self.tapSelect(sender: self) })
+                }
+            )
+        )
+
+        //
+        // DELETE
+        //
+        actions.append(
+            NCMenuAction(
+                title: NSLocalizedString("_delete_selected_files_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "trash"),
+                action: { _ in
+                    let alertController = UIAlertController(title: "", message: NSLocalizedString("_want_delete_", comment: ""), preferredStyle: .alert)
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
+                        for ocId in self.selectOcId {
+                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                                NCOperationQueue.shared.delete(metadata: metadata, onlyLocalCache: false)
+                            }
+                        }
+                        self.tapSelect(sender: self)
+                    })
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
+                        for ocId in self.selectOcId {
+                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                                NCOperationQueue.shared.delete(metadata: metadata, onlyLocalCache: true)
+                            }
+                        }
+                        self.tapSelect(sender: self)
+                    })
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
+                    self.present(alertController, animated: true, completion: nil)
+                }
+            )
+        )
+
+        presentMenu(with: actions)
+    }
 }
 }

+ 3 - 6
iOSClient/Menu/NCMedia+Menu.swift

@@ -224,13 +224,10 @@ extension NCMedia {
                     icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
                     icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
                     action: { _ in
                     action: { _ in
                         self.isEditMode = false
                         self.isEditMode = false
-                        self.appDelegate.pasteboardOcIds.removeAll()
-                        for ocId in self.selectOcId {
-                            self.appDelegate.pasteboardOcIds.append(ocId)
+                        NCFunctionCenter.shared.copyPasteboard(pasteboardOcIds: self.selectOcId) {
+                            self.selectOcId.removeAll()
+                            self.reloadDataThenPerform { }
                         }
                         }
-                        NCFunctionCenter.shared.copyPasteboard()
-                        self.selectOcId.removeAll()
-                        self.reloadDataThenPerform { }
                     }
                     }
                 )
                 )
             )
             )

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

@@ -257,8 +257,7 @@ extension NCViewer {
                 title: NSLocalizedString("_copy_file_", comment: ""),
                 title: NSLocalizedString("_copy_file_", comment: ""),
                 icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
                 icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
                 action: { _ in
                 action: { _ in
-                    self.appDelegate.pasteboardOcIds = [metadata.ocId]
-                    NCFunctionCenter.shared.copyPasteboard()
+                    NCFunctionCenter.shared.copyPasteboard(pasteboardOcIds: [metadata.ocId], completion: { })
                 }
                 }
             )
             )
         )
         )

+ 1 - 1
iOSClient/Networking/NCNetworking.swift

@@ -327,7 +327,7 @@ import Queuer
         }
         }
     }
     }
     
     
-    @objc func download(metadata: tableMetadata, selector: String, notificationCenterProgressTask: Bool = true, progressHandler: @escaping (_ progress: Progress) -> () = { _ in }, completion: @escaping (_ errorCode: Int)->()) {
+    @objc func download(metadata: tableMetadata, selector: String, notificationCenterProgressTask: Bool = true, progressHandler: @escaping (_ progress: Progress) -> Void = { _ in }, completion: @escaping (_ errorCode: Int) -> Void) {
         
         
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
         let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
         let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!

+ 3 - 3
iOSClient/Rename file/NCRenameFile.swift

@@ -70,7 +70,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
             fileNameWithoutExt.delegate = self
             fileNameWithoutExt.delegate = self
             fileNameWithoutExt.becomeFirstResponder()
             fileNameWithoutExt.becomeFirstResponder()
 
 
-            ext.text = (metadata.fileNameView as NSString).pathExtension
+            ext.text = metadata.fileExtension
             ext.delegate = self
             ext.delegate = self
             if disableChangeExt {
             if disableChangeExt {
                 ext.isEnabled = false
                 ext.isEnabled = false
@@ -176,7 +176,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
             } else {
             } else {
 
 
                 if ext.text == nil || ext.text?.count == 0 {
                 if ext.text == nil || ext.text?.count == 0 {
-                    self.ext.text = (metadata.fileNameView as NSString).pathExtension
+                    self.ext.text = metadata.fileExtension
                     return
                     return
                 } else {
                 } else {
                     extNew = ext.text!
                     extNew = ext.text!
@@ -196,7 +196,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
 
 
                     title = NSLocalizedString("_keep_", comment: "") + " ." + metadata.ext
                     title = NSLocalizedString("_keep_", comment: "") + " ." + metadata.ext
                     alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in
                     alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in
-                        self.ext.text = (metadata.fileNameView as NSString).pathExtension
+                        self.ext.text = metadata.fileExtension
                     }))
                     }))
 
 
                     self.present(alertController, animated: true)
                     self.present(alertController, animated: true)