Browse Source

Merge pull request #2317 from nextcloud/improvedGUI

Add Crop
Marino Faggiana 2 years ago
parent
commit
ebcf5e1d2a

+ 2 - 0
.swiftlint.yml

@@ -44,6 +44,7 @@ excluded:
   - Pods
 
   # iOS Files Quarantine
+
   - File Provider Extension/FileProviderData.swift
   - File Provider Extension/FileProviderDomain.swift
   - File Provider Extension/FileProviderEnumerator.swift
@@ -60,6 +61,7 @@ excluded:
   - Widget/Lockscreen/LockscreenData.swift
   - Widget/Lockscreen/LockscreenWidgetView.swift
   - Widget/Lockscreen/LockscreenWidgetProvider.swift
+  - iOSClient/GUI
   - iOSClient/ExternalResources
   - iOSClient/Activity/NCActivity.swift
   - iOSClient/Activity/NCActivityTableViewCell.swift

BIN
Nextcloud Hub.png


+ 25 - 4
Nextcloud.xcodeproj/project.pbxproj

@@ -96,6 +96,7 @@
 		F70460522499061800BB98A7 /* NotificationCenter+MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70460512499061800BB98A7 /* NotificationCenter+MainThread.swift */; };
 		F70460532499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70460512499061800BB98A7 /* NotificationCenter+MainThread.swift */; };
 		F70460542499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70460512499061800BB98A7 /* NotificationCenter+MainThread.swift */; };
+		F704A950296EE1C200D0737A /* Mantis in Frameworks */ = {isa = PBXBuildFile; productRef = F704A94F296EE1C200D0737A /* Mantis */; };
 		F704B5E32430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F704B5E22430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard */; };
 		F704B5E52430AA8000632F5F /* NCCreateFormUploadConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */; };
 		F704B5E72430C06700632F5F /* NCCreateFormUploadConflictCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F704B5E62430C06700632F5F /* NCCreateFormUploadConflictCell.xib */; };
@@ -412,6 +413,8 @@
 		F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
 		F7A0D1372591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
 		F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */; };
+		F7A48413297022E000BD1B49 /* ViewerQuickLook.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A48412297022E000BD1B49 /* ViewerQuickLook.swift */; };
+		F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */ = {isa = PBXBuildFile; fileRef = F7A48414297028FC00BD1B49 /* Nextcloud Hub.png */; };
 		F7A60F86292D215000FCE1F2 /* NCTalkAccounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A60F84292D215000FCE1F2 /* NCTalkAccounts.swift */; };
 		F7A60F87292D215000FCE1F2 /* NCTalkAccounts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7A60F85292D215000FCE1F2 /* NCTalkAccounts.storyboard */; };
 		F7A76DC8256A71CD00119AB3 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extension.swift */; };
@@ -498,7 +501,6 @@
 		F7D68FCF28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D68FCB28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift */; };
 		F7D68FD028CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D68FCB28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift */; };
 		F7D96FCC246ED7E200536D73 /* NCNetworkingCheckRemoteUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D96FCB246ED7E100536D73 /* NCNetworkingCheckRemoteUser.swift */; };
-		F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DFB7EF219C5B8000680748 /* NCCreateFormUploadAssets.swift */; };
 		F7E0710128B13BB00001B882 /* DashboardData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E0710028B13BB00001B882 /* DashboardData.swift */; };
 		F7E0CDCF265CE8610044854E /* NCUserStatus.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7E0CDCE265CE8610044854E /* NCUserStatus.storyboard */; };
 		F7E41316294A19B300839300 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
@@ -986,6 +988,8 @@
 		F7A0D1342591FBC5008F8A13 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
 		F7A321AB1E9E6AD50069AD1B /* CCAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAdvanced.h; sourceTree = "<group>"; };
 		F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAdvanced.m; sourceTree = "<group>"; };
+		F7A48412297022E000BD1B49 /* ViewerQuickLook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewerQuickLook.swift; sourceTree = "<group>"; };
+		F7A48414297028FC00BD1B49 /* Nextcloud Hub.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Nextcloud Hub.png"; sourceTree = SOURCE_ROOT; };
 		F7A60F84292D215000FCE1F2 /* NCTalkAccounts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCTalkAccounts.swift; sourceTree = "<group>"; };
 		F7A60F85292D215000FCE1F2 /* NCTalkAccounts.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCTalkAccounts.storyboard; sourceTree = "<group>"; };
 		F7A7FA6229265CF4000603EF /* NCManageE2EE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCManageE2EE.swift; sourceTree = "<group>"; };
@@ -1149,7 +1153,6 @@
 		F7D96FCB246ED7E100536D73 /* NCNetworkingCheckRemoteUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCNetworkingCheckRemoteUser.swift; sourceTree = "<group>"; };
 		F7DBD82B23E46A4700ECB7C6 /* MarkdownKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MarkdownKit.framework; path = Carthage/Build/iOS/MarkdownKit.framework; sourceTree = "<group>"; };
 		F7DE9AB01F482FA5008DFE10 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
-		F7DFB7EF219C5B8000680748 /* NCCreateFormUploadAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadAssets.swift; sourceTree = "<group>"; };
 		F7E0710028B13BB00001B882 /* DashboardData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardData.swift; sourceTree = "<group>"; };
 		F7E0CDCE265CE8610044854E /* NCUserStatus.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCUserStatus.storyboard; sourceTree = "<group>"; };
 		F7E41315294A19B300839300 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = "<group>"; };
@@ -1256,6 +1259,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F704A950296EE1C200D0737A /* Mantis in Frameworks */,
 				F76DA941277B75870082465B /* KTVHTTPCache.xcframework in Frameworks */,
 				F7ED547C25EEA65400956C55 /* QRCodeReader in Frameworks */,
 				F788ECC7263AAAFA00ADC67F /* MarkdownKit in Frameworks */,
@@ -1471,6 +1475,7 @@
 			isa = PBXGroup;
 			children = (
 				F7C7B488245EBA4100D93E60 /* NCViewerQuickLook.swift */,
+				F7A48412297022E000BD1B49 /* ViewerQuickLook.swift */,
 			);
 			path = NCViewerQuickLook;
 			sourceTree = "<group>";
