Browse Source

Merge pull request #1839 from nextcloud/fix/share-conflict

Fix Share extension duplicates
Marino Faggiana 3 years ago
parent
commit
193624204e

+ 1 - 1
.swiftlint.yml

@@ -4,6 +4,7 @@ opt_in_rules: # some rules are turned off by default, so you need to opt-in
   - empty_string
   - explicit_init
   - unneeded_parentheses_in_closure_argument
+  - operator_usage_whitespace
 
 empty_count:
   severity: warning
@@ -33,7 +34,6 @@ excluded:
   - File Provider Extension/FileProviderExtension.swift
   - File Provider Extension/FileProviderUtility.swift
   - Notification Service Extension/NotificationService.swift
-  - Share/NCShareExtension.swift
   - iOSClient/Activity/NCActivity.swift
   - iOSClient/Activity/NCActivityTableViewCell.swift
   - iOSClient/AppDelegate.swift

+ 24 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -18,6 +18,13 @@
 		371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */; };
 		3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */; };
 		8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */; };
+		AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */; };
+		AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F704B5E22430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard */; };
+		AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E82430C0B800632F5F /* NCCreateFormUploadConflictCell.swift */; };
+		AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F704B5E62430C06700632F5F /* NCCreateFormUploadConflictCell.xib */; };
+		AF22B20C277C6F4D00DAB0CC /* NCShareCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF22B20B277C6F4D00DAB0CC /* NCShareCell.swift */; };
+		AF22B217277D196700DAB0CC /* NCShareExtension+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF22B215277D196700DAB0CC /* NCShareExtension+DataSource.swift */; };
+		AF22B218277D196700DAB0CC /* NCShareExtension+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF22B216277D196700DAB0CC /* NCShareExtension+Files.swift */; };
 		AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */; };
 		AF2D7C7E2742559100ADF566 /* NCShareUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */; };
 		AF4BF614275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
@@ -32,6 +39,7 @@
 		AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; };
 		AF4BF62027562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; };
 		AF4BF62127562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; };
+		AF730AFA27843E4C00B7520E /* NCShareExtension+NCDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */; };
 		AF817EF1274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; };
 		AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; };
 		AF817EF3274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; };
@@ -448,11 +456,15 @@
 		371B5A3223D0BD5500FAFAE9 /* FloatingPanel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FloatingPanel.framework; path = Carthage/Build/iOS/FloatingPanel.framework; sourceTree = "<group>"; };
 		3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = "<group>"; };
 		8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Menu.swift"; sourceTree = "<group>"; };
+		AF22B20B277C6F4D00DAB0CC /* NCShareCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCell.swift; sourceTree = "<group>"; };
+		AF22B215277D196700DAB0CC /* NCShareExtension+DataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+DataSource.swift"; sourceTree = "<group>"; };
+		AF22B216277D196700DAB0CC /* NCShareExtension+Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+Files.swift"; sourceTree = "<group>"; };
 		AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareLinkCell.swift; sourceTree = "<group>"; };
 		AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareUserCell.swift; sourceTree = "<group>"; };
 		AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Account.swift"; sourceTree = "<group>"; };
 		AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabse+Metadata.swift"; sourceTree = "<group>"; };
 		AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Activity.swift"; sourceTree = "<group>"; };
+		AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+NCDelegate.swift"; sourceTree = "<group>"; };
 		AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUserBaseUrl.swift; sourceTree = "<group>"; };
 		AF8ED1F92757821000B8DBC4 /* NextcloudTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudTests.swift; sourceTree = "<group>"; };
@@ -1437,6 +1449,10 @@
 			children = (
 				F714803A262EBE3900693E51 /* MainInterface.storyboard */,
 				F7148040262EBE4000693E51 /* NCShareExtension.swift */,
+				AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */,
+				AF22B215277D196700DAB0CC /* NCShareExtension+DataSource.swift */,
+				AF22B216277D196700DAB0CC /* NCShareExtension+Files.swift */,
+				AF22B20B277C6F4D00DAB0CC /* NCShareCell.swift */,
 				F7148046262EBE4B00693E51 /* Share-Bridging-Header.h */,
 			);
 			path = Share;
@@ -2060,7 +2076,9 @@
 				F7148054262ED51000693E51 /* NCListCell.xib in Resources */,
 				F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */,
 				F7145A1A1D12E3B700CAFEEC /* Images.xcassets in Resources */,
+				AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */,
 				F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */,
+				AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */,
 				F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */,
 				F746EC51273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */,
 				F79EC784263161BA004E59D6 /* NCRenameFile.storyboard in Resources */,
@@ -2248,6 +2266,7 @@
 				F7707687263A853700A1BA94 /* NCContentPresenter.swift in Sources */,
 				F70460532499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */,
 				F70BFC7520E0FA7D00C67599 /* NCUtility.swift in Sources */,
+				AF22B20C277C6F4D00DAB0CC /* NCShareCell.swift in Sources */,
 				F79B646126CA661600838ACA /* UIControl+Extensions.swift in Sources */,
 				F7EDE4CC262D7B6F00414FE6 /* NCEmptyDataSet.swift in Sources */,
 				AF4BF61A27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */,
@@ -2256,20 +2275,25 @@
 				AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
 				F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */,
 				F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
+				AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */,
 				F7BD71E62636EAFC00643C34 /* NCNetworkingE2EE.swift in Sources */,
 				F7F878AF1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */,
+				AF22B218277D196700DAB0CC /* NCShareExtension+Files.swift in Sources */,
 				F702F2D025EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
 				F7EDE4DB262D7BA200414FE6 /* NCCellProtocol.swift in Sources */,
 				F7EDE4D1262D7B8400414FE6 /* NCDataSource.swift in Sources */,
 				F71459D21D12E3B700CAFEEC /* CCUtility.m in Sources */,
 				F75A9EE723796C6F0044CFCE /* NCNetworking.swift in Sources */,
+				AF730AFA27843E4C00B7520E /* NCShareExtension+NCDelegate.swift in Sources */,
 				F7EDE4E0262D7BAF00414FE6 /* NCGridCell.swift in Sources */,
 				F7A76DC8256A71CD00119AB3 /* UIImage+Extensions.swift in Sources */,
 				F7B8CD96261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
 				F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */,
+				AF22B217277D196700DAB0CC /* NCShareExtension+DataSource.swift in Sources */,
 				F780710A1EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */,
 				F79EC77F26316193004E59D6 /* NCRenameFile.swift in Sources */,
+				AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */,
 				F7148041262EBE4000693E51 /* NCShareExtension.swift in Sources */,
 				F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */,
 				F7BAADCC1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */,

+ 17 - 8
Share/MainInterface.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EAU-PF-EEd">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EAU-PF-EEd">
     <device id="retina6_5" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -46,39 +46,42 @@
                                         <rect key="frame" x="10" y="70" width="394" height="220"/>
                                         <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                                         <prototypes>
-                                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" rowHeight="50" id="6Kt-n0-i8J">
-                                                <rect key="frame" x="0.0" y="24.333333969116211" width="394" height="50"/>
+                                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" rowHeight="50" id="6Kt-n0-i8J" customClass="NCShareCell" customModule="Share" customModuleProvider="target">
+                                                <rect key="frame" x="0.0" y="44.666666030883789" width="394" height="50"/>
                                                 <autoresizingMask key="autoresizingMask"/>
                                                 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="6Kt-n0-i8J" id="WGN-Zn-lR8">
                                                     <rect key="frame" x="0.0" y="0.0" width="394" height="50"/>
                                                     <autoresizingMask key="autoresizingMask"/>
                                                     <subviews>
-                                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="10" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="uvl-De-S9p">
+                                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="uvl-De-S9p">
                                                             <rect key="frame" x="10" y="5" width="40" height="40"/>
                                                             <constraints>
                                                                 <constraint firstAttribute="width" constant="40" id="Vpb-6e-ta9"/>
                                                                 <constraint firstAttribute="height" constant="40" id="wvf-Ey-woY"/>
                                                             </constraints>
                                                         </imageView>
-                                                        <label opaque="NO" userInteractionEnabled="NO" tag="20" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="filename" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rz6-pe-DB5">
+                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="filename" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rz6-pe-DB5">
                                                             <rect key="frame" x="60" y="10" width="284" height="17"/>
                                                             <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                             <nil key="textColor"/>
                                                             <nil key="highlightedColor"/>
                                                         </label>
-                                                        <label opaque="NO" userInteractionEnabled="NO" tag="40" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="size" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QFh-Vy-b4Z">
+                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="size" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QFh-Vy-b4Z">
                                                             <rect key="frame" x="60" y="29.999999999999996" width="284" height="14.333333333333332"/>
                                                             <fontDescription key="fontDescription" type="system" pointSize="12"/>
                                                             <color key="textColor" systemColor="systemGray2Color"/>
                                                             <nil key="highlightedColor"/>
                                                         </label>
-                                                        <button opaque="NO" tag="30" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OGY-95-ZYi" customClass="NCShareExtensionButtonWithIndexPath" customModule="Share" customModuleProvider="target">
+                                                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OGY-95-ZYi">
                                                             <rect key="frame" x="354" y="10" width="30" height="30"/>
                                                             <constraints>
                                                                 <constraint firstAttribute="height" constant="30" id="QqR-Uw-RnV"/>
                                                                 <constraint firstAttribute="width" constant="30" id="bpm-t5-3HA"/>
                                                             </constraints>
                                                             <state key="normal" image="more"/>
+                                                            <connections>
+                                                                <action selector="buttonTapped:" destination="6Kt-n0-i8J" eventType="touchUpInside" id="Xx7-1X-AhZ"/>
+                                                            </connections>
                                                         </button>
                                                     </subviews>
                                                     <constraints>
@@ -95,6 +98,12 @@
                                                     </constraints>
                                                 </tableViewCellContentView>
                                                 <inset key="separatorInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                                <connections>
+                                                    <outlet property="fileNameCell" destination="rz6-pe-DB5" id="e8w-G7-bBe"/>
+                                                    <outlet property="imageCell" destination="uvl-De-S9p" id="S4F-TD-YGI"/>
+                                                    <outlet property="moreButton" destination="OGY-95-ZYi" id="vA3-mU-S1e"/>
+                                                    <outlet property="sizeCell" destination="QFh-Vy-b4Z" id="rmM-qr-QXU"/>
+                                                </connections>
                                             </tableViewCell>
                                         </prototypes>
                                         <connections>

+ 82 - 0
Share/NCShareCell.swift

@@ -0,0 +1,82 @@
+//
+//  NCShareCell.swift
+//  Share
+//
+//  Created by Henrik Storch on 29.12.21.
+//  Copyright © 2021 Henrik Storch. All rights reserved.
+//
+//  Author Henrik Storch <henrik.storch@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 NCCommunication
+
+protocol NCShareCellDelegate: AnyObject {
+    var uploadStarted: Bool { get }
+    func removeFile(named fileName: String)
+    func renameFile(named fileName: String)
+}
+
+class NCShareCell: UITableViewCell {
+    @IBOutlet weak var imageCell: UIImageView!
+    @IBOutlet weak var fileNameCell: UILabel!
+    @IBOutlet weak var moreButton: UIButton!
+    @IBOutlet weak var sizeCell: UILabel!
+    weak var delegate: (NCShareCellDelegate & UIViewController)?
+    var fileName = ""
+
+    func setup(fileName: String) {
+        self.fileName = fileName
+        let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
+
+        backgroundColor = NCBrandColor.shared.systemBackground
+        imageCell?.layer.cornerRadius = 6
+        imageCell?.layer.masksToBounds = true
+
+        if let image = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 80, height: 80)) {
+            imageCell.image = image
+        } else {
+            if !resultInternalType.iconName.isEmpty {
+                imageCell?.image = UIImage(named: resultInternalType.iconName)
+            } else {
+                imageCell?.image = NCBrandColor.cacheImages.file
+            }
+        }
+
+        fileNameCell?.text = fileName
+
+        let fileSize = NCUtilityFileSystem.shared.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
+        sizeCell?.text = CCUtility.transformedSize(fileSize)
+
+        moreButton?.setImage(NCUtility.shared.loadImage(named: "more").image(color: NCBrandColor.shared.label, size: 15), for: .normal)
+    }
+
+    @IBAction func buttonTapped(_ sender: Any) {
+        guard !fileName.isEmpty, delegate?.uploadStarted != true else { return }
+        let alertController = UIAlertController(title: "", message: fileName, preferredStyle: .alert)
+
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_rename_file_", comment: ""), style: .default) { _ in
+            self.delegate?.renameFile(named: self.fileName)
+        })
+
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_file_", comment: ""), style: .default) { _ in
+            self.delegate?.removeFile(named: self.fileName)
+        })
+
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { _ in })
+        delegate?.present(alertController, animated: true, completion: nil)
+    }
+}

+ 180 - 0
Share/NCShareExtension+DataSource.swift

@@ -0,0 +1,180 @@
+//
+//  NCShareExtension+DataSource.swift
+//  Share
+//
+//  Created by Henrik Storch on 29.12.21.
+//  Copyright © 2021 Henrik Storch. All rights reserved.
+//
+//  Author Henrik Storch <henrik.storch@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 NCCommunication
+
+// MARK: - Collection View (target folder)
+
+extension NCShareExtension: UICollectionViewDelegate {
+
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath),
+              let serverUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName) else {
+                  return showAlert(description: "_invalid_url_")
+              }
+
+        if metadata.e2eEncrypted && !CCUtility.isEnd(toEndEnabled: activeAccount.account) {
+            showAlert(title: "_info_", description: "_e2e_goto_settings_for_enable_")
+        }
+
+        self.serverUrl = serverUrl
+        reloadDatasource(withLoadFolder: true)
+        setNavigationBar(navigationTitle: metadata.fileNameView)
+    }
+}
+
+extension NCShareExtension: UICollectionViewDataSource {
+
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        let numberOfItems = dataSource.numberOfItems()
+        emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section)
+        return numberOfItems
+    }
+
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath), let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else {
+            return UICollectionViewCell()
+        }
+
+        cell.delegate = self
+
+        cell.fileObjectId = metadata.ocId
+        cell.fileUser = metadata.ownerId
+        cell.labelTitle.text = metadata.fileNameView
+        cell.labelTitle.textColor = NCBrandColor.shared.label
+
+        cell.imageSelect.image = nil
+        cell.imageStatus.image = nil
+        cell.imageLocal.image = nil
+        cell.imageFavorite.image = nil
+        cell.imageShared.image = nil
+        cell.imageMore.image = nil
+        cell.imageItem.image = nil
+        cell.imageItem.backgroundColor = nil
+
+        cell.progressView.progress = 0.0
+
+        if metadata.directory {
+            setupDirectoryCell(cell, with: metadata)
+        }
+
+        // image Favorite
+        if metadata.favorite {
+            cell.imageFavorite.image = NCBrandColor.cacheImages.favorite
+        }
+
+        cell.imageSelect.isHidden = true
+        cell.backgroundView = nil
+        cell.hideButtonMore(true)
+        cell.hideButtonShare(true)
+        cell.selectMode(false)
+
+        // Live Photo
+        if metadata.livePhoto {
+            cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto
+        }
+
+        // Remove last separator
+        cell.separator.isHidden = collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1
+
+        return cell
+    }
+
+    func setupDirectoryCell(_ cell: NCListCell, with metadata: tableMetadata) {
+        var isShare = false
+        var isMounted = false
+        if let metadataFolder = metadataFolder {
+            isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
+            isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
+        }
+
+        var tableShare: tableShare?
+        if dataSource.metadataShare[metadata.ocId] != nil {
+            tableShare = dataSource.metadataShare[metadata.ocId]
+        }
+
+        if metadata.e2eEncrypted {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted
+        } else if isShare {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
+        } else if tableShare != nil && tableShare?.shareType != 3 {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
+        } else if tableShare != nil && tableShare?.shareType == 3 {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderPublic
+        } else if metadata.mountType == "group" {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderGroup
+        } else if isMounted {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderExternal
+        } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
+            cell.imageItem.image = NCBrandColor.cacheImages.folderAutomaticUpload
+        } else {
+            cell.imageItem.image = NCBrandColor.cacheImages.folder
+        }
+
+        cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date)
+
+        let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)!
+        let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", activeAccount.account, lockServerUrl))
+
+        // Local image: offline
+        if tableDirectory != nil && tableDirectory!.offline {
+            cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
+        }
+    }
+}
+
+// MARK: - Table View (uploading files)
+
+extension NCShareExtension: UITableViewDelegate {
+
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return heightRowTableView
+    }
+
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        guard !uploadStarted else { return }
+        let fileName = filesName[indexPath.row]
+        renameFile(named: fileName)
+    }
+}
+
+extension NCShareExtension: UITableViewDataSource {
+
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        filesName.count
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NCShareCell else { return UITableViewCell() }
+        let fileName = filesName[indexPath.row]
+        cell.setup(fileName: fileName)
+        cell.delegate = self
+        return cell
+    }
+}

+ 215 - 0
Share/NCShareExtension+Files.swift