@@ -2043,7 +2048,6 @@
 			isa = PBXGroup;
 			children = (
 				F769CA162965AB7C00039397 /* NCUploadAssets.swift */,
-				F7DFB7EF219C5B8000680748 /* NCCreateFormUploadAssets.swift */,
 				F704B5E22430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard */,
 				F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */,
 				F704B5E82430C0B800632F5F /* NCCreateFormUploadConflictCell.swift */,
@@ -2190,6 +2194,7 @@
 			isa = PBXGroup;
 			children = (
 				F7AC9349296193050002BC0F /* Reasons to use Nextcloud.pdf */,
+				F7A48414297028FC00BD1B49 /* Nextcloud Hub.png */,
 				F7F4F0FB27ECDBDA008676F9 /* Font */,
 				F72B60941A24F04E004EF66F /* Localizations */,
 			);
@@ -2493,6 +2498,7 @@
 				F734B06528E75C0100E180D5 /* TLPhotoPicker */,
 				F77333872927A72100466E35 /* OpenSSL */,
 				F77BC3EA293E5268005F2B08 /* Swifter */,
+				F704A94F296EE1C200D0737A /* Mantis */,
 			);
 			productName = "Crypto Cloud";
 			productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */;
@@ -2651,6 +2657,7 @@
 				F734B06428E75C0100E180D5 /* XCRemoteSwiftPackageReference "TLPhotoPicker" */,
 				F77333862927A72100466E35 /* XCRemoteSwiftPackageReference "OpenSSL" */,
 				F77BC3E9293E5268005F2B08 /* XCRemoteSwiftPackageReference "swifter" */,
+				F704A94E296EE1C200D0737A /* XCRemoteSwiftPackageReference "Mantis" */,
 			);
 			productRefGroup = F7F67B9F1A24D27800EE80DA;
 			projectDirPath = "";
@@ -2757,6 +2764,7 @@
 				F710E8111EF95C9C00DC2427 /* ImagesIntro.xcassets in Resources */,
 				F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */,
 				F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */,
+				F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */,
 				F74C0437253F1CDC009762AB /* NCShares.storyboard in Resources */,
 				F7F4F10C27ECDBDB008676F9 /* Inconsolata-Regular.ttf in Resources */,
 				F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */,
@@ -3067,7 +3075,6 @@
 				F702F2CF25EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
 				F72CD63A25C19EBF00F46F9A /* NCAutoUpload.swift in Sources */,
 				AF93471D27E2361E002537EE /* NCShareAdvancePermissionFooter.swift in Sources */,
-				F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */,
 				AF1A9B6427D0CA1E00F17A9E /* UIAlertController+Extension.swift in Sources */,
 				F73B422C2476764F00A30FD3 /* NCNotification.swift in Sources */,
 				371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */,
@@ -3206,6 +3213,7 @@
 				F7D68FCC28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				F70CEF5623E9C7E50007035B /* UIColor+Extension.swift in Sources */,
 				F75CA1472962F13700B01130 /* HUDView.swift in Sources */,
+				F7A48413297022E000BD1B49 /* ViewerQuickLook.swift in Sources */,
 				F77BB748289985270090FC19 /* UITabBarController+Extension.swift in Sources */,
 				F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */,
 				F7A7FA6329265CF4000603EF /* NCManageE2EE.swift in Sources */,
@@ -3930,6 +3938,14 @@
 /* End XCConfigurationList section */
 
 /* Begin XCRemoteSwiftPackageReference section */
+		F704A94E296EE1C200D0737A /* XCRemoteSwiftPackageReference "Mantis" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/guoyingtao/Mantis.git";
+			requirement = {
+				branch = master;
+				kind = branch;
+			};
+		};
 		F70B86732642CE3B00ED5349 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
@@ -4093,6 +4109,11 @@
 /* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
+		F704A94F296EE1C200D0737A /* Mantis */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F704A94E296EE1C200D0737A /* XCRemoteSwiftPackageReference "Mantis" */;
+			productName = Mantis;
+		};
 		F70B86742642CE3B00ED5349 /* FirebaseCrashlytics */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = F70B86732642CE3B00ED5349 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;

+ 0 - 457
iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift

@@ -1,457 +0,0 @@
-//
-//  NCCreateFormUploadAssets.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 14/11/2018.
-//  Copyright © 2018 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import UIKit
-import Queuer
-import NextcloudKit
-import XLForm
-import Photos
-
-class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
-
-    var serverUrl: String = ""
-    var titleServerUrl: String?
-    var assets: [PHAsset] = []
-    var cryptated: Bool = false
-    var session: String = ""
-    let requestOptions = PHImageRequestOptions()
-    var imagePreview: UIImage?
-    let targetSizeImagePreview = CGSize(width: 100, height: 100)
-    let appDelegate = UIApplication.shared.delegate as! AppDelegate
-
-    var cellBackgoundColor = UIColor.secondarySystemGroupedBackground
-
-    // MARK: - View Life Cycle
-
-    convenience init(serverUrl: String, assets: [PHAsset], cryptated: Bool, session: String) {
-
-        self.init()
-
-        if serverUrl == NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
-            titleServerUrl = "/"
-        } else {
-            if let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
-                if let metadata = NCManageDatabase.shared.getMetadataFromOcId(tableDirectory.ocId) {
-                    titleServerUrl = metadata.fileNameView
-                } else { titleServerUrl = (serverUrl as NSString).lastPathComponent }
-            } else { titleServerUrl = (serverUrl as NSString).lastPathComponent }
-        }
-
-        self.serverUrl = serverUrl
-        self.assets = assets
-        self.cryptated = cryptated
-        self.session = session
-
-        requestOptions.resizeMode = PHImageRequestOptionsResizeMode.exact
-        requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
-        requestOptions.isSynchronous = true
-    }
-
-    override func viewDidLoad() {
-
-        super.viewDidLoad()
-
-        self.title = NSLocalizedString("_upload_photos_videos_", comment: "")
-
-        view.backgroundColor = .systemGroupedBackground
-        tableView.backgroundColor = .systemGroupedBackground
-        cellBackgoundColor = .secondarySystemGroupedBackground
-
-        self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
-        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
-
-        self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
-
-        if assets.count == 1 && assets[0].mediaType == PHAssetMediaType.image {
-            PHImageManager.default().requestImage(for: assets[0], targetSize: targetSizeImagePreview, contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { image, _ in
-                self.imagePreview = image
-            })
-        }
-
-        initializeForm()
-        reloadForm()
-    }
-
-    // MARK: XLForm
-
-    func initializeForm() {
-
-        let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
-        form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
-
-        var section: XLFormSectionDescriptor
-        var row: XLFormRowDescriptor
-
-        // Section: Destination Folder
-
-        section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: ""))
-        form.addFormSection(section)
-
-        row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: XLFormRowDescriptorTypeButton, title: self.titleServerUrl)
-        row.action.formSelector = #selector(changeDestinationFolder(_:))
-        row.cellConfig["backgroundColor"] = cellBackgoundColor
-
-        row.cellConfig["imageView.image"] = UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 25)
-        row.cellConfig["textLabel.textAlignment"] = NSTextAlignment.right.rawValue
-        row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textLabel.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        // User folder Autoupload
-        row = XLFormRowDescriptor(tag: "useFolderAutoUpload", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_use_folder_auto_upload_", comment: ""))
-        row.value = 0
-        row.cellConfig["backgroundColor"] = cellBackgoundColor
-
-        row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textLabel.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        // Use Sub folder
-        row = XLFormRowDescriptor(tag: "useSubFolder", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_autoupload_create_subfolder_", comment: ""))
-        let activeAccount = NCManageDatabase.shared.getActiveAccount()
-        if activeAccount?.autoUploadCreateSubfolder == true {
-            row.value = 1
-        } else {
-            row.value = 0
-        }
-        row.hidden = "$\("useFolderAutoUpload") == 0"
-
-        row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textLabel.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        // Section Mode filename
-
-        section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_mode_filename_", comment: ""))
-        form.addFormSection(section)
-
-        // Maintain the original fileName
-
-        row = XLFormRowDescriptor(tag: "maintainOriginalFileName", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_maintain_original_filename_", comment: ""))
-        row.value = CCUtility.getOriginalFileName(NCGlobal.shared.keyFileNameOriginal)
-        row.cellConfig["backgroundColor"] = cellBackgoundColor
-
-        row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textLabel.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        // Add File Name Type
-
-        row = XLFormRowDescriptor(tag: "addFileNameType", rowType: XLFormRowDescriptorTypeBooleanSwitch, title: NSLocalizedString("_add_filenametype_", comment: ""))
-        row.value = CCUtility.getFileNameType(NCGlobal.shared.keyFileNameType)
-        row.hidden = "$\("maintainOriginalFileName") == 1"
-        row.cellConfig["backgroundColor"] = cellBackgoundColor
-
-        row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textLabel.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        // Section: Rename File Name
-
-        section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: ""))
-        form.addFormSection(section)
-
-        row = XLFormRowDescriptor(tag: "maskFileName", rowType: XLFormRowDescriptorTypeText, title: (NSLocalizedString("_filename_", comment: "")))
-        let fileNameMask: String = CCUtility.getFileNameMask(NCGlobal.shared.keyFileNameMask)
-        if fileNameMask.count > 0 {
-            row.value = fileNameMask
-        }
-        row.hidden = "$\("maintainOriginalFileName") == 1"
-        row.cellConfig["backgroundColor"] = cellBackgoundColor
-
-        row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textLabel.textColor"] = UIColor.label
-
-        row.cellConfig["textField.textAlignment"] = NSTextAlignment.right.rawValue
-        row.cellConfig["textField.font"] = UIFont.systemFont(ofSize: 15.0)
-        row.cellConfig["textField.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        // Section: Preview File Name
-
-        row = XLFormRowDescriptor(tag: "previewFileName", rowType: XLFormRowDescriptorTypeTextView, title: "")
-        row.height = 180
-        row.disabled = true
-        row.cellConfig["backgroundColor"] = cellBackgoundColor
-
-        row.cellConfig["textView.backgroundColor"] = cellBackgoundColor
-        row.cellConfig["textView.font"] = UIFont.systemFont(ofSize: 14.0)
-        row.cellConfig["textView.textColor"] = UIColor.label
-
-        section.addFormRow(row)
-
-        self.form = form
-    }
-
-    override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
-
-        super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
-
-        if formRow.tag == "useFolderAutoUpload" {
-
-            if (formRow.value! as AnyObject).boolValue  == true {
-
-                let buttonDestinationFolder: XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
-                buttonDestinationFolder.hidden = true
-
-            } else {
-
-                let buttonDestinationFolder: XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
-                buttonDestinationFolder.hidden = false
-            }
-        } else if formRow.tag == "useSubFolder" {
-
-            if (formRow.value! as AnyObject).boolValue  == true {
-
-            } else {
-
-            }
-        } else if formRow.tag == "maintainOriginalFileName" {
-            CCUtility.setOriginalFileName((formRow.value! as AnyObject).boolValue, key: NCGlobal.shared.keyFileNameOriginal)
-            self.reloadForm()
-        } else if formRow.tag == "addFileNameType" {
-            CCUtility.setFileNameType((formRow.value! as AnyObject).boolValue, key: NCGlobal.shared.keyFileNameType)
-            self.reloadForm()
-        } else if formRow.tag == "maskFileName" {
-
-            let fileName = formRow.value as? String
-
-            self.form.delegate = nil
-
-            if let fileName = fileName {
-                formRow.value = CCUtility.removeForbiddenCharactersServer(fileName)
-            }
-
-            self.form.delegate = self
-
-            let previewFileName: XLFormRowDescriptor  = self.form.formRow(withTag: "previewFileName")!
-            previewFileName.value = self.previewFileName(valueRename: formRow.value as? String)
-
-            // reload cell
-            if fileName != nil {
-
-                if newValue as! String != formRow.value as! String {
-
-                    self.reloadFormRow(formRow)
-
-                    let error = NKError(errorCode: NCGlobal.shared.errorCharactersForbidden, errorDescription: "_forbidden_characters_")
-                    NCContentPresenter.shared.showInfo(error: error)
-                }
-            }
-
-            self.reloadFormRow(previewFileName)
-        }
-    }
-
-    func reloadForm() {
-
-        self.form.delegate = nil
-
-        let buttonDestinationFolder: XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
-        buttonDestinationFolder.title = self.titleServerUrl
-
-        let maskFileName: XLFormRowDescriptor = self.form.formRow(withTag: "maskFileName")!
-        let previewFileName: XLFormRowDescriptor  = self.form.formRow(withTag: "previewFileName")!
-        previewFileName.value = self.previewFileName(valueRename: maskFileName.value as? String)
-
-        self.tableView.reloadData()
-        self.form.delegate = self
-    }
-
-    // MARK: - Action
-
-    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
-
-        if serverUrl != nil {
-
-            self.serverUrl = serverUrl!
-
-            if serverUrl == NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
-                self.titleServerUrl = "/"
-            } else {
-                if let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, self.serverUrl)) {
-                    if let metadata = NCManageDatabase.shared.getMetadataFromOcId(tableDirectory.ocId) {
-                        titleServerUrl = metadata.fileNameView
-                    } else { titleServerUrl = (self.serverUrl as NSString).lastPathComponent }
-                } else { titleServerUrl = (self.serverUrl as NSString).lastPathComponent }
-            }
-
-            // Update
-            let row: XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
-            row.title = self.titleServerUrl
-            self.updateFormRow(row)
-        }
-    }
-
-    @objc func save() {
-
-        DispatchQueue.global().async {
-
-            let useFolderPhotoRow: XLFormRowDescriptor  = self.form.formRow(withTag: "useFolderAutoUpload")!
-            let useSubFolderRow: XLFormRowDescriptor  = self.form.formRow(withTag: "useSubFolder")!
-            var useSubFolder: Bool = false
-            var metadatasNOConflict: [tableMetadata] = []
-            var metadatasUploadInConflict: [tableMetadata] = []
-            let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId, account: self.appDelegate.account)
-
-            if (useFolderPhotoRow.value! as AnyObject).boolValue == true {
-                self.serverUrl = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId, account: self.appDelegate.account)
-                useSubFolder = (useSubFolderRow.value! as AnyObject).boolValue
-            }
-
-            if autoUploadPath == self.serverUrl {
-                if !NCNetworking.shared.createFolder(assets: self.assets, selector: NCGlobal.shared.selectorUploadFile, useSubFolder: useSubFolder, account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId) {
-                    
-                    let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_error_createsubfolders_upload_")
-                    NCContentPresenter.shared.showError(error: error)
-                    return
-                }
-            }
-
-            for asset in self.assets {
-
-                var serverUrl = self.serverUrl
-                var livePhoto: Bool = false
-                let creationDate = asset.creationDate ?? Date()
-                let fileName = CCUtility.createFileName(asset.value(forKey: "filename") as? String, fileDate: creationDate, fileType: asset.mediaType, keyFileName: NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)!
-
-                if asset.mediaSubtypes.contains(.photoLive) && CCUtility.getLivePhoto() {
-                    livePhoto = true
-                }
-
-                if useSubFolder {
-                    let dateFormatter = DateFormatter()
-                    dateFormatter.dateFormat = "yyyy"
-                    let yearString = dateFormatter.string(from: creationDate)
-                    dateFormatter.dateFormat = "MM"
-                    let monthString = dateFormatter.string(from: creationDate)
-                    serverUrl = autoUploadPath + "/" + yearString + "/" + monthString
-                }
-
-                // Check if is in upload
-                let isRecordInSessions = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@ AND session != ''", self.appDelegate.account, serverUrl, fileName), sorted: "fileName", ascending: false)
-                if isRecordInSessions.count > 0 { continue }
-
-                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", isLivePhoto: livePhoto)
-
-                metadataForUpload.assetLocalIdentifier = asset.localIdentifier
-                metadataForUpload.session = self.session
-                metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
-                metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
-
-                if let result = NCManageDatabase.shared.getMetadataConflict(account: self.appDelegate.account, serverUrl: serverUrl, fileNameView: fileName) {
-                    metadataForUpload.fileName = result.fileName
-                    metadatasUploadInConflict.append(metadataForUpload)
-                } else {
-                    metadatasNOConflict.append(metadataForUpload)
-                }
-            }
-
-            // Verify if file(s) exists
-            if metadatasUploadInConflict.count > 0 {
-
-                DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
-                    if let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict {
-
-                        conflict.serverUrl = self.serverUrl
-                        conflict.metadatasNOConflict = metadatasNOConflict
-                        conflict.metadatasUploadInConflict = metadatasUploadInConflict
-                        conflict.delegate = self.appDelegate
-
-                        self.appDelegate.window?.rootViewController?.present(conflict, animated: true, completion: nil)
-                    }
-                }
-
-            } else {
-                NCNetworkingProcessUpload.shared.createProcessUploads(metadatas: metadatasNOConflict, completion: { _ in })
-            }
-
-            DispatchQueue.main.async {self.dismiss(animated: true, completion: nil)  }
-        }
-    }
-
-    @objc func cancel() {
-
-        self.dismiss(animated: true, completion: nil)
-    }
-
-    // MARK: - Utility
-
-    func previewFileName(valueRename: String?) -> String {
-
-        var returnString: String = ""
-        let asset = assets[0]
-        let creationDate = asset.creationDate ?? Date()
-
-        if CCUtility.getOriginalFileName(NCGlobal.shared.keyFileNameOriginal) {
-
-            return (NSLocalizedString("_filename_", comment: "") + ": " + (asset.value(forKey: "filename") as! String))
-
-        } else if let valueRename = valueRename {
-
-            let valueRenameTrimming = valueRename.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
-
-            if valueRenameTrimming.count > 0 {
-
-                self.form.delegate = nil
-                CCUtility.setFileNameMask(valueRename, key: NCGlobal.shared.keyFileNameMask)
-                self.form.delegate = self
-
-                returnString = CCUtility.createFileName(asset.value(forKey: "filename") as! String?, fileDate: creationDate, fileType: asset.mediaType, keyFileName: NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)
-
-            } else {
-
-                CCUtility.setFileNameMask("", key: NCGlobal.shared.keyFileNameMask)
-                returnString = CCUtility.createFileName(asset.value(forKey: "filename") as! String?, fileDate: creationDate, fileType: asset.mediaType, keyFileName: nil, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)
-            }
-
-        } else {
-
-            CCUtility.setFileNameMask("", key: NCGlobal.shared.keyFileNameMask)
-            returnString = CCUtility.createFileName(asset.value(forKey: "filename") as! String?, fileDate: creationDate, fileType: asset.mediaType, keyFileName: nil, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)
-        }
-
-        return String(format: NSLocalizedString("_preview_filename_", comment: ""), "MM, MMM, DD, YY, YYYY, HH, hh, mm, ss, ampm") + ":" + "\n\n" + returnString
-    }
-
-    @objc func changeDestinationFolder(_ sender: XLFormRowDescriptor) {
-
-        self.deselectFormRow(sender)
-
-        let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
-        let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
-        let viewController = navigationController.topViewController as! NCSelect
-
-        viewController.delegate = self
-        viewController.typeOfCommandView = .selectCreateFolder
-        viewController.includeDirectoryE2EEncryption = true
-
-        self.present(navigationController, animated: true, completion: nil)
-    }
-}