@@ -0,0 +1,215 @@
+//
+//  NCShareExtension+Files.swift
+//  Share
+//
+//  Created by Henrik Storch on 29.12.21.
+//  Copyright © 2021 Henrik Storch. All rights reserved.
+//
+//  Author Henrik Storch <henrik.storch@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 Foundation
+
+extension NCShareExtension {
+
+    @objc func reloadDatasource(withLoadFolder: Bool) {
+
+        layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
+
+        let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
+        self.dataSource = NCDataSource(
+            metadatasSource: metadatasSource,
+            sort: layoutForView?.sort,
+            ascending: layoutForView?.ascending,
+            directoryOnTop: layoutForView?.directoryOnTop,
+            favoriteOnTop: true,
+            filterLivePhoto: true)
+
+        if withLoadFolder {
+            loadFolder()
+        } else {
+            self.refreshControl.endRefreshing()
+        }
+
+        collectionView.reloadData()
+    }
+
+    func createFolder(with fileName: String) {
+
+        NCNetworking.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: activeAccount.account, urlBase: activeAccount.urlBase) { errorCode, errorDescription in
+
+            DispatchQueue.main.async {
+                if errorCode == 0 {
+
+                    self.serverUrl += "/" + fileName
+                    self.reloadDatasource(withLoadFolder: true)
+                    self.setNavigationBar(navigationTitle: fileName)
+
+                } else {
+                    self.showAlert(title: "_error_createsubfolders_upload_", description: errorDescription)
+                }
+            }
+        }
+    }
+
+    func loadFolder() {
+
+        networkInProgress = true
+        collectionView.reloadData()
+
+        NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { _, metadataFolder, _, _, _, _, errorCode, errorDescription in
+
+            DispatchQueue.main.async {
+                if errorCode != 0 {
+                    self.showAlert(description: errorDescription)
+                }
+                self.networkInProgress = false
+                self.metadataFolder = metadataFolder
+                self.reloadDatasource(withLoadFolder: false)
+            }
+        }
+    }
+}
+
+class NCFilesExtensionHandler {
+    var itemsProvider: [NSItemProvider] = []
+    lazy var filesName: [String] = []
+    let dateFormatter: DateFormatter = {
+        let formatter = DateFormatter()
+        formatter.dateFormat = "yyyy-MM-dd HH-mm-ss-"
+        return formatter
+    }()
+
+    @discardableResult
+    init(items: [NSExtensionItem], completion: @escaping ([String]) -> Void) {
+        CCUtility.emptyTemporaryDirectory()
+        var counter = 0
+
+        self.itemsProvider = items.compactMap({ $0.attachments }).flatMap { $0.filter({
+            $0.hasItemConformingToTypeIdentifier(kUTTypeItem as String) || $0.hasItemConformingToTypeIdentifier("public.url")
+        }) }
+
+        for (ix, provider) in itemsProvider.enumerated() {
+            provider.loadItem(forTypeIdentifier: provider.typeIdentifier) { [self] item, error in
+                defer {
+                    counter += 1
+                    if counter == itemsProvider.count { completion(self.filesName) }
+                }
+                guard error == nil else { return }
+                var originalName = (dateFormatter.string(from: Date())) + String(ix)
+
+                if let url = item as? URL, url.isFileURL, !url.lastPathComponent.isEmpty {
+                    originalName = url.lastPathComponent
+                }
+
+                var fileName: String?
+                switch item {
+                case let image as UIImage:
+                    fileName = getItem(image: image, fileName: originalName)
+                case let url as URL:
+                    fileName = getItem(url: url, fileName: originalName)
+                case let data as Data:
+                    fileName = getItem(data: data, fileName: originalName, provider: provider)
+                case let text as String:
+                    fileName = getItem(string: text, fileName: originalName)
+                default: return
+                }
+
+                if let fileName = fileName, !filesName.contains(fileName) { filesName.append(fileName) }
+            }
+        }
+    }
+
+    // Image
+    func getItem(image: UIImage, fileName: String) -> String? {
+        var fileUrl = URL(fileURLWithPath: NSTemporaryDirectory() + fileName)
+        if fileUrl.pathExtension.isEmpty { fileUrl.appendPathExtension("png") }
+        guard let pngImageData = image.pngData(),
+              (try? pngImageData.write(to: fileUrl, options: [.atomic])) != nil
+        else { return nil }
+        return fileUrl.lastPathComponent
+    }
+
+    // URL
+    // Does not work for directories
+    func getItem(url: URL, fileName: String) -> String? {
+        var fileName = fileName
+        guard url.isFileURL else {
+            guard !filesName.contains(url.lastPathComponent) else { return nil }
+            if !url.deletingPathExtension().lastPathComponent.isEmpty { fileName = url.deletingPathExtension().lastPathComponent }
+            fileName += "." + (url.pathExtension.isEmpty ? "html" : url.pathExtension)
+            let filenamePath = NSTemporaryDirectory() + fileName
+
+            do {
+                let downloadedContent = try Data(contentsOf: url)
+                guard !FileManager.default.fileExists(atPath: filenamePath) else { return nil }
+                try downloadedContent.write(to: URL(fileURLWithPath: filenamePath))
+            } catch { print(error); return nil }
+            return fileName
+        }
+
+        let filenamePath = NSTemporaryDirectory() + fileName
+
+        try? FileManager.default.removeItem(atPath: filenamePath)
+
+        do {
+            try FileManager.default.copyItem(atPath: url.path, toPath: filenamePath)
+
+            let attr = try FileManager.default.attributesOfItem(atPath: filenamePath)
+            guard !attr.isEmpty else { return nil }
+            return fileName
+        } catch { return nil }
+    }
+
+    // Data
+    func getItem(data: Data, fileName: String, provider: NSItemProvider) -> String? {
+        guard !data.isEmpty else { return nil }
+        var fileName = fileName
+
+        if let url = URL(string: fileName), !url.pathExtension.isEmpty {
+            fileName = url.lastPathComponent
+        } else if let name = provider.suggestedName {
+            fileName = name
+        } else if let ext = provider.registeredTypeIdentifiers.last?.split(separator: ".").last {
+            fileName += "." + ext
+        } // else: no file information, use default name without ext
+
+        // when sharing images in safari only data is retuned.
+        // also, when sharing option "Automatic" is slected extension will return both raw data and a url, which will be downloaded, causing the image to appear twice with different names
+        if let image = UIImage(data: data) {
+            return getItem(image: image, fileName: fileName)
+        }
+
+        let filenamePath = NSTemporaryDirectory() + fileName
+        FileManager.default.createFile(atPath: filenamePath, contents: data, attributes: nil)
+        return fileName
+    }
+
+    // String
+    func getItem(string: String, fileName: String) -> String? {
+        guard !string.isEmpty else { return nil }
+        let filenamePath = NSTemporaryDirectory() + fileName + ".txt"
+        FileManager.default.createFile(atPath: filenamePath, contents: string.data(using: String.Encoding.utf8), attributes: nil)
+        return fileName
+    }
+}
+
+extension NSItemProvider {
+    var typeIdentifier: String {
+        if hasItemConformingToTypeIdentifier("public.url") { return "public.url" } else
+        if hasItemConformingToTypeIdentifier(kUTTypeItem as String) { return kUTTypeItem as String } else { return "" }
+    }
+}

+ 159 - 0
Share/NCShareExtension+NCDelegate.swift

@@ -0,0 +1,159 @@
+//
+//  NCShareExtension.swift
+//  Share
+//
+//  Created by Marino Faggiana on 04.01.2022.
+//  Copyright © 2022 Henrik Storch. All rights reserved.
+//
+//  Author Henrik Storch <henrik.storch@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 NCCommunication
+import UIKit
+
+extension NCShareExtension: NCEmptyDataSetDelegate, NCAccountRequestDelegate {
+    // MARK: - Empty
+
+    func emptyDataSetView(_ view: NCEmptyView) {
+
+        if networkInProgress {
+            view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
+            view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
+            view.emptyDescription.text = ""
+        } else {
+            view.emptyImage.image = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
+            view.emptyTitle.text = NSLocalizedString("_files_no_folders_", comment: "")
+            view.emptyDescription.text = ""
+        }
+    }
+
+    // MARK: - Account
+
+    func showAccountPicker() {
+        let accounts = NCManageDatabase.shared.getAllAccountOrderAlias()
+        guard accounts.count > 1,
+              let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest else { return }
+
+        // Only here change the active account
+        for account in accounts {
+            account.active = account.account == self.activeAccount.account
+        }
+
+        vcAccountRequest.activeAccount = self.activeAccount
+        vcAccountRequest.accounts = accounts.sorted { sorg, dest -> Bool in
+            return sorg.active && !dest.active
+        }
+        vcAccountRequest.enableTimerProgress = false
+        vcAccountRequest.enableAddAccount = false
+        vcAccountRequest.delegate = self
+        vcAccountRequest.dismissDidEnterBackground = true
+
+        let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height / 5)
+        let height = min(CGFloat(accounts.count * Int(vcAccountRequest.heightCell) + 45), screenHeighMax)
+
+        let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height + 20)
+
+        self.present(popup, animated: true)
+    }
+
+    func accountRequestChangeAccount(account: String) {
+        guard let activeAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) else {
+            cancel(with: NCShareExtensionError.noAccount)
+            return
+        }
+        self.activeAccount = activeAccount
+
+        // NETWORKING
+        NCCommunicationCommon.shared.setup(
+            account: activeAccount.account,
+            user: activeAccount.user,
+            userId: activeAccount.userId,
+            password: CCUtility.getPassword(activeAccount.account),
+            urlBase: activeAccount.urlBase,
+            userAgent: CCUtility.getUserAgent(),
+            webDav: NCUtilityFileSystem.shared.getWebDAV(account: activeAccount.account),
+            nextcloudVersion: 0,
+            delegate: NCNetworking.shared)
+
+        // get auto upload folder
+        autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
+        autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: activeAccount.urlBase, account: activeAccount.account)
+
+        serverUrl = NCUtilityFileSystem.shared.getHomeServer(account: activeAccount.account)
+
+        layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
+
+        reloadDatasource(withLoadFolder: true)
+        setNavigationBar(navigationTitle: NCBrandOptions.shared.brand)
+    }
+}
+
+extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCellDelegate {
+
+    func removeFile(named fileName: String) {
+        guard let index = self.filesName.firstIndex(of: fileName) else {
+            return showAlert(title: "_file_not_found_", description: fileName)
+        }
+        self.filesName.remove(at: index)
+        if self.filesName.isEmpty {
+            cancel(with: NCShareExtensionError.noFiles)
+        } else {
+            self.setCommandView()
+        }
+    }
+
+    func renameFile(named fileName: String) {
+        guard let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile else { return }
+
+        let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
+        vcRename.delegate = self
+        vcRename.fileName = fileName
+        if let previewImage = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 140, height: 140)) {
+            vcRename.imagePreview = previewImage
+        } else {
+            vcRename.imagePreview = UIImage(named: resultInternalType.iconName) ?? NCBrandColor.cacheImages.file
+        }
+
+        let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height)
+
+        self.present(popup, animated: true)
+    }
+
+    func rename(fileName: String, fileNameNew: String) {
+        guard fileName != fileNameNew else { return }
+        guard let fileIx = self.filesName.firstIndex(of: fileName),
+              !self.filesName.contains(fileNameNew),
+              NCUtilityFileSystem.shared.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + fileNameNew)) else {
+                  return showAlert(title: "_single_file_conflict_title_", description: "'\(fileName)' -> '\(fileNameNew)'")
+              }
+
+        filesName[fileIx] = fileNameNew
+        tableView.reloadData()
+    }
+}
+
+extension NCShareExtension: NCCreateFormUploadConflictDelegate {
+    func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
+        guard let metadatas = metadatas else {
+            uploadStarted = false
+            uploadMetadata.removeAll()
+            return
+        }
+
+        self.uploadMetadata.append(contentsOf: metadatas)
+        self.upload()
+    }
+}

+ 139 - 738
Share/NCShareExtension.swift

@@ -1,11 +1,13 @@
 //
 //  NCShareExtension.swift
-//  Nextcloud
+//  Share
 //
 //  Created by Marino Faggiana on 20/04/2021.
 //  Copyright © 2021 Marino Faggiana. All rights reserved.
+//  Copyright © 2021 Henrik Storch. All rights reserved.
 //
 //  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@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
@@ -25,7 +27,11 @@ import UIKit
 import NCCommunication
 import IHProgressHUD
 
-class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDelegate, NCRenameFileDelegate, NCAccountRequestDelegate {
+enum NCShareExtensionError: Error {
+    case cancel, fileUpload, noAccount, noFiles
+}
+
+class NCShareExtension: UIViewController {
 
     @IBOutlet weak var collectionView: UICollectionView!
     @IBOutlet weak var tableView: UITableView!
@@ -40,6 +46,7 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
     @IBOutlet weak var createFolderLabel: UILabel!
 
     @IBOutlet weak var uploadView: UIView!
+    // is this still needed?
     @IBOutlet weak var uploadImage: UIImageView!
     @IBOutlet weak var uploadLabel: UILabel!
 
@@ -48,26 +55,28 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
     var filesName: [String] = []
     // -------------------------------------------------------------
 
-    private var emptyDataSet: NCEmptyDataSet?
-    private let keyLayout = NCGlobal.shared.layoutViewShareExtension
-    private var metadataFolder: tableMetadata?
-    private var networkInProgress = false
-    private var dataSource = NCDataSource()
+    var emptyDataSet: NCEmptyDataSet?
+    let keyLayout = NCGlobal.shared.layoutViewShareExtension
+    var metadataFolder: tableMetadata?
+    var networkInProgress = false
+    var dataSource = NCDataSource()
 
-    private var layoutForView: NCGlobal.layoutForViewType?
+    var layoutForView: NCGlobal.layoutForViewType?
 
-    private var heightRowTableView: CGFloat = 50
-    private var heightCommandView: CGFloat = 170
+    let heightRowTableView: CGFloat = 50
+    private let heightCommandView: CGFloat = 170
 
-    private var autoUploadFileName = ""
-    private var autoUploadDirectory = ""
+    var autoUploadFileName = ""
+    var autoUploadDirectory = ""
 
-    private let refreshControl = UIRefreshControl()
-    private var activeAccount: tableAccount!
+    let refreshControl = UIRefreshControl()
+    var activeAccount: tableAccount!
     private let chunckSize = CCUtility.getChunkSize() * 1000000
 
-    private var numberFilesName: Int = 0
-    private var counterUpload: Int = 0
+    private var counterUploaded: Int = 0
+    private var uploadErrors: [tableMetadata] = []
+    var uploadMetadata: [tableMetadata] = []
+    var uploadStarted = false
 
     // MARK: - View Life Cycle
 
@@ -128,7 +137,7 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
         }
 
         // HUD
-        IHProgressHUD.set(viewForExtension: self.view)
+        IHProgressHUD.set(viewForExtension: self.collectionView)
         IHProgressHUD.set(defaultMaskType: .clear)
         IHProgressHUD.set(minimumDismiss: 0)
 
@@ -137,52 +146,27 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
 
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
+        guard serverUrl.isEmpty else { return }
 
-        if serverUrl == "" {
-
-            if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
-
-                setAccount(account: activeAccount.account)
-                getFilesExtensionContext { filesName in
-
-                    self.filesName = filesName
-                    DispatchQueue.main.async {
-
-                        var saveHtml: [String] = []
-                        var saveOther: [String] = []
-
-                        for fileName in self.filesName {
-                            if (fileName as NSString).pathExtension.lowercased() == "html" {
-                                saveHtml.append(fileName)
-                            } else {
-                                saveOther.append(fileName)
-                            }
-                        }
-
-                        if saveOther.count > 0 && saveHtml.count > 0 {
-                            for file in saveHtml {
-                                self.filesName = self.filesName.filter {$0 != file}
-                            }
-                        }
-
-                        self.setCommandView()
-                    }
-                }
-
-            } else {
-
-                let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_no_active_account_", comment: ""), preferredStyle: .alert)
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
-                    self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
-                }))
-                self.present(alertController, animated: true)
+        guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else {
+            return showAlert(description: "_no_active_account_") {
+                self.cancel(with: NCShareExtensionError.noAccount)
             }
         }
+
+        accountRequestChangeAccount(account: activeAccount.account)
+        guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else {
+            cancel(with: NCShareExtensionError.noFiles)
+            return
+        }
+        NCFilesExtensionHandler(items: inputItems) { fileNames in
+            self.filesName = fileNames
+            DispatchQueue.main.async { self.setCommandView() }
+        }
     }
 
     override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
         super.viewWillTransition(to: size, with: coordinator)
-
         coordinator.animate(alongsideTransition: nil) { _ in
             self.collectionView?.collectionViewLayout.invalidateLayout()
         }
@@ -190,46 +174,35 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
 
     override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
         super.traitCollectionDidChange(previousTraitCollection)
-
         collectionView.reloadData()
         tableView.reloadData()
     }
 
     // MARK: -
 