+ 1 - 1
iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift

@@ -254,7 +254,7 @@ class NCCreateFormUploadConflict: UIViewController {
                 metadata.fileNameView = newFileName
 
                 // This is not an asset - [file]
-                if metadata.assetLocalIdentifier == "" {
+                if metadata.assetLocalIdentifier == "" || metadata.isExtractFile {
                     let newPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: newFileName)
                     CCUtility.moveFile(atPath: oldPath, toPath: newPath)
                 }

+ 197 - 73
iOSClient/Main/Create cloud/NCUploadAssets.swift

@@ -24,6 +24,8 @@
 import SwiftUI
 import NextcloudKit
 import TLPhotoPicker
+import Mantis
+import Photos
 
 class NCHostingUploadAssetsView: NSObject {
 
@@ -37,15 +39,31 @@ class NCHostingUploadAssetsView: NSObject {
 
 // MARK: - Class
 
-class NCUploadAssets: ObservableObject, NCCreateFormUploadConflictDelegate {
+struct PreviewStore {
+    var id: String
+    var image: UIImage
+    var asset: TLPHAsset
+    var hasChanges: Bool
+}
+
+class NCUploadAssets: NSObject, ObservableObject, NCCreateFormUploadConflictDelegate {
 
     @Published var serverUrl: String
     @Published var assets: [TLPHAsset]
     @Published var userBaseUrl: NCUserBaseUrl
     @Published var dismiss = false
+    @Published var previewStore: [PreviewStore] = []
 
     var metadatasNOConflict: [tableMetadata] = []
     var metadatasUploadInConflict: [tableMetadata] = []
+    var timer: Timer?
+
+    /*
+
+     */
+    let resizeImagePreview: Double = 200
+    let sizeImagePreview: Double = 100
+    let compressionQuality: CGFloat = 0.5
 
     init(assets: [TLPHAsset], serverUrl: String, userBaseUrl: NCUserBaseUrl) {
 
@@ -54,6 +72,39 @@ class NCUploadAssets: ObservableObject, NCCreateFormUploadConflictDelegate {
         self.userBaseUrl = userBaseUrl
     }
 
+    func loadImages() {
+        DispatchQueue.global().async {
+            for asset in self.assets {
+                guard asset.type == .photo, let image = asset.fullResolutionImage?.resizeImage(size: CGSize(width: self.resizeImagePreview, height: self.resizeImagePreview), isAspectRation: true), let localIdentifier = asset.phAsset?.localIdentifier else { continue }
+                self.previewStore.append(PreviewStore(id: localIdentifier, image: image, asset: asset, hasChanges: false))
+            }
+        }
+    }
+
+    func startTimer(navigationItem: UINavigationItem) {
+        self.timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true, block: { _ in
+            let numItemsRight = navigationItem.rightBarButtonItems?.count ?? 0
+            if let buttonCrop = navigationItem.leftBarButtonItems?.first {
+                if numItemsRight > 1 && buttonCrop.isEnabled {
+                    buttonCrop.isEnabled = false
+                    if let buttonDone = navigationItem.rightBarButtonItems?.last {
+                        buttonDone.isEnabled = false
+                    }
+                }
+                if numItemsRight == 1 && !buttonCrop.isEnabled {
+                    buttonCrop.isEnabled = true
+                    if let buttonDone = navigationItem.rightBarButtonItems?.first {
+                        buttonDone.isEnabled = true
+                    }
+                }
+            }
+        })
+    }
+
+    func stopTimer() {
+        self.timer?.invalidate()
+    }
+
     func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
 
         if let metadatas = metadatas {
@@ -75,6 +126,12 @@ struct UploadAssetsView: View {
     @State private var isAddFilenametype: Bool = CCUtility.getFileNameType(NCGlobal.shared.keyFileNameType)
     @State private var isPresentedSelect = false
     @State private var isPresentedUploadConflict = false
+    @State private var isPresentedQuickLook = false
+    @State private var fileNamePath = NSTemporaryDirectory() + "Photo.jpg"
+    @State private var metadata: tableMetadata?
+    @State private var index: Int = 0
+
+    var gridItems: [GridItem] = [GridItem()]
 
     @ObservedObject var uploadAssets: NCUploadAssets
 
@@ -82,6 +139,7 @@ struct UploadAssetsView: View {
 
     init(uploadAssets: NCUploadAssets) {
         self.uploadAssets = uploadAssets
+        uploadAssets.loadImages()
     }
 
     func getOriginalFilename() -> String {
@@ -179,6 +237,24 @@ struct UploadAssetsView: View {
             metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
             metadata.status = NCGlobal.shared.metadataStatusWaitUpload
 
+            // Modified
+            if let previewStore = uploadAssets.previewStore.first(where: { $0.id == asset.localIdentifier }), previewStore.hasChanges, let data = previewStore.image.jpegData(compressionQuality: 1) {
+                if metadata.contentType == "image/heic" {
+                    let fileNameNoExtension = (fileName as NSString).deletingPathExtension
+                    metadata.contentType = "image/jpeg"
+                    metadata.fileName = fileNameNoExtension + ".jpg"
+                    metadata.fileNameView = fileNameNoExtension + ".jpg"
+                }
+                let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+                do {
+                    try data.write(to: URL(fileURLWithPath: fileNamePath))
+                    metadata.isExtractFile = true
+                    metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
+                    metadata.creationDate = asset.creationDate as? NSDate ?? (Date() as NSDate)
+                    metadata.date = asset.modificationDate as? NSDate ?? (Date() as NSDate)
+                } catch {  }
+            }
+
             if let result = NCManageDatabase.shared.getMetadataConflict(account: uploadAssets.userBaseUrl.account, serverUrl: serverUrl, fileNameView: fileName) {
                 metadata.fileName = result.fileName
                 metadatasUploadInConflict.append(metadata)
@@ -196,95 +272,143 @@ struct UploadAssetsView: View {
         }
     }
 
+    func presentedQuickLook(size: CGFloat, index: Int) {
+
+        let previewStore = uploadAssets.previewStore[index]
+        var image = previewStore.image
+
+        if !previewStore.hasChanges {
+            if let fullResolutionImage = previewStore.asset.fullResolutionImage?.resizeImage(size: CGSize(width: size, height: size)) {
+                image = fullResolutionImage
+            }
+        }
+
+        if let data = image.jpegData(compressionQuality: uploadAssets.compressionQuality) {
+            do {
+                try data.write(to: URL(fileURLWithPath: fileNamePath))
+                self.index = index
+                isPresentedQuickLook = true
+            } catch {
+            }
+        }
+    }
+
     var body: some View {
-        NavigationView {
-            List {
-                Section(header: Text(NSLocalizedString("_save_path_", comment: ""))) {
-
-                    HStack {
-                        Label {
-                            if NCUtilityFileSystem.shared.getHomeServer(urlBase: uploadAssets.userBaseUrl.urlBase, userId: uploadAssets.userBaseUrl.userId) == uploadAssets.serverUrl {
-                                Text("/")
-                                    .frame(maxWidth: .infinity, alignment: .trailing)
-                            } else {
-                                Text((uploadAssets.serverUrl as NSString).lastPathComponent)
-                                    .frame(maxWidth: .infinity, alignment: .trailing)
+
+        GeometryReader { geo in
+            NavigationView {
+                List {
+
+                    if !uploadAssets.previewStore.isEmpty {
+                        Section(header: Text(NSLocalizedString("_modify_photo_", comment: "")), footer: Text(NSLocalizedString("_modify_photo_desc_", comment: ""))) {
+                            ScrollView(.horizontal) {
+                                LazyHGrid(rows: gridItems, alignment: .center, spacing: 10) {
+                                    ForEach(0..<uploadAssets.previewStore.count, id: \.self) { index in
+                                        VStack {
+                                            Image(uiImage: uploadAssets.previewStore[index].image)
+                                                .resizable()
+                                                .frame(width: uploadAssets.sizeImagePreview, height: uploadAssets.sizeImagePreview, alignment: .center)
+                                                .cornerRadius(10)
+                                                .scaledToFit()
+                                                .onTapGesture {
+                                                    presentedQuickLook(size: max(geo.size.height, geo.size.height), index: index)
+                                                }.fullScreenCover(isPresented: $isPresentedQuickLook) {
+                                                    ViewerQuickLook(url: URL(fileURLWithPath: fileNamePath), index: $index, isPresentedQuickLook: $isPresentedQuickLook, uploadAssets: uploadAssets)
+                                                        .ignoresSafeArea()
+                                                }
+                                        }
+                                    }
+                                }
                             }
-                        } icon: {
-                            Image("folder")
-                                .renderingMode(.template)
-                                .resizable()
-                                .scaledToFit()
-                                .foregroundColor(Color(NCBrandColor.shared.brand))
                         }
                     }
-                    .contentShape(Rectangle())
-                    .onTapGesture {
-                        isPresentedSelect = true
-                    }
-                }
 
-                Section(header: Text(NSLocalizedString("_mode_filename_", comment: ""))) {
+                    Section(header: Text(NSLocalizedString("_mode_filename_", comment: ""))) {
 
-                    Toggle(NSLocalizedString("_maintain_original_filename_", comment: ""), isOn: $isMaintainOriginalFilename)
-                        .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brand)))
-
-                    if !isMaintainOriginalFilename {
-                        Toggle(NSLocalizedString("_add_filenametype_", comment: ""), isOn: $isAddFilenametype)
+                        Toggle(NSLocalizedString("_maintain_original_filename_", comment: ""), isOn: $isMaintainOriginalFilename)
                             .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brand)))
-                    }
-                }
 
-                Section(header: Text(NSLocalizedString("_filename_", comment: ""))) {
-
-                    HStack {
-                        Text(NSLocalizedString("_filename_", comment: ""))
-                        if isMaintainOriginalFilename {
-                            Text(getOriginalFilename())
-                                .frame(maxWidth: .infinity, alignment: .trailing)
-                        } else {
-                            TextField(NSLocalizedString("_enter_filename_", comment: ""), text: $fileName)
-                                .modifier(TextFieldClearButton(text: $fileName))
-                                .multilineTextAlignment(.trailing)
+                        if !isMaintainOriginalFilename {
+                            Toggle(NSLocalizedString("_add_filenametype_", comment: ""), isOn: $isAddFilenametype)
+                                .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brand)))
                         }
                     }
-                    if !isMaintainOriginalFilename {
-                        Text(setFileNameMask(fileName: fileName))
+
+                    Section {
+
+                        HStack {
+                            Label {
+                                if NCUtilityFileSystem.shared.getHomeServer(urlBase: uploadAssets.userBaseUrl.urlBase, userId: uploadAssets.userBaseUrl.userId) == uploadAssets.serverUrl {
+                                    Text("/")
+                                        .frame(maxWidth: .infinity, alignment: .trailing)
+                                } else {
+                                    Text((uploadAssets.serverUrl as NSString).lastPathComponent)
+                                        .frame(maxWidth: .infinity, alignment: .trailing)
+                                }
+                            } icon: {
+                                Image("folder")
+                                    .renderingMode(.template)
+                                    .resizable()
+                                    .scaledToFit()
+                                    .foregroundColor(Color(NCBrandColor.shared.brand))
+                            }
+                        }
+                        .contentShape(Rectangle())
+                        .onTapGesture {
+                            isPresentedSelect = true
+                        }
+
+                        HStack {
+                            Text(NSLocalizedString("_filename_", comment: ""))
+                            if isMaintainOriginalFilename {
+                                Text(getOriginalFilename())
+                                    .frame(maxWidth: .infinity, alignment: .trailing)
+                            } else {
+                                TextField(NSLocalizedString("_enter_filename_", comment: ""), text: $fileName)
+                                    .modifier(TextFieldClearButton(text: $fileName))
+                                    .multilineTextAlignment(.trailing)
+                            }
+                        }
+                        if !isMaintainOriginalFilename {
+                            Text(setFileNameMask(fileName: fileName))
+                                .font(.system(size: 12))
+                                .foregroundColor(Color.gray)
+                        }
                     }
-                }
-                .complexModifier { view in
-                    if #available(iOS 15, *) {
-                        view.listRowSeparator(.hidden)
+                    .complexModifier { view in
+                        if #available(iOS 15, *) {
+                            view.listRowSeparator(.hidden)
+                        }
                     }
-                }
 
-                Button(NSLocalizedString("_save_", comment: "")) {
-                    save { metadatasNOConflict, metadatasUploadInConflict in
-                        if metadatasUploadInConflict.isEmpty {
-                            uploadAssets.dismiss = true
-                        } else {
-                            uploadAssets.metadatasNOConflict = metadatasNOConflict
-                            uploadAssets.metadatasUploadInConflict = metadatasUploadInConflict
-                            isPresentedUploadConflict = true
+                    Button(NSLocalizedString("_save_", comment: "")) {
+                        save { metadatasNOConflict, metadatasUploadInConflict in
+                            if metadatasUploadInConflict.isEmpty {
+                                uploadAssets.dismiss = true
+                            } else {
+                                uploadAssets.metadatasNOConflict = metadatasNOConflict
+                                uploadAssets.metadatasUploadInConflict = metadatasUploadInConflict
+                                isPresentedUploadConflict = true
+                            }
                         }
                     }
+                    .frame(maxWidth: .infinity)
+                    .buttonStyle(ButtonRounded(disabled: false))
+                    .listRowBackground(Color(UIColor.systemGroupedBackground))
                 }
-                .frame(maxWidth: .infinity)
-                .buttonStyle(ButtonRounded(disabled: false))
-                .listRowBackground(Color(UIColor.systemGroupedBackground))
+                .navigationTitle(NSLocalizedString("_upload_photos_videos_", comment: ""))
+                .navigationBarTitleDisplayMode(.inline)
             }
-            .navigationTitle(NSLocalizedString("_upload_photos_videos_", comment: ""))
-            .navigationBarTitleDisplayMode(.inline)
-        }
-        .sheet(isPresented: $isPresentedSelect) {
-            SelectView(serverUrl: $uploadAssets.serverUrl)
-        }
-        .sheet(isPresented: $isPresentedUploadConflict) {
-            UploadConflictView(delegate: uploadAssets, serverUrl: uploadAssets.serverUrl, metadatasUploadInConflict: uploadAssets.metadatasUploadInConflict, metadatasNOConflict: uploadAssets.metadatasNOConflict)
-        }
-        .onReceive(uploadAssets.$dismiss) { newValue in
-            if newValue {
-                presentationMode.wrappedValue.dismiss()
+            .sheet(isPresented: $isPresentedSelect) {
+                SelectView(serverUrl: $uploadAssets.serverUrl)
+            }
+            .sheet(isPresented: $isPresentedUploadConflict) {
+                UploadConflictView(delegate: uploadAssets, serverUrl: uploadAssets.serverUrl, metadatasUploadInConflict: uploadAssets.metadatasUploadInConflict, metadatasNOConflict: uploadAssets.metadatasNOConflict)
+            }
+            .onReceive(uploadAssets.$dismiss) { newValue in
+                if newValue {
+                    presentationMode.wrappedValue.dismiss()
+                }
             }
         }
     }

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

@@ -917,6 +917,8 @@
 "_mobile_config_"           = "Download the configuration profile";
 "_calendar_contacts_footer_"    = "After the downloading you need to go to the Settings app to install it. If a profile is not installed within 8 minutes of downloading it, it is automatically deleted";
 "_preview_"                 = "Preview";
+"_crop_"                    = "Crop";
+"_modify_photo_desc_"       = "Tap the image for modify photo";
 
 // Video
 "_select_trace_"            = "Select the trace";
@@ -974,3 +976,10 @@
 "_already_plan_"                = "The selected plan has already been bought.";
 "_change_billing_"              = "Change Billing";
 "_payment_method_"              = "Payment Method";
+
+// MARK: Mantis library
+"Mantis.Done"                   = "Done";
+"Mantis.Cancel"                 = "Cancel";
+"Mantis.Reset"                  = "Reset";
+"Mantis.Original"               = "Original";
+"Mantis.Square"                 = "Square";

+ 56 - 0
iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift

@@ -5,6 +5,7 @@
 //  Created by Marino Faggiana on 03/05/2020.
 //  Copyright © 2020 Marino Faggiana. All rights reserved.
 //  Copyright © 2022 Henrik Storch. All rights reserved.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
 //
 //  Author Marino Faggiana <marino.faggiana@nextcloud.com>
 //  Author Henrik Storch <henrik.storch@nextcloud.com>
@@ -26,6 +27,12 @@
 import UIKit
 import QuickLook
 import NextcloudKit
+import Mantis
+import SwiftUI
+
+protocol NCViewerQuickLookDelegate: AnyObject {
+    func dismiss(url: URL, hasChanges: Bool)
+}
 
 @objc class NCViewerQuickLook: QLPreviewController {
 
@@ -33,6 +40,7 @@ import NextcloudKit
     var previewItems: [PreviewItem] = []
     var isEditingEnabled: Bool
     var metadata: tableMetadata?
+    var delegateViewer: NCViewerQuickLookDelegate?
 
     // if the document has any changes (annotations)
     var hasChanges = false
@@ -40,6 +48,9 @@ import NextcloudKit
     // used to display the save alert
     var parentVC: UIViewController?
 
+    // if the Crop is presented
+    var isPresentCrop = false
+
     required init?(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
@@ -70,6 +81,8 @@ import NextcloudKit
             let error = NKError(errorCode: NCGlobal.shared.errorCharactersForbidden, errorDescription: "_message_disable_overwrite_livephoto_")
             NCContentPresenter.shared.showInfo(error: error)
         }
+
+        navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_crop_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(crop))
     }
 
     override func viewDidAppear(_ animated: Bool) {
@@ -81,6 +94,8 @@ import NextcloudKit
     override func viewDidDisappear(_ animated: Bool) {
         super.viewDidDisappear(animated)
 
+        guard !isPresentCrop else { return }
+        delegateViewer?.dismiss(url: url, hasChanges: hasChanges)
         guard isEditingEnabled, hasChanges, let metadata = metadata else { return }
 
         let alertController = UIAlertController(title: NSLocalizedString("_save_", comment: ""), message: nil, preferredStyle: .alert)
@@ -102,6 +117,24 @@ import NextcloudKit
         alertController.addAction(UIAlertAction(title: NSLocalizedString("_discard_changes_", comment: ""), style: .destructive) { _ in })
         parentVC?.present(alertController, animated: true)
     }
+
+    @objc func crop() {
+
+        guard let image = UIImage(contentsOfFile: url.path) else { return }
+        let config = Mantis.Config()
+
+        if let bundleIdentifier = Bundle.main.bundleIdentifier {
+            config.localizationConfig.bundle = Bundle(identifier: bundleIdentifier)
+            config.localizationConfig.tableName = "Localizable"
+        }
+        let cropViewController = Mantis.cropViewController(image: image, config: config)
+
+        cropViewController.delegate = self
+        cropViewController.modalPresentationStyle = .fullScreen
+
+        self.isPresentCrop = true
+        self.present(cropViewController, animated: true)
+    }
 }
 
 extension NCViewerQuickLook: QLPreviewControllerDataSource, QLPreviewControllerDelegate {
@@ -165,6 +198,29 @@ extension NCViewerQuickLook: QLPreviewControllerDataSource, QLPreviewControllerD
     }
 }
 
+extension NCViewerQuickLook: CropViewControllerDelegate {
+
+    func cropViewControllerDidCrop(_ cropViewController: Mantis.CropViewController, cropped: UIImage, transformation: Mantis.Transformation, cropInfo: Mantis.CropInfo) {
+        cropViewController.dismiss(animated: true) {
+            self.isPresentCrop = false
+        }
+
+        guard let data = cropped.jpegData(compressionQuality: 1) else { return }
+
+        do {
+            try data.write(to: self.url)
+            reloadData()
+        } catch {  }
+    }
+
+    func cropViewControllerDidCancel(_ cropViewController: Mantis.CropViewController, original: UIImage) {
+
+        cropViewController.dismiss(animated: true) {
+            self.isPresentCrop = false
+        }
+    }
+}
+
 class PreviewItem: NSObject, QLPreviewItem {
     var previewItemURL: URL?
 }

+ 131 - 0
iOSClient/Viewer/NCViewerQuickLook/ViewerQuickLook.swift

@@ -0,0 +1,131 @@
+//
+//  ViewerQuickLook.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 12/01/23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import SwiftUI
+import QuickLook
+import Mantis
+
+struct ViewerQuickLook: UIViewControllerRepresentable {
+
+    let url: URL
+
+    @Binding var index: Int
+    @Binding var isPresentedQuickLook: Bool
+    @ObservedObject var uploadAssets: NCUploadAssets
+
+    func makeUIViewController(context: Context) -> UINavigationController {
+        let controller = QLPreviewController()
+
+        controller.dataSource = context.coordinator
+        controller.delegate = context.coordinator
+        context.coordinator.viewController = controller
+
+        controller.navigationItem.rightBarButtonItem = UIBarButtonItem(
+            barButtonSystemItem: .done, target: context.coordinator,
+            action: #selector(context.coordinator.dismiss)
+        )
+
+        controller.navigationItem.leftBarButtonItem = UIBarButtonItem(
+            title: NSLocalizedString("_crop_", comment: ""), style: UIBarButtonItem.Style.plain, target: context.coordinator,
+            action: #selector(context.coordinator.crop)
+        )
+
+        uploadAssets.startTimer(navigationItem: controller.navigationItem)
+
+        let navigationController = UINavigationController(rootViewController: controller)
+        return navigationController
+    }
+
+    func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { }
+
+    func makeCoordinator() -> Coordinator {
+        return Coordinator(parent: self)
+    }
+
+    class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate, CropViewControllerDelegate {
+
+        weak var viewController: QLPreviewController?
+        let parent: ViewerQuickLook
+
+        var image: UIImage?
+        var hasChange = false
+
+        init(parent: ViewerQuickLook) {
+            self.parent = parent
+        }
+
+        @objc func dismiss() {
+            parent.uploadAssets.stopTimer()
+            parent.isPresentedQuickLook = false
+            if let image = image {
+                parent.uploadAssets.previewStore[parent.index].image = image
+                parent.uploadAssets.previewStore[parent.index].hasChanges = hasChange
+            }
+        }
+
+        // MARK: -
+
+        func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
+            return 1
+        }
+
+        func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
+            return .createCopy
+        }
+
+        func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) {
+            guard NCUtilityFileSystem.shared.moveFile(atPath: modifiedContentsURL.path, toPath: parent.url.path) else { return }
+            if let image = UIImage(contentsOfFile: parent.url.path) {
+                self.image = image
+                self.hasChange = true
+            }
+        }
+
+        func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
+            return parent.url as NSURL
+        }
+
+        // MARK: -
+
+        func cropViewControllerDidCrop(_ cropViewController: Mantis.CropViewController, cropped: UIImage, transformation: Mantis.Transformation, cropInfo: Mantis.CropInfo) {
+            cropViewController.dismiss(animated: true)
+            guard let data = cropped.jpegData(compressionQuality: 1) else { return }
+            do {
+                try data.write(to: parent.url)
+                self.image = cropped
+                self.hasChange = true
+                viewController?.reloadData()
+            } catch {  }
+        }
+        func cropViewControllerDidCancel(_ cropViewController: Mantis.CropViewController, original: UIImage) {
+            cropViewController.dismiss(animated: true)
+        }
+
+        func cropViewControllerDidFailToCrop(_ cropViewController: Mantis.CropViewController, original: UIImage) {}
+        func cropViewControllerDidBeginResize(_ cropViewController: Mantis.CropViewController) {}
+        func cropViewControllerDidEndResize(_ cropViewController: Mantis.CropViewController, original: UIImage, cropInfo: Mantis.CropInfo) {}
+        func cropViewControllerDidImageTransformed(_ cropViewController: Mantis.CropViewController) { }
+
+        @objc func crop() {
+
+            guard let image = UIImage(contentsOfFile: parent.url.path) else { return }
+            let config = Mantis.Config()
+
+            if let bundleIdentifier = Bundle.main.bundleIdentifier {
+                config.localizationConfig.bundle = Bundle(identifier: bundleIdentifier)
+                config.localizationConfig.tableName = "Localizable"
+            }
+            let cropViewController = Mantis.cropViewController(image: image, config: config)
+
+            cropViewController.delegate = self
+            cropViewController.modalPresentationStyle = .fullScreen
+
+            viewController?.present(cropViewController, animated: true)
+        }
+    }
+}