-    @objc func triggerProgressTask(_ notification: NSNotification) {
-
-        if let userInfo = notification.userInfo as NSDictionary?, let progressNumber = userInfo["progress"] as? NSNumber {
+    func cancel(with error: NCShareExtensionError) {
+        // make sure no uploads are continued
+        uploadStarted = false
+        let metadata = uploadMetadata[counterUploaded]
+        let filePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
 
-            let progress = CGFloat(progressNumber.floatValue)
-            let status =  NSLocalizedString("_upload_file_", comment: "") + " \(self.counterUpload) " + NSLocalizedString("_of_", comment: "") + " \(self.numberFilesName)"
-            IHProgressHUD.show(progress: progress, status: status)
-        }
+        NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+        NCNetworking.shared.uploadRequest[filePath]?.tasks.forEach({ $0.cancel() })
+        extensionContext?.cancelRequest(withError: error)
     }
 
-    // MARK: -
-
-    func setAccount(account: String) {
-
-        guard let activeAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) else {
-            extensionContext?.completeRequest(returningItems: extensionContext?.inputItems, completionHandler: nil)
-            return
-        }
-        self.activeAccount = activeAccount
-
-        // NETWORKING
-        NCCommunicationCommon.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), webDav: NCUtilityFileSystem.shared.getWebDAV(account: activeAccount.account), nextcloudVersion: 0, delegate: NCNetworking.shared)
-
-        // get auto upload folder
-        autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
-        autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: activeAccount.urlBase, account: activeAccount.account)
-
-        serverUrl = NCUtilityFileSystem.shared.getHomeServer(account: activeAccount.account)
-
-        layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
+    func showAlert(title: String = "_error_", description: String, onDismiss: (() -> Void)? = nil) {
+        let alertController = UIAlertController(title: NSLocalizedString(title, comment: ""), message: NSLocalizedString(description, comment: ""), preferredStyle: .alert)
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
+            onDismiss?()
+        }))
+        self.present(alertController, animated: true)
+    }
 
-        reloadDatasource(withLoadFolder: true)
-        setNavigationBar(navigationTitle: NCBrandOptions.shared.brand)
+    @objc func triggerProgressTask(_ notification: NSNotification) {
+        guard let progress = notification.userInfo?["progress"] as? CGFloat else { return }
+        let status = NSLocalizedString("_upload_file_", comment: "") + " \(counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(filesName.count)"
+        IHProgressHUD.show(progress: progress, status: status)
     }
 
     func setNavigationBar(navigationTitle: String) {
@@ -238,18 +211,15 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
         cancelButton.title = NSLocalizedString("_cancel_", comment: "")
 
         // BACK BUTTON
-
         let backButton = UIButton(type: .custom)
         backButton.setImage(UIImage(named: "back"), for: .normal)
         backButton.tintColor = .systemBlue
         backButton.semanticContentAttribute = .forceLeftToRight
-        backButton.setTitle(" "+NSLocalizedString("_back_", comment: ""), for: .normal)
+        backButton.setTitle(" " + NSLocalizedString("_back_", comment: ""), for: .normal)
         backButton.setTitleColor(.systemBlue, for: .normal)
         backButton.action(for: .touchUpInside) { _ in
 
-            while self.serverUrl.last != "/" {
-                self.serverUrl.removeLast()
-            }
+            while self.serverUrl.last != "/" { self.serverUrl.removeLast() }
             self.serverUrl.removeLast()
 
             self.reloadDatasource(withLoadFolder: true)
@@ -262,7 +232,6 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
         }
 
         // PROFILE BUTTON
-
         let image = NCUtility.shared.loadUserImage(
             for: activeAccount.user,
                displayName: activeAccount.displayName,
@@ -287,100 +256,39 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
         profileButton.semanticContentAttribute = .forceLeftToRight
         profileButton.sizeToFit()
         profileButton.action(for: .touchUpInside) { _ in
-
-            let accounts = NCManageDatabase.shared.getAllAccountOrderAlias()
-            if accounts.count > 1 {
-
-                if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
-
-                    // Only here change the active account
-                    for account in accounts {
-                        if account.account == self.activeAccount.account {
-                            account.active = true
-                        } else {
-                            account.active = false
-                        }
-                    }
-
-                    vcAccountRequest.activeAccount = self.activeAccount
-                    vcAccountRequest.accounts = accounts.sorted { sorg, dest -> Bool in
-                        return sorg.active && !dest.active
-                    }
-                    vcAccountRequest.enableTimerProgress = false
-                    vcAccountRequest.enableAddAccount = false
-                    vcAccountRequest.delegate = self
-                    vcAccountRequest.dismissDidEnterBackground = true
-
-                    let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
-                    let numberCell = accounts.count
-                    let height = min(CGFloat(numberCell * Int(vcAccountRequest.heightCell) + 45), screenHeighMax)
-
-                    let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height+20)
-
-                    self.present(popup, animated: true)
-                }
-            }
+            self.showAccountPicker()
         }
-
-        if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: activeAccount.account) {
-
-            navigationItem.setLeftBarButtonItems([UIBarButtonItem(customView: profileButton)], animated: true)
-
-        } else {
-
+        var navItems = [UIBarButtonItem(customView: profileButton)]
+        if serverUrl != NCUtilityFileSystem.shared.getHomeServer(account: activeAccount.account) {
             let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
             space.width = 20
-
-            navigationItem.setLeftBarButtonItems([UIBarButtonItem(customView: backButton), space, UIBarButtonItem(customView: profileButton)], animated: true)
+            navItems.append(contentsOf: [UIBarButtonItem(customView: backButton), space])
         }
+        navigationItem.setLeftBarButtonItems(navItems, animated: true)
     }
 
     func setCommandView() {
-
-        var counter: CGFloat = 0
-
-        if filesName.count == 0 {
-            self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
+        guard !filesName.isEmpty else {
+            cancel(with: NCShareExtensionError.noFiles)
             return
-        } else {
-            if filesName.count < 3 {
-                counter = CGFloat(filesName.count)
-                self.commandViewHeightConstraint.constant = heightCommandView + (self.heightRowTableView * counter)
-            } else {
-                counter = 3
-                self.commandViewHeightConstraint.constant = heightCommandView + (self.heightRowTableView * counter)
-            }
-            if filesName.count <= 3 {
-                self.tableView.isScrollEnabled = false
-            }
-            // Label upload button
-            numberFilesName = filesName.count
-            uploadLabel.text = NSLocalizedString("_upload_", comment: "") + " \(numberFilesName) " + NSLocalizedString("_files_", comment: "")
-            // Empty
-            emptyDataSet = NCEmptyDataSet(view: collectionView, offset: -50*counter, delegate: self)
-            self.tableView.reloadData()
         }
-    }
+        let counter = min(CGFloat(filesName.count), 3)
+        self.commandViewHeightConstraint.constant = heightCommandView + (self.heightRowTableView * counter)
 
-    // MARK: - Empty
-
-    func emptyDataSetView(_ view: NCEmptyView) {
-
-        if networkInProgress {
-            view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
-            view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
-            view.emptyDescription.text = ""
-        } else {
-            view.emptyImage.image = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
-            view.emptyTitle.text = NSLocalizedString("_files_no_folders_", comment: "")
-            view.emptyDescription.text = ""
+        if filesName.count <= 3 {
+            self.tableView.isScrollEnabled = false
         }
+        // Label upload button
+        uploadLabel.text = NSLocalizedString("_upload_", comment: "") + " \(filesName.count) " + NSLocalizedString("_files_", comment: "")
+        // Empty
+        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: -50 * counter, delegate: self)
+        self.tableView.reloadData()
     }
 
     // MARK: ACTION
 
     @IBAction func actionCancel(_ sender: UIBarButtonItem) {
-        extensionContext?.completeRequest(returningItems: extensionContext?.inputItems, completionHandler: nil)
+        cancel(with: NCShareExtensionError.cancel)
     }
 
     @objc func actionCreateFolder() {
@@ -391,609 +299,102 @@ class NCShareExtension: UIViewController, NCListCellDelegate, NCEmptyDataSetDele
             textField.autocapitalizationType = UITextAutocapitalizationType.words
         }
 
-        let actionSave = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default) { (_: UIAlertAction) in
+        let actionSave = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default) { _ in
             if let fileName = alertController.textFields?.first?.text {
                 self.createFolder(with: fileName)
             }
         }
 
-        let actionCancel = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in
-            print("You've pressed cancel button")
-        }
+        let actionCancel = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)
 
         alertController.addAction(actionSave)
         alertController.addAction(actionCancel)
 
         self.present(alertController, animated: true, completion: nil)
     }
+}
 
+// MARK: - Upload
+extension NCShareExtension {
     @objc func actionUpload() {
+        guard !uploadStarted else { return }
+        guard !filesName.isEmpty else { return showAlert(description: "_files_no_files_") }
 
-        if let fileName = filesName.first {
+        counterUploaded = 0
+        uploadStarted = true
+        uploadErrors = []
 
-            counterUpload += 1
-            filesName.removeFirst()
+        var conflicts: [tableMetadata] = []
+        for fileName in filesName {
             let ocId = NSUUID().uuidString
-            let filePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!
-
-            if NCUtilityFileSystem.shared.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: filePath) {
-
-                let metadata = NCManageDatabase.shared.createMetadata(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: activeAccount.urlBase, url: "", contentType: "", livePhoto: false)
-
-                metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload
-                metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
-                metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: filePath)
-                metadata.status = NCGlobal.shared.metadataStatusWaitUpload
-
-                // E2EE
-                if CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) {
-                    metadata.e2eEncrypted = true
-                }
-
-                // CHUNCK
-                if chunckSize != 0 && metadata.size > chunckSize {
-                    metadata.chunk = true
-                }
-
-                NCNetworking.shared.upload(metadata: metadata) {
-
-                } completion: { errorCode, errorDescription in
-
-                    if errorCode == 0 {
-
-                        self.actionUpload()
-
-                    } else {
-
-                        IHProgressHUD.dismiss()
-
-                        NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocId))
-                        NCManageDatabase.shared.deleteChunks(account: self.activeAccount.account, ocId: ocId)
-
-                        let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
-                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
-                            self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
-                        }))
-                        self.present(alertController, animated: true)
-                    }
-                }
-            }
-        } else {
-
-            IHProgressHUD.showSuccesswithStatus(NSLocalizedString("_success_", comment: ""))
-
-            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-                self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
-            }
-        }
-    }
-
-    func rename(fileName: String, fileNameNew: String) {
-
-        if let row = self.filesName.firstIndex(where: {$0 == fileName}) {
-
-            if NCUtilityFileSystem.shared.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + fileNameNew)) {
-                filesName[row] = fileNameNew
-                tableView.reloadData()
-            }
-        }
-    }
-
-    func accountRequestChangeAccount(account: String) {
-        setAccount(account: account)
-    }
-}
-
-// MARK: - Collection View
-
-extension NCShareExtension: UICollectionViewDelegate {
-
-    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-
-        if let metadata = dataSource.cellForItemAt(indexPath: indexPath) {
-            if let serverUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName) {
-
-                if metadata.e2eEncrypted && !CCUtility.isEnd(toEndEnabled: activeAccount.account) {
-                    let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: NSLocalizedString("_e2e_goto_settings_for_enable_", comment: ""), preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                    self.present(alertController, animated: true)
-                    return
-                }
-
-                self.serverUrl = serverUrl
-                reloadDatasource(withLoadFolder: true)
-                setNavigationBar(navigationTitle: metadata.fileNameView)
-            }
-        }
-    }
-}
-
-extension NCShareExtension: UICollectionViewDataSource {
-
-    func numberOfSections(in collectionView: UICollectionView) -> Int {
-        return 1
-    }
-
-    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        let numberOfItems = dataSource.numberOfItems()
-        emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section)
-        return numberOfItems
-    }
-
-    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-
-        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else {
-            return collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! NCListCell
-        }
-
-        var tableShare: tableShare?
-        var isShare = false
-        var isMounted = false
-
-        if let metadataFolder = metadataFolder {
-            isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
-            isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
-        }
-
-        if dataSource.metadataShare[metadata.ocId] != nil {
-            tableShare = dataSource.metadataShare[metadata.ocId]
-        }
-
-        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! NCListCell
-        cell.delegate = self
-
-        cell.fileObjectId = metadata.ocId
-        cell.fileUser = metadata.ownerId
-        cell.labelTitle.text = metadata.fileNameView
-        cell.labelTitle.textColor = NCBrandColor.shared.label
-
-        cell.imageSelect.image = nil
-        cell.imageStatus.image = nil
-        cell.imageLocal.image = nil
-        cell.imageFavorite.image = nil
-        cell.imageShared.image = nil
-        cell.imageMore.image = nil
-
-        cell.imageItem.image = nil
-        cell.imageItem.backgroundColor = nil
-
-        cell.progressView.progress = 0.0
-
-        if metadata.directory {
-
-            if metadata.e2eEncrypted {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted
-            } else if isShare {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
-            } else if tableShare != nil && tableShare?.shareType != 3 {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
-            } else if tableShare != nil && tableShare?.shareType == 3 {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderPublic
-            } else if metadata.mountType == "group" {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderGroup
-            } else if isMounted {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderExternal
-            } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
-                cell.imageItem.image = NCBrandColor.cacheImages.folderAutomaticUpload
+            let toPath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!
+            guard NCUtilityFileSystem.shared.copyFile(atPath: (NSTemporaryDirectory() + fileName), toPath: toPath) else { continue }
+            let metadata = NCManageDatabase.shared.createMetadata(
+                account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId,
+                fileName: fileName, fileNameView: fileName,
+                ocId: ocId,
+                serverUrl: serverUrl, urlBase: activeAccount.urlBase, url: "",
+                contentType: "",
+                livePhoto: false)
+            metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload
+            metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
+            metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath)
+            metadata.status = NCGlobal.shared.metadataStatusWaitUpload
+            if NCManageDatabase.shared.getMetadataConflict(account: activeAccount.account, serverUrl: serverUrl, fileName: fileName) != nil {
+                conflicts.append(metadata)
             } else {
-                cell.imageItem.image = NCBrandColor.cacheImages.folder
-            }
-
-            cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date)
-
-            let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)!
-            let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", activeAccount.account, lockServerUrl))
-
-            // Local image: offline
-            if tableDirectory != nil && tableDirectory!.offline {
-                cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
+                uploadMetadata.append(metadata)
             }
-
-        }
-
-        // image Favorite
-        if metadata.favorite {
-            cell.imageFavorite.image = NCBrandColor.cacheImages.favorite
         }
 
-        cell.imageSelect.isHidden = true
-        cell.backgroundView = nil
-        cell.hideButtonMore(true)
-        cell.hideButtonShare(true)
-        cell.selectMode(false)
-
-        // Live Photo
-        if metadata.livePhoto {
-            cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto
-        }
-
-        // Remove last separator
-        if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 {
-            cell.separator.isHidden = true
+        if !conflicts.isEmpty {
+            guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict
+            else { return }
+            conflict.serverUrl = self.serverUrl
+            conflict.metadatasUploadInConflict = conflicts
+            conflict.delegate = self
+            self.present(conflict, animated: true, completion: nil)
         } else {
-            cell.separator.isHidden = false
+            upload()
         }
-
-        return cell
     }
-}
-
-// MARK: - Table View
-
-extension NCShareExtension: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
-        return heightRowTableView
-    }
-
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-    }
-}
 
-extension NCShareExtension: UITableViewDataSource {
+    func upload() {
+        guard uploadStarted else { return }
+        guard uploadMetadata.count > counterUploaded else { return finishedUploading() }
+        let metadata = uploadMetadata[counterUploaded]
 
-    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        filesName.count
-    }
-
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-
-        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
-        cell.backgroundColor = NCBrandColor.shared.systemBackground
-
-        let imageCell = cell.viewWithTag(10) as? UIImageView
-        let fileNameCell = cell.viewWithTag(20) as? UILabel
-        let moreButton = cell.viewWithTag(30) as? NCShareExtensionButtonWithIndexPath
-        let sizeCell = cell.viewWithTag(40) as? UILabel
+        // E2EE
+        metadata.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
 
-        imageCell?.layer.cornerRadius = 6
-        imageCell?.layer.masksToBounds = true
+        // CHUNCK
+        metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
 
-        let fileName = filesName[indexPath.row]
-        let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
-
-        if let image = UIImage(contentsOfFile: (NSTemporaryDirectory() + fileName)) {
-            imageCell?.image = image.resizeImage(size: CGSize(width: 80, height: 80), isAspectRation: true)
-        } else {
-            if resultInternalType.iconName.count > 0 {
-                imageCell?.image = UIImage(named: resultInternalType.iconName)
+        NCNetworking.shared.upload(metadata: metadata) { } completion: { errorCode, _ in
+            if errorCode == 0 {
+                self.counterUploaded += 1
+                // next
+                self.upload()
             } else {
-                imageCell?.image = NCBrandColor.cacheImages.file
-            }
-        }
-
-        fileNameCell?.text = fileName
-
-        let fileSize = NCUtilityFileSystem.shared.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
-        sizeCell?.text = CCUtility.transformedSize(fileSize)
-
-        moreButton?.setImage(NCUtility.shared.loadImage(named: "more").image(color: NCBrandColor.shared.label, size: 15), for: .normal)
-        moreButton?.indexPath = indexPath
-        moreButton?.fileName = fileName
-        moreButton?.image = imageCell?.image
-        moreButton?.action(for: .touchUpInside, { sender in
-
-            if let fileName = (sender as! NCShareExtensionButtonWithIndexPath).fileName {
-                let alertController = UIAlertController(title: "", message: fileName, preferredStyle: .alert)
-
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_delete_file_", comment: ""), style: .default) { (_: UIAlertAction) in
-                    if let index = self.filesName.firstIndex(of: fileName) {
-
-                        self.filesName.remove(at: index)
-                        if self.filesName.count == 0 {
-                            self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
-                        } else {
-                            self.setCommandView()
-                        }
-                    }
-                })
-
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_rename_file_", comment: ""), style: .default) { (_: UIAlertAction) in
-
-                    if let vcRename = UIStoryboard(name: "NCRenameFile", bundle: nil).instantiateInitialViewController() as? NCRenameFile {
-
-                        vcRename.delegate = self
-                        vcRename.fileName = fileName
-                        vcRename.imagePreview = (sender as! NCShareExtensionButtonWithIndexPath).image
-
-                        let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height)
-
-                        self.present(popup, animated: true)
-                    }
-                })
-
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in })
-
-                self.present(alertController, animated: true, completion: nil)
-            }
-        })
-
-        return cell
-    }
-}
-
-// MARK: - NC API & Algorithm
-
-extension NCShareExtension {
-
-    @objc func reloadDatasource(withLoadFolder: Bool) {
-
-        layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
-
-        let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
-        self.dataSource = NCDataSource(metadatasSource: metadatasSource, sort: layoutForView?.sort, ascending: layoutForView?.ascending, directoryOnTop: layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
-
-        if withLoadFolder {
-            loadFolder()
-        } else {
-            self.refreshControl.endRefreshing()
-        }
-
-        collectionView.reloadData()
-    }
-
-    func createFolder(with fileName: String) {
-
-        NCNetworking.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: activeAccount.account, urlBase: activeAccount.urlBase) { errorCode, errorDescription in
-
-            DispatchQueue.main.async {
-                if errorCode == 0 {
-
-                    self.serverUrl += "/" + fileName
-                    self.reloadDatasource(withLoadFolder: true)
-                    self.setNavigationBar(navigationTitle: fileName)
-
-                } else {
-
-                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                    self.present(alertController, animated: true)
-                }
+                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                NCManageDatabase.shared.deleteChunks(account: self.activeAccount.account, ocId: metadata.ocId)
+                self.uploadErrors.append(metadata)
             }
         }
     }
 
-    func loadFolder() {
-
-        networkInProgress = true
-        collectionView.reloadData()
-
-        NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { _, metadataFolder, _, _, _, _, errorCode, errorDescription in
-
-            DispatchQueue.main.async {
-                if errorCode != 0 {
-                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorDescription, preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                    self.present(alertController, animated: true)
-                }
-                self.networkInProgress = false
-                self.metadataFolder = metadataFolder
-                self.reloadDatasource(withLoadFolder: false)
+    func finishedUploading() {
+        uploadStarted = false
+        if !uploadErrors.isEmpty {
+            let fileList = "- " + uploadErrors.map({ $0.fileName }).joined(separator: "\n  - ")
+            showAlert(title: "_error_files_upload_", description: fileList) {
+                self.extensionContext?.cancelRequest(withError: NCShareExtensionError.fileUpload)
             }
-        }
-    }
-
-    func getFilesExtensionContext(completion: @escaping (_ filesName: [String]) -> Void) {
-
-        var itemsProvider: [NSItemProvider] = []
-        var filesName: [String] = []
-        var conuter = 0
-        let dateFormatter = DateFormatter()
-
-        // ----------------------------------------------------------------------------------------
-
-        // Image
-        func getItem(image: UIImage, fileNameOriginal: String?) {
-
-            var fileName: String = ""
-
-            if let pngImageData = image.pngData() {
-
-                if fileNameOriginal != nil {
-                    fileName =  fileNameOriginal!
-                } else {
-                    fileName = "\(dateFormatter.string(from: Date()))\(conuter).png"
-                }
-
-                let filenamePath = NSTemporaryDirectory() + fileName
-
-                if (try? pngImageData.write(to: URL(fileURLWithPath: filenamePath), options: [.atomic])) != nil {
-                    filesName.append(fileName)
-                }
-            }
-        }
-
-        // URL
-        func getItem(url: NSURL, fileNameOriginal: String?) {
-
-            guard let path = url.path else { return }
-
-            var fileName: String = ""
-
-            if fileNameOriginal != nil {
-                fileName =  fileNameOriginal!
-            } else {
-                if let ext = url.pathExtension {
-                    fileName = "\(dateFormatter.string(from: Date()))\(conuter)." + ext
-                }
-            }
-
-            let filenamePath = NSTemporaryDirectory() + fileName
-
-            do {
-                try FileManager.default.removeItem(atPath: filenamePath)
-            } catch { }
-
-            do {
-                try FileManager.default.copyItem(atPath: path, toPath: filenamePath)
-
-                do {
-                    let attr: NSDictionary? = try FileManager.default.attributesOfItem(atPath: filenamePath) as NSDictionary?
-
-                    if let _attr = attr {
-                        if _attr.fileSize() > 0 {
-                            filesName.append(fileName)
-                        }
-                    }
-
-                } catch { }
-            } catch { }
-        }
-
-        // Data
-        func getItem(data: Data, fileNameOriginal: String?, description: String) {
-
-            var fileName: String = ""
-
-            if data.count > 0 {
-
-                if fileNameOriginal != nil {
-                    fileName =  fileNameOriginal!
-                } else {
-                    let fullNameArr = description.components(separatedBy: "\"")
-                    let fileExtArr = fullNameArr[1].components(separatedBy: ".")
-                    let pathExtention = (fileExtArr[fileExtArr.count-1]).uppercased()
-                    fileName = "\(dateFormatter.string(from: Date()))\(conuter).\(pathExtention)"
-                }
-
-                let filenamePath = NSTemporaryDirectory() + fileName
-                FileManager.default.createFile(atPath: filenamePath, contents: data, attributes: nil)
-                filesName.append(fileName)
-            }
-        }
-
-        // String
-        func getItem(string: NSString, fileNameOriginal: String?) {
-
-            var fileName: String = ""
-
-            if string.length > 0 {
-
-                fileName = "\(dateFormatter.string(from: Date()))\(conuter).txt"
-                let filenamePath = NSTemporaryDirectory() + "\(dateFormatter.string(from: Date()))\(conuter).txt"
-                FileManager.default.createFile(atPath: filenamePath, contents: string.data(using: String.Encoding.utf8.rawValue), attributes: nil)
-                filesName.append(fileName)
-            }
-        }
-
-        // ----------------------------------------------------------------------------------------
-
-        guard let inputItems: [NSExtensionItem] = extensionContext?.inputItems as? [NSExtensionItem] else {
-            return completion(filesName)
-        }
-
-        for item: NSExtensionItem in inputItems {
-            if let attachments = item.attachments {
-                if attachments.isEmpty { continue }
-                for (_, itemProvider) in (attachments.enumerated()) {
-                    if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeItem as String) || itemProvider.hasItemConformingToTypeIdentifier("public.url") {
-                        itemsProvider.append(itemProvider)
-                    }
-                }
-            }
-        }
-
-        CCUtility.emptyTemporaryDirectory()
-        dateFormatter.dateFormat = "yyyy-MM-dd HH-mm-ss-"
-
-        for itemProvider in itemsProvider {
-
-            var typeIdentifier = ""
-            if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeItem as String) { typeIdentifier = kUTTypeItem as String }
-            if itemProvider.hasItemConformingToTypeIdentifier("public.url") { typeIdentifier = "public.url" }
-
-            itemProvider.loadItem(forTypeIdentifier: typeIdentifier, options: nil, completionHandler: {item, error -> Void in
-
-                if error == nil {
-
-                    var fileNameOriginal: String?
-
-                    if let url = item as? NSURL {
-                        if FileManager.default.fileExists(atPath: url.path ?? "") {
-                            fileNameOriginal = url.lastPathComponent!
-                        } else if url.scheme?.lowercased().contains("http") == true {
-                            fileNameOriginal = "\(dateFormatter.string(from: Date()))\(conuter).html"
-                        } else {
-                            fileNameOriginal = "\(dateFormatter.string(from: Date()))\(conuter)"
-                        }
-                    }
-
-                    if let image = item as? UIImage {
-                       getItem(image: image, fileNameOriginal: fileNameOriginal)
-                    }
-
-                    if let url = item as? URL {
-                        getItem(url: url as NSURL, fileNameOriginal: fileNameOriginal)
-                    }
-
-                    if let data = item as? Data {
-                        getItem(data: data, fileNameOriginal: fileNameOriginal, description: itemProvider.description)
-                    }
-
-                    if let string = item as? NSString {
-                        getItem(string: string, fileNameOriginal: fileNameOriginal)
-                    }
-                }
-
-                conuter += 1
-                if conuter == itemsProvider.count {
-                    completion(filesName)
-                }
-            })
-        }
-    }
-}
-
-/*
-let task = URLSession.shared.downloadTask(with: urlitem) { localURL, urlResponse, error in
-    
-    if let localURL = localURL {
-        
-        if fileNameOriginal != nil {
-            fileName =  fileNameOriginal!
         } else {
-            let ext = url.pathExtension
-            fileName = "\(dateFormatter.string(from: Date()))\(conuter)." + ext
-        }
-        
-        let filenamePath = NSTemporaryDirectory() + fileName
-      
-        do {
-            try FileManager.default.removeItem(atPath: filenamePath)
-        }
-        catch { }
-        
-        do {
-            try FileManager.default.copyItem(atPath: localURL.path, toPath:filenamePath)
-            
-            do {
-                let attr : NSDictionary? = try FileManager.default.attributesOfItem(atPath: filenamePath) as NSDictionary?
-                
-                if let _attr = attr {
-                    if _attr.fileSize() > 0 {
-                        
-                        filesName.append(fileName)
-                    }
-                }
-                
-            } catch let error {
-                outError = error
+            IHProgressHUD.showSuccesswithStatus(NSLocalizedString("_success_", comment: ""))
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+                self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
             }
-            
-        } catch let error {
-            outError = error
         }
     }
-    
-    if index + 1 == attachments.count {
-        completion(filesName, outError)
-    }
-}
-task.resume()
-*/
-
-class NCShareExtensionButtonWithIndexPath: UIButton {
-    var indexPath: IndexPath?
-    var fileName: String?
-    var image: UIImage?
 }

+ 7 - 0
iOSClient/AppDelegate.swift

@@ -890,3 +890,10 @@ extension AppDelegate: NCAudioRecorderViewControllerDelegate {
     func didFinishWithoutRecording(_ viewController: NCAudioRecorderViewController, fileName: String) {
     }
 }
+
+extension AppDelegate: NCCreateFormUploadConflictDelegate {
+    func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
+        guard let metadatas = metadatas, !metadatas.isEmpty else { return }
+        networkingProcessUpload?.createProcessUploads(metadatas: metadatas)
+    }
+}

+ 31 - 0
iOSClient/Extensions/UIImage+Extensions.swift

@@ -173,4 +173,35 @@ extension UIImage {
         UIGraphicsEndImageContext()
         return newImage
     }
+
+    /// Downsamles a image using ImageIO. Has better memory perfomance than redrawing using UIKit
+    ///
+    /// - [Source](https://swiftsenpai.com/development/reduce-uiimage-memory-footprint/)
+    /// - [Original Source, WWDC18](https://developer.apple.com/videos/play/wwdc2018/416/?time=1352)
+    /// - Parameters:
+    ///   - imageURL: The URL path of the image
+    ///   - pointSize: The target point size
+    ///   - scale: The point to pixel scale (Pixeld per point)
+    /// - Returns: The downsampled image, if successful
+    static func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat = UIScreen.main.scale) -> UIImage? {
+
+        // Create an CGImageSource that represent an image
+        let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
+        guard let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions) else { return nil }
+
+        // Calculate the desired dimension
+        let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
+
+        // Perform downsampling
+        let downsampleOptions = [
+            kCGImageSourceCreateThumbnailFromImageAlways: true,
+            kCGImageSourceShouldCacheImmediately: true,
+            kCGImageSourceCreateThumbnailWithTransform: true,
+            kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
+        ] as CFDictionary
+        guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else { return nil }
+
+        // Return the downsampled image as UIImage
+        return UIImage(cgImage: downsampledImage)
+    }
 }

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

@@ -463,6 +463,7 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
                         conflict.metadatasNOConflict = metadatasNOConflict
                         conflict.metadatasMOV = metadatasMOV
                         conflict.metadatasUploadInConflict = metadatasUploadInConflict
+                        conflict.delegate = self.appDelegate
 
                         self.appDelegate.window?.rootViewController?.present(conflict, animated: true, completion: nil)
                     }

+ 11 - 14
iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift

@@ -58,7 +58,6 @@ extension NCCreateFormUploadConflictDelegate {
     @objc var alwaysNewFileNameNumber: Bool = false
     @objc var textLabelDetailNewFile: String?
 
-    let appDelegate = UIApplication.shared.delegate as! AppDelegate
     var metadatasConflictNewFiles: [String] = []
     var metadatasConflictAlreadyExistingFiles: [String] = []
     var fileNamesPath: [String: String] = [:]
@@ -179,7 +178,9 @@ extension NCCreateFormUploadConflictDelegate {
         }))
 
         conflictAlert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_keep_existing_action_title_", comment: ""), style: .cancel, handler: { _ in
-            self.dismiss(animated: true, completion: nil)
+            self.dismiss(animated: true) {
+                self.delegate?.dismissCreateFormUploadConflict(metadatas: nil)
+            }
         }))
 
         conflictAlert.addAction(UIAlertAction(title: NSLocalizedString("_more_action_title_", comment: ""), style: .default, handler: { _ in
@@ -237,9 +238,9 @@ extension NCCreateFormUploadConflictDelegate {
     }
 
     @IBAction func buttonCancelTouch(_ sender: Any) {
-
-        delegate?.dismissCreateFormUploadConflict(metadatas: nil)
-        dismiss(animated: true)
+        dismiss(animated: true) {
+            self.delegate?.dismissCreateFormUploadConflict(metadatas: nil)
+        }
     }
 
     @IBAction func buttonContinueTouch(_ sender: Any) {
@@ -305,19 +306,15 @@ extension NCCreateFormUploadConflictDelegate {
                 }
 
             } else {
-                print("error")
+                // used UIAlert (replace all)
             }
         }
 
         metadatasNOConflict.append(contentsOf: metadatasMOV)
 
-        if delegate != nil {
-            delegate?.dismissCreateFormUploadConflict(metadatas: metadatasNOConflict)
-        } else {
-            appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatasNOConflict)
+        dismiss(animated: true) {
+            self.delegate?.dismissCreateFormUploadConflict(metadatas: self.metadatasNOConflict)
         }
-
-        dismiss(animated: true)
     }
 }
 
@@ -458,8 +455,8 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
 
                 do {
                     if metadataNewFile.classFile ==  NCCommunicationCommon.typeClassFile.image.rawValue {
-                        let data = try Data(contentsOf: URL(fileURLWithPath: filePathNewFile))
-                        if let image = UIImage(data: data) {
+                        // preserver memory especially for very large files in Share extension
+                        if let image = UIImage.downsample(imageAt: URL(fileURLWithPath: filePathNewFile), to: cell.imageNewFile.frame.size) {
                             cell.imageNewFile.image = image
                         }
                     }

+ 1 - 1
iOSClient/Main/NCPickerViewController.swift

@@ -160,7 +160,7 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate {
                 if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: fileName) != nil {
 
                     if let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict {
-
+                        conflict.delegate = appDelegate
                         conflict.serverUrl = serverUrl
                         conflict.metadatasUploadInConflict = [metadataForUpload]
 

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

@@ -219,8 +219,9 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
             }
 
             fileNameNew = (fileNameWithoutExt.text ?? "") + "." + (ext.text ?? "")
-            self.delegate?.rename(fileName: fileName, fileNameNew: fileNameNew)
-            self.dismiss(animated: true)
+            self.dismiss(animated: true) {
+                self.delegate?.rename(fileName: fileName, fileNameNew: fileNameNew)
+            }
         }
     }
 

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

@@ -143,6 +143,11 @@
 "_recent_"                  = "Recent";
 "_view_in_folder_"          = "View in folder";
 "_leave_share_"             = "Leave this share";
+
+/* Remove a file from a list, don't delete it entirely */
+"_remove_file_"             = "Remove file";
+
+/* Delete file and put it into the trash */
 "_delete_file_"             = "Delete file";
 "_delete_folder_"           = "Delete folder";
 "_delete_photo_"            = "Delete photo";
@@ -763,6 +768,7 @@
 "_request_entity_too_large_"            = "The file is too large";
 "_not_possible_download_"               = "It is not possible to download the file";
 "_not_possible_upload_"                 = "It is not possible to upload the file";
+"_error_files_upload_"                  = "Error uploading files";
 "_method_not_allowed_"                  = "The requested method is not supported";
 "_invalid_url_"                         = "Invalid server URL";
 "_invalid_literal_"                     = "Invalid search string";