瀏覽代碼

Merge pull request #2623 from nextcloud/develop

Version 4.9.0
Marino Faggiana 1 年之前
父節點
當前提交
b99999adc3
共有 100 個文件被更改,包括 3107 次插入2456 次删除
  1. 5 0
      .github/workflows/xcode.yml
  2. 10 115
      .swiftlint.yml
  3. 1 1
      Brand/Database.swift
  4. 2 0
      Brand/Intro/NCIntroCollectionViewCell.swift
  5. 1 1
      Brand/Intro/NCIntroViewController.swift
  6. 39 35
      Brand/NCBrand.swift
  7. 3 3
      Brand/iOSClient.plist
  8. 4 3
      ExternalResources/NCApplicationHandle.swift
  9. 3 3
      File Provider Extension/FileProviderData.swift
  10. 2 2
      File Provider Extension/FileProviderDomain.swift
  11. 1 1
      File Provider Extension/FileProviderEnumerator.swift
  12. 3 5
      File Provider Extension/FileProviderExtension+Actions.swift
  13. 9 13
      File Provider Extension/FileProviderExtension.swift
  14. 155 28
      Nextcloud.xcodeproj/project.pbxproj
  15. 0 1
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme
  16. 2 2
      Notification Service Extension/NotificationService.swift
  17. 1 1
      README.md
  18. 1 0
      Share/NCShareExtension+DataSource.swift
  19. 2 1
      Share/NCShareExtension+NCDelegate.swift
  20. 12 4
      Share/NCShareExtension.swift
  21. 19 15
      Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift
  22. 2 8
      Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift
  23. 1 1
      Tests/NextcloudUITests/LoginUITests.swift
  24. 7 13
      Widget/Dashboard/DashboardData.swift
  25. 8 7
      Widget/Dashboard/DashboardWidgetView.swift
  26. 5 5
      Widget/Files/FilesData.swift
  27. 6 5
      Widget/Files/FilesWidgetView.swift
  28. 1 1
      Widget/Lockscreen/LockscreenData.swift
  29. 3 0
      Widget/Lockscreen/LockscreenWidgetView.swift
  30. 1 3
      Widget/Toolbar/ToolbarWidgetView.swift
  31. 16 0
      Widget/Widget.swift
  32. 16 23
      iOSClient/Activity/NCActivity.swift
  33. 14 5
      iOSClient/Activity/NCActivityTableViewCell.swift
  34. 161 204
      iOSClient/AppDelegate.swift
  35. 2 2
      iOSClient/BrowserWeb/NCBrowserWeb.swift
  36. 1 1
      iOSClient/Color/NCColorPicker.swift
  37. 7 96
      iOSClient/Data/NCDataSource.swift
  38. 2 40
      iOSClient/Data/NCDatabase.swift
  39. 20 20
      iOSClient/Data/NCManageDatabase+Account.swift
  40. 12 6
      iOSClient/Data/NCManageDatabase+Activity.swift
  41. 4 2
      iOSClient/Data/NCManageDatabase+Avatar.swift
  42. 7 1
      iOSClient/Data/NCManageDatabase+Capabilities.swift
  43. 127 0
      iOSClient/Data/NCManageDatabase+Chunk.swift
  44. 4 2
      iOSClient/Data/NCManageDatabase+DashboardWidget.swift
  45. 31 5
      iOSClient/Data/NCManageDatabase+Directory.swift
  46. 289 79
      iOSClient/Data/NCManageDatabase+E2EE.swift
  47. 2 1
      iOSClient/Data/NCManageDatabase+LayoutForView.swift
  48. 209 0
      iOSClient/Data/NCManageDatabase+LocalFile.swift
  49. 97 52
      iOSClient/Data/NCManageDatabase+Metadata.swift
  50. 23 8
      iOSClient/Data/NCManageDatabase+Share.swift
  51. 2 1
      iOSClient/Data/NCManageDatabase+Video.swift
  52. 54 260
      iOSClient/Data/NCManageDatabase.swift
  53. 0 1
      iOSClient/EmptyView/NCEmptyDataSet.swift
  54. 9 3
      iOSClient/Extensions/UIAlertController+Extension.swift
  55. 13 0
      iOSClient/Extensions/UIDevice+Extension.swift
  56. 1 1
      iOSClient/Extensions/UINavigationController+Extension.swift
  57. 2 2
      iOSClient/Extensions/UIToolbar+Extension.swift
  58. 34 51
      iOSClient/Favorites/NCFavorite.swift
  59. 70 44
      iOSClient/Files/NCFiles.swift
  60. 28 35
      iOSClient/Groupfolders/NCGroupfolders.swift
  61. 40 29
      iOSClient/Login/NCLogin.swift
  62. 47 58
      iOSClient/Login/NCLoginWeb.swift
  63. 1 1
      iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift
  64. 226 345
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  65. 15 14
      iOSClient/Main/Collection Common/NCGridCell.swift
  66. 19 18
      iOSClient/Main/Collection Common/NCListCell.swift
  67. 7 5
      iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
  68. 18 19
      iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
  69. 62 72
      iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
  70. 27 27
      iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
  71. 8 5
      iOSClient/Main/Create cloud/NCCreateMenuAdd.swift
  72. 70 97
      iOSClient/Main/NCActionCenter.swift
  73. 1 0
      iOSClient/Main/NCCellProtocol.swift
  74. 27 14
      iOSClient/Main/NCMainTabBar.swift
  75. 8 2
      iOSClient/Main/NCPickerViewController.swift
  76. 112 20
      iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.swift
  77. 98 28
      iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib
  78. 1 0
      iOSClient/Media/Cell/NCGridMediaCell.swift
  79. 97 87
      iOSClient/Media/NCMedia.swift
  80. 4 4
      iOSClient/Media/NCMediaCommandView.xib
  81. 72 65
      iOSClient/Menu/AppDelegate+Menu.swift
  82. 20 25
      iOSClient/Menu/NCCollectionViewCommon+Menu.swift
  83. 11 13
      iOSClient/Menu/NCContextMenu.swift
  84. 4 5
      iOSClient/Menu/NCLoginWeb+Menu.swift
  85. 14 11
      iOSClient/Menu/NCMedia+Menu.swift
  86. 1 1
      iOSClient/Menu/NCMenu+FloatingPanel.swift
  87. 15 13
      iOSClient/Menu/NCMenuAction.swift
  88. 15 1
      iOSClient/Menu/NCShare+Menu.swift
  89. 1 1
      iOSClient/Menu/NCSortMenu.swift
  90. 16 0
      iOSClient/Menu/NCTrash+Menu.swift
  91. 8 7
      iOSClient/Menu/NCViewer+Menu.swift
  92. 44 0
      iOSClient/More/Cells/BaseNCMoreCell.swift
  93. 18 0
      iOSClient/More/Cells/CCCellMore.swift
  94. 57 0
      iOSClient/More/Cells/NCMoreAppSuggestionsCell.swift
  95. 157 0
      iOSClient/More/Cells/NCMoreAppSuggestionsCell.xib
  96. 23 0
      iOSClient/More/Cells/NCMoreUserCell.swift
  97. 3 3
      iOSClient/More/Cells/NCMoreUserCell.xib
  98. 3 3
      iOSClient/More/NCMore.storyboard
  99. 102 190
      iOSClient/More/NCMore.swift
  100. 69 51
      iOSClient/NCGlobal.swift

+ 5 - 0
.github/workflows/xcode.yml

@@ -44,6 +44,11 @@ jobs:
       run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
     - name: Install docker
       run: |
+        # Workaround for https://github.com/actions/runner-images/issues/8104
+        brew remove --ignore-dependencies qemu
+        curl -o ./qemu.rb https://raw.githubusercontent.com/Homebrew/homebrew-core/dc0669eca9479e9eeb495397ba3a7480aaa45c2e/Formula/qemu.rb
+        brew install ./qemu.rb
+
         brew install docker
         colima start
     - name: Create docker test server and export enviroment variables

+ 10 - 115
.swiftlint.yml

@@ -10,24 +10,25 @@ empty_count:
   severity: warning
 
 line_length:
-  warning: 400
+  warning: 1000
   error: 5000
 
 function_body_length:
    warning: 400
 
 type_body_length:
-  warning: 800
-  error: 1000
-  
+  warning: 1500
+  error: 2000
+
 file_length:
-  warning: 1000
-  error: 1500
+  warning: 2000
+  error: 2500
   ignore_comment_only_lines: true
 
 identifier_name:
   min_length: 0
-  
+  max_length: 100
+
 disabled_rules:  
   - unused_setter_value
   - large_tuple
@@ -38,120 +39,14 @@ disabled_rules:
   - nesting
   - shorthand_operator
   - type_name
-    
+  - void_function_in_ternary
+
 excluded:
   - Carthage
   - Pods
   - Tests
-
-  # iOS Files Quarantine
-
   - Brand/NCBrand.swift
-  - File Provider Extension/FileProviderData.swift
-  - File Provider Extension/FileProviderDomain.swift
-  - File Provider Extension/FileProviderEnumerator.swift
-  - File Provider Extension/FileProviderExtension+Actions.swift
-  - File Provider Extension/FileProviderExtension+Thumbnail.swift
-  - File Provider Extension/FileProviderExtension.swift
-  - File Provider Extension/FileProviderUtility.swift
-  - Notification Service Extension/NotificationService.swift
-  - Widget/Widget.swift
-  - Widget/Dashboard/DashboardData.swift
-  - Widget/Dashboard/DashboardWidgetView.swift
-  - Widget/Files/FilesData.swift
-  - Widget/Files/FilesWidgetView.swift
-  - Widget/Lockscreen/LockscreenData.swift
-  - Widget/Lockscreen/LockscreenWidgetView.swift
-  - Widget/Lockscreen/LockscreenWidgetProvider.swift
-  - iOSClient/GUI
-  - iOSClient/ExternalResources
-  - iOSClient/Activity/NCActivity.swift
-  - iOSClient/Activity/NCActivityTableViewCell.swift
-  - iOSClient/AppDelegate.swift
-  - iOSClient/BackgroundImageColor/NCBackgroundImageColor.swift
-  - iOSClient/BrowserWeb/NCBrowserWeb.swift
-  - iOSClient/Diagnostics/NCCapabilitiesViewController.swift
-  - iOSClient/EmptyView/NCEmptyDataSet.swift
-  - iOSClient/Extensions/UIColor+Extensions.swift
-  - iOSClient/Extensions/UIImage+Extensions.swift
-  - iOSClient/FileViewInFolder/NCFileViewInFolder.swift
-  - iOSClient/Login/NCAppConfigView.swift
-  - iOSClient/Login/NCLogin.swift
-  - iOSClient/Login/NCLoginWeb.swift
-  - iOSClient/Main/Account Request/NCAccountRequest.swift
-  - iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift
-  - iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
-  - iOSClient/Main/Collection Common/NCGridCell.swift
-  - iOSClient/Main/Collection Common/NCListCell.swift
-  - iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift
-  - iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
-  - iOSClient/Main/Create cloud/NCCreateFormUploadConflictCell.swift
-  - iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
-  - iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift
-  - iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
-  - iOSClient/Main/Create cloud/NCCreateMenuAdd.swift
-  - iOSClient/Main/NCFunctionCenter.swift
-  - iOSClient/Main/NCMainTabBar.swift
-  - iOSClient/Main/NCPickerViewController.swift
-  - iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift
-  - iOSClient/Media/Cell/NCGridMediaCell.swift
-  - iOSClient/Media/NCMedia.swift
-  - iOSClient/Menu/AppDelegate+Menu.swift
-  - iOSClient/Menu/NCCollectionViewCommon+Menu.swift
-  - iOSClient/Menu/NCLoginWeb+Menu.swift
-  - iOSClient/Menu/NCMedia+Menu.swift
-  - iOSClient/Menu/NCSortMenu.swift
-  - iOSClient/Menu/NCViewer+Menu.swift
-  - iOSClient/More/NCMore.swift
   - iOSClient/NCGlobal.swift
-  - iOSClient/Networking/NCAutoUpload.swift
-  - iOSClient/Networking/NCNetworking.swift
-  - iOSClient/Networking/NCNetworkingCheckRemoteUser.swift
-  - iOSClient/Networking/NCNetworkingChunkedUpload.swift
-  - iOSClient/Networking/E2EE/NCNetworkingE2EE.swift
-  - iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift
-  - iOSClient/Networking/E2EE/NCNetworkingE2EEDelete.swift
-  - iOSClient/Networking/E2EE/NCNetworkingE2EERename.swift
-  - iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift
-  - iOSClient/Networking/NCNetworkingProcessUpload.swift
-  - iOSClient/Networking/NCOperationQueue.swift
-  - iOSClient/Networking/NCService.swift
-  - iOSClient/Networking/NCConfigServer.swift
-  - iOSClient/Notification/NCNotification.swift
-  - iOSClient/Recent/NCRecent.swift
-  - iOSClient/Rename file/NCRenameFile.swift
-  - iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift
-  - iOSClient/RichWorkspace/NCViewerRichWorkspace.swift
-  - iOSClient/ScanDocument/ScanCollectionView.swift
-  - iOSClient/Security/NCEndToEndMetadata.swift
-  - iOSClient/Security/NCViewCertificateDetails.swift
-  - iOSClient/Select/NCSelect.swift
-  - iOSClient/Settings/NCEndToEndInitialize.swift
-  - iOSClient/Settings/NCManageAutoUploadFileName.swift
-  - iOSClient/Settings/NCManageE2EE.swift
-  - iOSClient/Share/NCShareCommon.swift
-  - iOSClient/Share/NCShareNetworking.swift
-  - iOSClient/Shares/NCShares.swift
-  - iOSClient/Transfers/NCTransferCell.swift
-  - iOSClient/Transfers/NCTransfers.swift
-  - iOSClient/UserStatus/NCUserStatus.swift
-  - iOSClient/Utility/NCAskAuthorization.swift
-  - iOSClient/Utility/NCContentPresenter.swift
   - iOSClient/Utility/NCLivePhoto.swift
-  - iOSClient/Utility/NCPopupViewController.swift
-  - iOSClient/Utility/NCStoreReview.swift
-  - iOSClient/Utility/NCUtility.swift
-  - iOSClient/Utility/NCUtilityFileSystem.swift
-  - iOSClient/Utility/NCUtilityGUI.swift
-  - iOSClient/Viewer/NCViewer.swift
-  - iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift
-  - iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift
-  - iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift
-  - iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift
-  - iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift
-  - iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift
-  - iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift
-  - iOSClient/Viewer/NCViewerProviderContextMenu.swift
-  - iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift
 
 reporter: "xcode"

+ 1 - 1
Brand/Database.swift

@@ -26,4 +26,4 @@ import Foundation
 // Database Realm
 //
 let databaseName                    = "nextcloud.realm"
-let databaseSchemaVersion: UInt64   = 301
+let databaseSchemaVersion: UInt64   = 318

+ 2 - 0
Brand/Intro/NCIntroCollectionViewCell.swift

@@ -30,6 +30,8 @@ class NCIntroCollectionViewCell: UICollectionViewCell {
     @IBOutlet weak var titleLabel: UILabel!
     @IBOutlet weak var imageView: UIImageView!
 
+    var indexPath = IndexPath()
+
     override func awakeFromNib() {
         super.awakeFromNib()
     }

+ 1 - 1
Brand/Intro/NCIntroViewController.swift

@@ -137,7 +137,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
         let cell = (collectionView.dequeueReusableCell(withReuseIdentifier: "introCell", for: indexPath) as? NCIntroCollectionViewCell)!
         cell.backgroundColor = NCBrandColor.shared.customer
-
+        cell.indexPath = indexPath
         cell.titleLabel.textColor = textColor
         cell.titleLabel.text = titles[indexPath.row]
         cell.imageView.image = images[indexPath.row]

+ 39 - 35
Brand/NCBrand.swift

@@ -23,7 +23,11 @@
 
 import UIKit
 
-// MARK: - Options
+let userAgent: String = {
+    let appVersion: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
+    // Original Nextcloud useragent "Mozilla/5.0 (iOS) Nextcloud-iOS/\(appVersion)"
+    return "Mozilla/5.0 (iOS) Nextcloud-iOS/\(appVersion)"
+}()
 
 @objc class NCBrandOptions: NSObject {
     @objc static let shared: NCBrandOptions = {
@@ -54,33 +58,31 @@ import UIKit
     @objc public var capabilitiesGroups: String = "group.it.twsweb.Crypto-Cloud"
     @objc public var capabilitiesGroupApps: String = "group.com.nextcloud.apps"
 
-    // User Agent
-    @objc public var userAgent: String = "Nextcloud-iOS"                                                            // Don't touch me !!
-
     // BRAND ONLY
-    @objc public var use_login_web_personalized:        Bool = false                                                // Don't touch me !!
-    @objc public var use_AppConfig:                     Bool = false                                                // Don't touch me !!
-    @objc public var use_GroupApps:                     Bool = true                                                 // Don't touch me !!
+    @objc public var use_login_web_personalized: Bool = false                                   // Don't touch me !!
+    @objc public var use_AppConfig: Bool = false                                                // Don't touch me !!
+    @objc public var use_GroupApps: Bool = true                                                 // Don't touch me !!
 
     // Options
-    @objc public var use_default_auto_upload:           Bool = false
-    @objc public var use_themingColor:                  Bool = true
-    @objc public var use_themingLogo:                   Bool = false
-    @objc public var use_storeLocalAutoUploadAll:       Bool = false
-    @objc public var use_loginflowv2:                   Bool = false                                                // Don't touch me !!
-
-    @objc public var disable_intro:                     Bool = false
-    @objc public var disable_request_login_url:         Bool = false
-    @objc public var disable_multiaccount:              Bool = false
-    @objc public var disable_manage_account:            Bool = false
-    @objc public var disable_more_external_site:        Bool = false
-    @objc public var disable_openin_file:               Bool = false                                                // Don't touch me !!
-    @objc public var disable_crash_service:             Bool = false
-    @objc public var disable_log:                       Bool = false
-    @objc public var disable_mobileconfig:              Bool = false
+    @objc public var use_default_auto_upload: Bool = false
+    @objc public var use_themingColor: Bool = true
+    @objc public var use_themingLogo: Bool = false
+    @objc public var use_storeLocalAutoUploadAll: Bool = false
+    @objc public var use_loginflowv2: Bool = false                                              // Don't touch me !!
+
+    @objc public var disable_intro: Bool = false
+    @objc public var disable_request_login_url: Bool = false
+    @objc public var disable_multiaccount: Bool = false
+    @objc public var disable_manage_account: Bool = false
+    @objc public var disable_more_external_site: Bool = false
+    @objc public var disable_openin_file: Bool = false                                          // Don't touch me !!
+    @objc public var disable_crash_service: Bool = false
+    @objc public var disable_log: Bool = false
+    @objc public var disable_mobileconfig: Bool = false
+    @objc public var disable_show_more_nextcloud_apps_in_settings: Bool = false
 
     // Internal option behaviour
-    @objc public var cleanUpDay:                        Int = 0                                                     // Set default "Delete, in the cache, all files older than" possible days value are: 0, 1, 7, 30, 90, 180, 365
+    @objc public var cleanUpDay: Int = 0                                                        // Set default "Delete, in the cache, all files older than" possible days value are: 0, 1, 7, 30, 90, 180, 365
 
     // Info Paging
     enum NCInfoPagingTab: Int, CaseIterable {
@@ -92,10 +94,10 @@ import UIKit
         if folderBrandAutoUpload != "" {
             folderDefaultAutoUpload = folderBrandAutoUpload
         }
-        
+
         // wrapper AppConfig
         if let configurationManaged = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"), use_AppConfig {
-            
+
             if let str = configurationManaged[NCGlobal.shared.configuration_brand] as? String {
                 brand = str
             }
@@ -122,9 +124,11 @@ import UIKit
             }
         }
     }
-}
 
-// MARK: - Color
+    @objc func getUserAgent() -> String {
+        return userAgent
+    }
+}
 
 class NCBrandColor: NSObject {
     @objc static let shared: NCBrandColor = {
@@ -172,15 +176,15 @@ class NCBrandColor: NSObject {
     }
 
     // Color
-    @objc public let customer: UIColor = UIColor(red: 0.0/255.0, green: 130.0/255.0, blue: 201.0/255.0, alpha: 1.0)         // BLU NC : #0082c9
+    @objc public let customer: UIColor = UIColor(red: 0.0 / 255.0, green: 130.0 / 255.0, blue: 201.0 / 255.0, alpha: 1.0)         // BLU NC : #0082c9
     @objc public var customerText: UIColor = .white
 
     @objc public var brand: UIColor                                                                                         // don't touch me
     @objc public var brandElement: UIColor                                                                                  // don't touch me
     @objc public var brandText: UIColor                                                                                     // don't touch me
 
-    @objc public let nextcloud: UIColor = UIColor(red: 0.0/255.0, green: 130.0/255.0, blue: 201.0/255.0, alpha: 1.0)
-    @objc public let yellowFavorite: UIColor = UIColor(red: 248.0/255.0, green: 205.0/255.0, blue: 70.0/255.0, alpha: 1.0)
+    @objc public let nextcloud: UIColor = UIColor(red: 0.0 / 255.0, green: 130.0 / 255.0, blue: 201.0 / 255.0, alpha: 1.0)
+    @objc public let yellowFavorite: UIColor = UIColor(red: 248.0 / 255.0, green: 205.0 / 255.0, blue: 70.0 / 255.0, alpha: 1.0)
 
     public var userColors: [CGColor] = []
     public var themingColor: String = ""
@@ -224,7 +228,7 @@ class NCBrandColor: NSObject {
         cacheImages.folderGroup = UIImage(named: "folder_group")!.image(color: brandElement, size: folderWidth)
         cacheImages.folderExternal = UIImage(named: "folder_external")!.image(color: brandElement, size: folderWidth)
         cacheImages.folderAutomaticUpload = UIImage(named: "folderAutomaticUpload")!.image(color: brandElement, size: folderWidth)
-        cacheImages.folder =  UIImage(named: "folder")!.image(color: brandElement, size: folderWidth)
+        cacheImages.folder = UIImage(named: "folder")!.image(color: brandElement, size: folderWidth)
 
         cacheImages.checkedYes = NCUtility.shared.loadImage(named: "checkmark.circle.fill", color: .systemBlue)
         cacheImages.checkedNo = NCUtility.shared.loadImage(named: "circle", color: .systemGray)
@@ -315,7 +319,7 @@ class NCBrandColor: NSObject {
             brand = customer
             brandText = customerText
         }
-        
+
         createImagesThemingColor()
 #if !EXTENSION
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeTheming)
@@ -352,9 +356,9 @@ class NCBrandColor: NSObject {
      3 colors \* 6 will result in 18 generated colors
      */
     func generateColors(steps: Int = 6) -> [CGColor] {
-        let red = UIColor(red: 182/255, green: 70/255, blue: 157/255, alpha: 1).cgColor
-        let yellow = UIColor(red: 221/255, green: 203/255, blue: 85/255, alpha: 1).cgColor
-        let blue = UIColor(red: 0/255, green: 130/255, blue: 201/255, alpha: 1).cgColor
+        let red = UIColor(red: 182 / 255, green: 70 / 255, blue: 157 / 255, alpha: 1).cgColor
+        let yellow = UIColor(red: 221 / 255, green: 203 / 255, blue: 85 / 255, alpha: 1).cgColor
+        let blue = UIColor(red: 0 / 255, green: 130 / 255, blue: 201 / 255, alpha: 1).cgColor
 
         let palette1 = mixPalette(steps: steps, color1: red, color2: yellow)
         let palette2 = mixPalette(steps: steps, color1: yellow, color2: blue)

+ 3 - 3
Brand/iOSClient.plist

@@ -65,11 +65,11 @@
 	<key>NSFaceIDUsageDescription</key>
 	<string>Face ID is required to authenticate using face recognition.</string>
 	<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
-	<string>GPS is used to detect new photos from camera roll. Continued use of GPS running in the background can dramatically decrease battery life.</string>
+	<string>The app will show your location on a map.</string>
 	<key>NSLocationAlwaysUsageDescription</key>
-	<string>GPS is used to detect new photos from camera roll. Continued use of GPS running in the background can dramatically decrease battery life.</string>
+	<string>The app will show your location on a map.</string>
 	<key>NSLocationWhenInUseUsageDescription</key>
-	<string>GPS is used to detect new photos from camera roll on background. It is useless to use GPS only while using the app.</string>
+	<string>The app will show your location on a map.</string>
 	<key>NSMicrophoneUsageDescription</key>
 	<string>Microphone access is required to create voice notes.</string>
 	<key>NSPhotoLibraryAddUsageDescription</key>

+ 4 - 3
ExternalResources/NCApplicationHandle.swift

@@ -32,8 +32,8 @@ class NCApplicationHandle: NSObject {
 
     // class: AppDelegate
     // func nextcloudPushNotificationAction(data: [String: AnyObject])
-    func nextcloudPushNotificationAction(data: [String: AnyObject], completion: @escaping () -> Void) {
-        completion()
+    func nextcloudPushNotificationAction(data: [String: AnyObject]) -> [String: AnyObject]? {
+        return data
     }
 
     // class: AppDelegate
@@ -86,6 +86,7 @@ class NCApplicationHandle: NSObject {
     }
 
     // class: NCNotification
-    func didSelectNotification(_ notification: NKNotifications, viewController: UIViewController) {
+    func didSelectNotification(_ notification: NKNotifications, viewController: UIViewController) -> NKNotifications? {
+        return notification
     }
 }

+ 3 - 3
File Provider Extension/FileProviderData.swift

@@ -93,7 +93,7 @@ class fileProviderData: NSObject {
 
             NCManageDatabase.shared.setCapabilities(account: account)
 
-            NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
+            NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
             NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
 
             return tableAccount.init(value: activeAccount)
@@ -101,7 +101,7 @@ class fileProviderData: NSObject {
 
         // DOMAIN
         let accounts = NCManageDatabase.shared.getAllAccount()
-        if accounts.count == 0 { return nil }
+        if accounts.isEmpty { return nil }
 
         for activeAccount in accounts {
             guard let url = NSURL(string: activeAccount.urlBase) else { continue }
@@ -117,7 +117,7 @@ class fileProviderData: NSObject {
 
                 NCManageDatabase.shared.setCapabilities(account: account)
 
-                NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: CCUtility.getUserAgent(), nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
+                NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
                 NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
 
                 return tableAccount.init(value: activeAccount)

+ 2 - 2
File Provider Extension/FileProviderDomain.swift

@@ -43,7 +43,7 @@ class FileProviderDomain: NSObject {
                 for account in accounts {
                     guard let urlBase = NSURL(string: account.urlBase) else { continue }
                     guard let host = urlBase.host else { continue }
-                    let accountDomain =  account.userId + " (" + host + ")"
+                    let accountDomain = account.userId + " (" + host + ")"
                     if domain == accountDomain {
                         domainFound = true
                         break
@@ -64,7 +64,7 @@ class FileProviderDomain: NSObject {
                 var domainFound = false
                 guard let urlBase = NSURL(string: account.urlBase) else { continue }
                 guard let host = urlBase.host else { continue }
-                let accountDomain =  account.userId + " (" + host + ")"
+                let accountDomain = account.userId + " (" + host + ")"
                 for domain in domains {
                     if domain == accountDomain {
                         domainFound = true

+ 1 - 1
File Provider Extension/FileProviderEnumerator.swift

@@ -173,7 +173,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
             for metadata in metadatas! {
 
-                if metadata.e2eEncrypted || (metadata.session != "" && metadata.session != NCNetworking.shared.sessionIdentifierBackgroundExtension) { continue }
+                if metadata.e2eEncrypted || (!metadata.session.isEmpty && metadata.session != NCNetworking.shared.sessionIdentifierBackgroundExtension) { continue }
 
                 fileProviderUtility.shared.createocIdentifierOnFileSystem(metadata: metadata)
 

+ 3 - 5
File Provider Extension/FileProviderExtension+Actions.swift

@@ -37,15 +37,13 @@ extension FileProviderExtension {
         let directoryName = NCUtilityFileSystem.shared.createFileName(directoryName, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.account)
         let serverUrlFileName = tableDirectory.serverUrl + "/" + directoryName
 
-        NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName) { account, ocId, _, error in
+        NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName) { _, ocId, _, error in
 
             if error == .success {
 
-                NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { account, files, _, error in
+                NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles()) { _, files, _, error in
 
-                    if error == .success && files.count > 0 {
-
-                        let file = files.first!
+                    if error == .success, let file = files.first {
 
                         let isDirectoryEncrypted = NCUtility.shared.isDirectoryE2EE(file: file)
                         let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryEncrypted)

+ 9 - 13
File Provider Extension/FileProviderExtension.swift

@@ -79,9 +79,9 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
             if fileProviderData.shared.setupAccount(domain: domain, providerExtension: self) == nil {
                 throw NSError(domain: NSFileProviderErrorDomain, code: NSFileProviderError.notAuthenticated.rawValue, userInfo: [:])
             } else if let passcode = CCUtility.getPasscode(), !passcode.isEmpty, CCUtility.isPasscodeAtStartEnabled() {
-                throw NSError(domain: NSFileProviderErrorDomain, code: NSFileProviderError.notAuthenticated.rawValue, userInfo: ["code" : NSNumber(value: NCGlobal.shared.errorUnauthorizedFilesPasscode)])
+                throw NSError(domain: NSFileProviderErrorDomain, code: NSFileProviderError.notAuthenticated.rawValue, userInfo: ["code": NSNumber(value: NCGlobal.shared.errorUnauthorizedFilesPasscode)])
             } else if CCUtility.getDisableFilesApp() || NCBrandOptions.shared.disable_openin_file {
-                throw NSError(domain: NSFileProviderErrorDomain, code: NSFileProviderError.notAuthenticated.rawValue, userInfo: ["code" : NSNumber(value: NCGlobal.shared.errorDisableFilesApp)])
+                throw NSError(domain: NSFileProviderErrorDomain, code: NSFileProviderError.notAuthenticated.rawValue, userInfo: ["code": NSNumber(value: NCGlobal.shared.errorDisableFilesApp)])
             }
         }
 
@@ -190,26 +190,22 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
         let pathComponents = url.pathComponents
         let identifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
 
-        if let _ = outstandingSessionTasks[url] {
-            completionHandler(nil)
-            return
+        if outstandingSessionTasks[url] != nil {
+            return completionHandler(nil)
         }
 
         guard let metadata = fileProviderUtility.shared.getTableMetadataFromItemIdentifier(identifier) else {
-            completionHandler(NSFileProviderError(.noSuchItem))
-            return
+            return completionHandler(NSFileProviderError(.noSuchItem))
         }
 
         // Document VIEW ONLY
         if metadata.isDocumentViewableOnly {
-            completionHandler(NSFileProviderError(.noSuchItem))
-            return
+            return completionHandler(NSFileProviderError(.noSuchItem))
         }
 
         let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
         if tableLocalFile != nil && CCUtility.fileProviderStorageExists(metadata) && tableLocalFile?.etag == metadata.etag {
-            completionHandler(nil)
-            return
+            return completionHandler(nil)
         }
 
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
@@ -337,8 +333,8 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
                 // typefile directory ? (NOT PERMITTED)
                 do {
                     let attributes = try fileProviderUtility.shared.fileManager.attributesOfItem(atPath: fileURL.path)
-                    size = attributes[FileAttributeKey.size] as! Int64
-                    let typeFile = attributes[FileAttributeKey.type] as! FileAttributeType
+                    size = attributes[FileAttributeKey.size] as? Int64 ?? 0
+                    let typeFile = attributes[FileAttributeKey.type] as? FileAttributeType
                     if typeFile == FileAttributeType.typeDirectory {
                         completionHandler(nil, NSFileProviderError(.noSuchItem))
                         return

+ 155 - 28
Nextcloud.xcodeproj/project.pbxproj

@@ -68,7 +68,6 @@
 		AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
 		AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
 		AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
-		AFD33240276A02C100F5AE02 /* UIApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFD3323F276A02C000F5AE02 /* UIApplication+Extension.swift */; };
 		C0046CDD2A17B98400D87C9D /* LoginUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0046CDC2A17B98400D87C9D /* LoginUITests.swift */; };
 		C03BA14A2A17BC57002C8BA3 /* XLForm in Frameworks */ = {isa = PBXBuildFile; productRef = C03BA1492A17BC57002C8BA3 /* XLForm */; };
 		C03BA14C2A17BC60002C8BA3 /* UICKeyChainStore in Frameworks */ = {isa = PBXBuildFile; productRef = C03BA14B2A17BC60002C8BA3 /* UICKeyChainStore */; };
@@ -102,6 +101,7 @@
 		F31F69662A2F929600162F76 /* PreviewSnapshotsTesting in Frameworks */ = {isa = PBXBuildFile; productRef = F31F69652A2F929600162F76 /* PreviewSnapshotsTesting */; };
 		F31F69692A2F92F000162F76 /* SnapshotTestingHEIC in Frameworks */ = {isa = PBXBuildFile; productRef = F31F69682A2F92F000162F76 /* SnapshotTestingHEIC */; };
 		F32ED5062A2F254400EABA81 /* EnvVars.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30A96302A27AEBF00D7BCFE /* EnvVars.generated.swift */; };
+		F33AAF9A2A60394C006ECCBD /* NCMoreUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F33AAF992A60394C006ECCBD /* NCMoreUserCell.xib */; };
 		F343A4B32A1E01FF00DDA874 /* PHAsset+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */; };
 		F343A4B42A1E084100DDA874 /* PHAsset+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */; };
 		F343A4B52A1E084200DDA874 /* PHAsset+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */; };
@@ -116,7 +116,20 @@
 		F343A4BF2A1E734600DDA874 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */; };
 		F343A4C02A1E734600DDA874 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */; };
 		F343A4C12A1E734600DDA874 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */; };
+		F359D8672A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F359D8682A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F359D8692A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F359D86A2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F359D86C2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F359D86D2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F39298972A3B12CB00509762 /* BaseNCMoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */; };
+		F3953BD72A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */; };
 		F3A7AFC62A41AA82001FC89C /* BaseUIXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3A7AFC52A41AA82001FC89C /* BaseUIXCTestCase.swift */; };
+		F3BB464D2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */; };
+		F3BB464F2A39EBE500461F6E /* NCMoreUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB464E2A39EBE500461F6E /* NCMoreUserCell.swift */; };
+		F3BB46522A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */; };
+		F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */; };
 		F700222C1EC479840080073F /* Custom.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F700222B1EC479840080073F /* Custom.xcassets */; };
 		F700222D1EC479840080073F /* Custom.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F700222B1EC479840080073F /* Custom.xcassets */; };
 		F700510122DF63AC003A3356 /* NCShare.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F700510022DF63AC003A3356 /* NCShare.storyboard */; };
@@ -177,7 +190,6 @@
 		F7148041262EBE4000693E51 /* NCShareExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7148040262EBE4000693E51 /* NCShareExtension.swift */; };
 		F714804F262ED4F900693E51 /* NCGridCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4521903D010088454D /* NCGridCell.xib */; };
 		F7148054262ED51000693E51 /* NCListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4321903CF20088454D /* NCListCell.xib */; };
-		F7148059262ED52200693E51 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */; };
 		F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
 		F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7239876253D86D300257F49 /* NCEmptyView.xib */; };
 		F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F717402B24F699A5000C87D5 /* NCFavorite.storyboard */; };
@@ -197,6 +209,10 @@
 		F7245927289BB59300474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; };
 		F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F72685E927C78E490019EF5E /* InfoPlist.strings */; };
 		F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */; };
+		F72944F22A84246400246839 /* NCEndToEndMetadataV20.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72944F12A84246400246839 /* NCEndToEndMetadataV20.swift */; };
+		F72944F32A84246400246839 /* NCEndToEndMetadataV20.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72944F12A84246400246839 /* NCEndToEndMetadataV20.swift */; };
+		F72944F52A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72944F42A8424F800246839 /* NCEndToEndMetadataV1.swift */; };
+		F72944F62A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72944F42A8424F800246839 /* NCEndToEndMetadataV1.swift */; };
 		F72A17D828B221E300F3F159 /* DashboardWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72A17D728B221E300F3F159 /* DashboardWidgetView.swift */; };
 		F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72A47EB2487B06B005AD489 /* NCOperationQueue.swift */; };
 		F72AD70D28C24B93006CB92D /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = F72AD70C28C24B93006CB92D /* NextcloudKit */; };
@@ -294,6 +310,13 @@
 		F74AF3A4247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */; };
 		F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */; };
 		F74AF3A6247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */; };
+		F74B6D952A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
+		F74B6D962A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
+		F74B6D972A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
+		F74B6D982A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
+		F74B6D992A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
+		F74B6D9A2A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
+		F74B6D9B2A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */; };
 		F74C0436253F1CDC009762AB /* NCShares.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74C0434253F1CDC009762AB /* NCShares.swift */; };
 		F74C0437253F1CDC009762AB /* NCShares.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F74C0435253F1CDC009762AB /* NCShares.storyboard */; };
 		F74DE14325135B6800917068 /* NCTransfers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74DE14125135B6800917068 /* NCTransfers.swift */; };
@@ -415,6 +438,7 @@
 		F77BC3ED293E528A005F2B08 /* NCConfigServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BC3EC293E528A005F2B08 /* NCConfigServer.swift */; };
 		F77C97392953131000FDDD09 /* NCCameraRoll.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C97382953131000FDDD09 /* NCCameraRoll.swift */; };
 		F77C973A2953143A00FDDD09 /* NCCameraRoll.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C97382953131000FDDD09 /* NCCameraRoll.swift */; };
+		F77CB6A92AA08053000C3CA4 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = F77CB6A82AA08053000C3CA4 /* OpenSSL */; };
 		F77ED59128C9CE9D00E24ED0 /* ToolbarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77ED59028C9CE9D00E24ED0 /* ToolbarData.swift */; };
 		F77ED59328C9CEA000E24ED0 /* ToolbarWidgetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77ED59228C9CEA000E24ED0 /* ToolbarWidgetProvider.swift */; };
 		F77ED59528C9CEA400E24ED0 /* ToolbarWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77ED59428C9CEA300E24ED0 /* ToolbarWidgetView.swift */; };
@@ -456,6 +480,13 @@
 		F785EEA42461A4A600B3F945 /* NCUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70BFC7320E0FA7C00C67599 /* NCUtility.swift */; };
 		F785EEA52461A4CF00B3F945 /* CCUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F7053E3D1C639DF500741EA5 /* CCUtility.m */; };
 		F785EEA62461A4FB00B3F945 /* CCUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F7053E3D1C639DF500741EA5 /* CCUtility.m */; };
+		F7864ACC2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
+		F7864ACD2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
+		F7864ACE2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
+		F7864ACF2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
+		F7864AD02A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
+		F7864AD12A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
+		F7864AD22A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */; };
 		F787704F22E7019900F287A9 /* NCShareLinkCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F787704E22E7019900F287A9 /* NCShareLinkCell.xib */; };
 		F787AC09298BCB4A0001BB00 /* SVGKitSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F787AC08298BCB4A0001BB00 /* SVGKitSwift */; };
 		F787AC0B298BCB540001BB00 /* SVGKitSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F787AC0A298BCB540001BB00 /* SVGKitSwift */; };
@@ -476,7 +507,6 @@
 		F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4921903F850088454D /* NCTrashListCell.xib */; };
 		F78ACD52219046DC0088454D /* NCSectionHeaderMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */; };
 		F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
-		F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */; };
 		F78C6FDE296D677300C952C3 /* NCContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78C6FDD296D677300C952C3 /* NCContextMenu.swift */; };
 		F78E2D6529AF02DB0024D4F3 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78E2D6429AF02DB0024D4F3 /* Database.swift */; };
 		F78E2D6629AF02DB0024D4F3 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78E2D6429AF02DB0024D4F3 /* Database.swift */; };
@@ -493,7 +523,6 @@
 		F793E59E28B763C2005E4B02 /* NCAskAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = F733598025C1C188002ABA72 /* NCAskAuthorization.swift */; };
 		F793E59F28B764F6005E4B02 /* NCContentPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765608E23BF813500765969 /* NCContentPresenter.swift */; };
 		F793E5A128B76541005E4B02 /* NotificationCenter+MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70460512499061800BB98A7 /* NotificationCenter+MainThread.swift */; };
-		F793E5A228B76580005E4B02 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
 		F798F0E225880608000DAFFD /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; };
 		F798F0E725880609000DAFFD /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; };
 		F798F0EC2588060A000DAFFD /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */; };
@@ -509,6 +538,8 @@
 		F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F702F30725EE5D47008F8E80 /* NCPopupViewController.swift */; };
 		F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDA9F26B004980007D134 /* NCPlayerToolBar.swift */; };
 		F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDAA126B004980007D134 /* NCPlayer.swift */; };
+		F79FFB262A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */; };
+		F79FFB272A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */; };
 		F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
 		F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
 		F7A0D1372591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
@@ -546,12 +577,11 @@
 		F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCLoginWeb.swift */; };
 		F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */; };
 		F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; };
+		F7B398422A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */; };
+		F7B398432A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */; };
 		F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */; };
 		F7B7504B2397D38F004E13EC /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extension.swift */; };
 		F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F7B8B82F25681C3400967775 /* GoogleService-Info.plist */; };
-		F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
-		F7B8CD96261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
-		F7B8CD9B261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
 		F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */; };
 		F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */; };
 		F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */; };
@@ -588,7 +618,6 @@
 		F7CB689A2541676B0050EC94 /* NCMore.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7CB68992541676B0050EC94 /* NCMore.storyboard */; };
 		F7CB68A0254169530050EC94 /* NCSettings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7CB689F254169530050EC94 /* NCSettings.storyboard */; };
 		F7CBC31C24F78E79004D3812 /* NCSortMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CBC31B24F78E79004D3812 /* NCSortMenu.swift */; };
-		F7CF16A32A4D7C7A000FF107 /* NCMoreUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7CF16A22A4D7C7A000FF107 /* NCMoreUserCell.xib */; };
 		F7D1612023CF19E30039EBBF /* NCViewerRichWorkspace.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7D1611F23CF19E30039EBBF /* NCViewerRichWorkspace.storyboard */; };
 		F7D56B1A2972405500FA46C4 /* Mantis in Frameworks */ = {isa = PBXBuildFile; productRef = F7D56B192972405500FA46C4 /* Mantis */; };
 		F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7CA212C25F1333200826ABB /* NCAccountRequest.storyboard */; };
@@ -635,6 +664,8 @@
 		F7F4F10C27ECDBDB008676F9 /* Inconsolata-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F10427ECDBDB008676F9 /* Inconsolata-Regular.ttf */; };
 		F7F4F11027ECDC4A008676F9 /* UIDevice+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F4F10F27ECDC4A008676F9 /* UIDevice+Extension.swift */; };
 		F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F4F11127ECDC52008676F9 /* UIFont+Extension.swift */; };
+		F7F623B52A5EF4D30022D3D4 /* Gzip in Frameworks */ = {isa = PBXBuildFile; productRef = F7F623B42A5EF4D30022D3D4 /* Gzip */; };
+		F7F623B72A5EFA0C0022D3D4 /* Gzip in Frameworks */ = {isa = PBXBuildFile; productRef = F7F623B62A5EFA0C0022D3D4 /* Gzip */; };
 		F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */; };
 		F7F878AF1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */; };
 		F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F9D1BA25397CE000D9BFF5 /* NCViewer.swift */; };
@@ -796,7 +827,6 @@
 		AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = "<group>"; };
 		AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = "<group>"; };
 		AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = "<group>"; };
-		AFD3323F276A02C000F5AE02 /* UIApplication+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extension.swift"; sourceTree = "<group>"; };
 		C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		C0046CDC2A17B98400D87C9D /* LoginUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginUITests.swift; sourceTree = "<group>"; };
 		C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -809,9 +839,17 @@
 		F31F69442A2F6D4600162F76 /* NextcloudSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudSnapshotTests.swift; sourceTree = "<group>"; };
 		F31F694F2A2F707E00162F76 /* SwiftUIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftUIView+Extensions.swift"; sourceTree = "<group>"; };
 		F31F69602A2F907800162F76 /* __Snapshots__ */ = {isa = PBXFileReference; lastKnownFileType = folder; path = __Snapshots__; sourceTree = "<group>"; };
+		F33AAF992A60394C006ECCBD /* NCMoreUserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCMoreUserCell.xib; sourceTree = "<group>"; };
 		F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAsset+Extension.swift"; sourceTree = "<group>"; };
 		F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
+		F359D8662A7D03420023F405 /* NCUtility+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Exif.swift"; sourceTree = "<group>"; };
+		F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNCMoreCell.swift; sourceTree = "<group>"; };
+		F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseIntegrationXCTestCase.swift; sourceTree = "<group>"; };
 		F3A7AFC52A41AA82001FC89C /* BaseUIXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUIXCTestCase.swift; sourceTree = "<group>"; };
+		F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCMoreAppSuggestionsCell.xib; sourceTree = "<group>"; };
+		F3BB464E2A39EBE500461F6E /* NCMoreUserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreUserCell.swift; sourceTree = "<group>"; };
+		F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreAppSuggestionsCell.swift; sourceTree = "<group>"; };
+		F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCCellMore.swift; sourceTree = "<group>"; };
 		F700222B1EC479840080073F /* Custom.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Custom.xcassets; sourceTree = "<group>"; };
 		F700510022DF63AC003A3356 /* NCShare.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCShare.storyboard; sourceTree = "<group>"; };
 		F700510222DF6897003A3356 /* Parchment.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Parchment.framework; path = Carthage/Build/iOS/Parchment.framework; sourceTree = "<group>"; };
@@ -928,6 +966,8 @@
 		F7267A81225DFCE100D6DB7D /* AFNetworking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AFNetworking.framework; path = Carthage/Build/iOS/AFNetworking.framework; sourceTree = "<group>"; };
 		F72685E827C78E490019EF5E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCEndToEndInitialize.swift; sourceTree = "<group>"; };
+		F72944F12A84246400246839 /* NCEndToEndMetadataV20.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEndToEndMetadataV20.swift; sourceTree = "<group>"; };
+		F72944F42A8424F800246839 /* NCEndToEndMetadataV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEndToEndMetadataV1.swift; sourceTree = "<group>"; };
 		F72A17D728B221E300F3F159 /* DashboardWidgetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashboardWidgetView.swift; sourceTree = "<group>"; };
 		F72A47EB2487B06B005AD489 /* NCOperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCOperationQueue.swift; sourceTree = "<group>"; };
 		F72CD63925C19EBF00F46F9A /* NCAutoUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCAutoUpload.swift; sourceTree = "<group>"; };
@@ -970,6 +1010,7 @@
 		F749B650297B0F2400087535 /* NCManageDatabase+Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Avatar.swift"; sourceTree = "<group>"; };
 		F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUtilityFileSystem.swift; sourceTree = "<group>"; };
 		F74AFCE822E8B024003DE61F /* FSCalendar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FSCalendar.framework; path = Carthage/Build/iOS/FSCalendar.framework; sourceTree = "<group>"; };
+		F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Chunk.swift"; sourceTree = "<group>"; };
 		F74C0434253F1CDC009762AB /* NCShares.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShares.swift; sourceTree = "<group>"; };
 		F74C0435253F1CDC009762AB /* NCShares.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCShares.storyboard; sourceTree = "<group>"; };
 		F74C4FBA2328C3C100A23E25 /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenSSL.framework; path = Carthage/Build/iOS/OpenSSL.framework; sourceTree = "<group>"; };
@@ -1087,6 +1128,7 @@
 		F783031028B4C86200B84583 /* libc++.1.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.1.tbd"; path = "usr/lib/libc++.1.tbd"; sourceTree = SDKROOT; };
 		F783031128B4C86200B84583 /* libc++abi.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++abi.tbd"; path = "usr/lib/libc++abi.tbd"; sourceTree = SDKROOT; };
 		F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EE.swift; sourceTree = "<group>"; };
+		F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+LocalFile.swift"; sourceTree = "<group>"; };
 		F787704E22E7019900F287A9 /* NCShareLinkCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareLinkCell.xib; sourceTree = "<group>"; };
 		F78A10BE29322E8A008499B8 /* NCManageDatabase+Directory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Directory.swift"; sourceTree = "<group>"; };
 		F78A18B523CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerRichWorkspaceWebView.swift; sourceTree = "<group>"; };
@@ -1100,7 +1142,6 @@
 		F78ACD4921903F850088454D /* NCTrashListCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCTrashListCell.xib; sourceTree = "<group>"; };
 		F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionHeaderMenu.swift; sourceTree = "<group>"; };
 		F78ACD53219047D40088454D /* NCSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionFooter.xib; sourceTree = "<group>"; };
-		F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionHeaderMenu.xib; sourceTree = "<group>"; };
 		F78C6FDD296D677300C952C3 /* NCContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCContextMenu.swift; sourceTree = "<group>"; };
 		F78D6F461F0B7CB9002F9619 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F78D6F4D1F0B7CE4002F9619 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -1121,6 +1162,7 @@
 		F79BCEEA270B49C800B5B71F /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
 		F79EDA9F26B004980007D134 /* NCPlayerToolBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPlayerToolBar.swift; sourceTree = "<group>"; };
 		F79EDAA126B004980007D134 /* NCPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPlayer.swift; sourceTree = "<group>"; };
+		F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EEMarkFolder.swift; sourceTree = "<group>"; };
 		F7A0D1342591FBC5008F8A13 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
 		F7A321AB1E9E6AD50069AD1B /* CCAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAdvanced.h; sourceTree = "<group>"; };
 		F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAdvanced.m; sourceTree = "<group>"; };
@@ -1189,10 +1231,10 @@
 		F7AF7632246BEDFE00B86E3C /* TOPasscodeViewController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TOPasscodeViewController.framework; path = Carthage/Build/iOS/TOPasscodeViewController.framework; sourceTree = "<group>"; };
 		F7B1076C25D3CF2800E72DE2 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = System/Library/Frameworks/BackgroundTasks.framework; sourceTree = SDKROOT; };
 		F7B1A7761EBB3C8000BFB6D1 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
+		F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionHeaderMenu.xib; sourceTree = "<group>"; };
 		F7B6B70327C4E7FA00A7F6EB /* NCScan+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCScan+CollectionView.swift"; sourceTree = "<group>"; };
 		F7B7504A2397D38E004E13EC /* UIImage+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = "<group>"; };
 		F7B8B82F25681C3400967775 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; };
-		F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingChunkedUpload.swift; sourceTree = "<group>"; };
 		F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCDatabase.swift; sourceTree = "<group>"; };
 		F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCManageDatabase.swift; sourceTree = "<group>"; };
 		F7BB04851FD58ACB00BBFD2A /* cs-CZ */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "cs-CZ"; path = "cs-CZ.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -1397,9 +1439,11 @@
 				F710FC80277B7D2700AA9FBF /* RealmSwift in Frameworks */,
 				F72AD70F28C24BA1006CB92D /* NextcloudKit in Frameworks */,
 				F72CD01227A7E92400E59476 /* JGProgressHUD in Frameworks */,
+				F77CB6A92AA08053000C3CA4 /* OpenSSL in Frameworks */,
 				F73ADD2126554F8E0069EA0D /* SwiftEntryKit in Frameworks */,
 				F7EBCDCF277B81FF00A4EF67 /* UICKeyChainStore in Frameworks */,
 				F70821D829E59E6D001CA2D7 /* TagListView in Frameworks */,
+				F7F623B72A5EFA0C0022D3D4 /* Gzip in Frameworks */,
 				F72D7EB7263B1207000B3DFC /* MarkdownKit in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1453,6 +1497,7 @@
 				F70B86752642CE3B00ED5349 /* FirebaseCrashlytics in Frameworks */,
 				F7A1050E29E587AF00FFD92B /* TagListView in Frameworks */,
 				F76DA969277B77EA0082465B /* DropDown in Frameworks */,
+				F7F623B52A5EF4D30022D3D4 /* Gzip in Frameworks */,
 				F75EAED826D2552E00F4320E /* MarqueeLabel in Frameworks */,
 				F710FC7A277B7D0000AA9FBF /* Realm in Frameworks */,
 				F72DA9B425F53E4E00B87DB1 /* SwiftRichString in Frameworks */,
@@ -1541,6 +1586,7 @@
 		C04E2F212A17BB4D001BAD85 /* NextcloudIntegrationTests */ = {
 			isa = PBXGroup;
 			children = (
+				F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */,
 				C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */,
 			);
 			path = NextcloudIntegrationTests;
@@ -1583,6 +1629,27 @@
 			path = Extensions;
 			sourceTree = "<group>";
 		};
+		F33AAF982A601465006ECCBD /* Recovered References */ = {
+			isa = PBXGroup;
+			children = (
+				F7CF16A22A4D7C7A000FF107 /* NCMoreUserCell.xib */,
+			);
+			name = "Recovered References";
+			sourceTree = "<group>";
+		};
+		F3BB46502A39EC2D00461F6E /* Cells */ = {
+			isa = PBXGroup;
+			children = (
+				F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */,
+				F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */,
+				F33AAF992A60394C006ECCBD /* NCMoreUserCell.xib */,
+				F3BB464E2A39EBE500461F6E /* NCMoreUserCell.swift */,
+				F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */,
+				F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */,
+			);
+			path = Cells;
+			sourceTree = "<group>";
+		};
 		F70211F31BAC56E9003FC03E /* Main */ = {
 			isa = PBXGroup;
 			children = (
@@ -1810,7 +1877,6 @@
 				F72CD63925C19EBF00F46F9A /* NCAutoUpload.swift */,
 				F75A9EE523796C6F0044CFCE /* NCNetworking.swift */,
 				F7D96FCB246ED7E100536D73 /* NCNetworkingCheckRemoteUser.swift */,
-				F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */,
 				F70D8D8024A4A9BF000A5756 /* NCNetworkingProcessUpload.swift */,
 				F72A47EB2487B06B005AD489 /* NCOperationQueue.swift */,
 				F755BD9A20594AC7008C5FBB /* NCService.swift */,
@@ -1961,7 +2027,7 @@
 			children = (
 				F78ACD53219047D40088454D /* NCSectionFooter.xib */,
 				F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */,
-				F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */,
+				F7B398412A6A91D5007538D6 /* NCSectionHeaderMenu.xib */,
 				F78ACD51219046DC0088454D /* NCSectionHeaderMenu.swift */,
 			);
 			path = "Section Header Footer";
@@ -2048,7 +2114,6 @@
 				F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */,
 				F7A0D1342591FBC5008F8A13 /* String+Extension.swift */,
 				AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */,
-				AFD3323F276A02C000F5AE02 /* UIApplication+Extension.swift */,
 				AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */,
 				F70CEF5523E9C7E50007035B /* UIColor+Extension.swift */,
 				F79B645F26CA661600838ACA /* UIControl+Extension.swift */,
@@ -2129,6 +2194,7 @@
 				F7C1EEA425053A9C00866ACC /* NCDataSource.swift */,
 				F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */,
 				AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */,
+				F74B6D942A7E239A00F03C5F /* NCManageDatabase+Chunk.swift */,
 				AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */,
 				F749B650297B0F2400087535 /* NCManageDatabase+Avatar.swift */,
 				F763D29C2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift */,
@@ -2137,6 +2203,7 @@
 				F72FD3B4297ED49A00075D28 /* NCManageDatabase+E2EE.swift */,
 				F757CC8129E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift */,
 				F7BF9D812934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift */,
+				F7864ACB2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift */,
 				AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */,
 				F749B649297B0CBB00087535 /* NCManageDatabase+Share.swift */,
 				F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */,
@@ -2172,6 +2239,7 @@
 				F707C26421A2DC5200F6181E /* NCStoreReview.swift */,
 				AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */,
 				F70BFC7320E0FA7C00C67599 /* NCUtility.swift */,
+				F359D8662A7D03420023F405 /* NCUtility+Exif.swift */,
 				AF93474B27E34120002537EE /* NCUtility+Image.swift */,
 				F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */,
 				F702F2FC25EE5D2C008F8E80 /* NYMnemonic */,
@@ -2213,8 +2281,11 @@
 				F70CAE381F8CF31A008125FD /* NCEndToEndEncryption.h */,
 				F70CAE391F8CF31A008125FD /* NCEndToEndEncryption.m */,
 				F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */,
+				F72944F12A84246400246839 /* NCEndToEndMetadataV20.swift */,
+				F72944F42A8424F800246839 /* NCEndToEndMetadataV1.swift */,
 				F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */,
 				F7C30DF9291BCF790017149B /* NCNetworkingE2EECreateFolder.swift */,
+				F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */,
 				F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */,
 				F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */,
 				F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */,
@@ -2265,7 +2336,7 @@
 		F7CB68942541670D0050EC94 /* More */ = {
 			isa = PBXGroup;
 			children = (
-				F7CF16A22A4D7C7A000FF107 /* NCMoreUserCell.xib */,
+				F3BB46502A39EC2D00461F6E /* Cells */,
 				F7CB68992541676B0050EC94 /* NCMore.storyboard */,
 				F73F537E1E929C8500F8678D /* NCMore.swift */,
 			);
@@ -2370,6 +2441,7 @@
 				C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */,
 				C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */,
 				F31F69422A2F6D4500162F76 /* NextcloudSnapshotTests.xctest */,
+				F33AAF982A601465006ECCBD /* Recovered References */,
 			);
 			sourceTree = "<group>";
 		};
@@ -2714,6 +2786,8 @@
 				F72CD01127A7E92400E59476 /* JGProgressHUD */,
 				F72AD70E28C24BA1006CB92D /* NextcloudKit */,
 				F70821D729E59E6D001CA2D7 /* TagListView */,
+				F7F623B62A5EFA0C0022D3D4 /* Gzip */,
+				F77CB6A82AA08053000C3CA4 /* OpenSSL */,
 			);
 			productName = "Share Ext";
 			productReference = F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */;
@@ -2814,6 +2888,7 @@
 				F787AC08298BCB4A0001BB00 /* SVGKitSwift */,
 				F7A1050D29E587AF00FFD92B /* TagListView */,
 				F31F69632A2F929600162F76 /* PreviewSnapshots */,
+				F7F623B42A5EF4D30022D3D4 /* Gzip */,
 			);
 			productName = "Crypto Cloud";
 			productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */;
@@ -2988,6 +3063,7 @@
 				F31F694B2A2F6EFA00162F76 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */,
 				F31F69622A2F929600162F76 /* XCRemoteSwiftPackageReference "swiftui-preview-snapshots" */,
 				F31F69672A2F92F000162F76 /* XCRemoteSwiftPackageReference "SnapshotTestingHEIC" */,
+				F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */,
 			);
 			productRefGroup = F7F67B9F1A24D27800EE80DA;
 			projectDirPath = "";
@@ -3060,11 +3136,11 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				F7148059262ED52200693E51 /* NCSectionHeaderMenu.xib in Resources */,
 				F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */,
 				F7148054262ED51000693E51 /* NCListCell.xib in Resources */,
 				F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */,
 				AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */,
+				F7B398432A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */,
 				F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */,
 				AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */,
 				F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */,
@@ -3102,6 +3178,7 @@
 				F7362A1F220C853A005101B5 /* LaunchScreen.storyboard in Resources */,
 				F77444F622281649000D5EB0 /* NCGridMediaCell.xib in Resources */,
 				F78ACD4421903CF20088454D /* NCListCell.xib in Resources */,
+				F3BB464D2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib in Resources */,
 				F7F4F10727ECDBDB008676F9 /* Inconsolata-Black.ttf in Resources */,
 				F761856D29E98543006EB3B0 /* NCIntroCollectionViewCell.xib in Resources */,
 				F78ACD4621903D010088454D /* NCGridCell.xib in Resources */,
@@ -3115,6 +3192,7 @@
 				F723985C253C95CE00257F49 /* NCViewerRichdocument.storyboard in Resources */,
 				F758B45A212C564000515F55 /* NCScan.storyboard in Resources */,
 				F70D87CF25EE6E58008CBBBD /* NCRenameFile.storyboard in Resources */,
+				F33AAF9A2A60394C006ECCBD /* NCMoreUserCell.xib in Resources */,
 				F765F73225237E3F00391DBE /* NCRecent.storyboard in Resources */,
 				F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */,
 				F702F30225EE5D2C008F8E80 /* english.txt in Resources */,
@@ -3163,7 +3241,6 @@
 				F77B0F611D118A16002130FE /* Acknowledgements.rtf in Resources */,
 				F7EDE509262DA9D600414FE6 /* NCSelectCommandViewSelect.xib in Resources */,
 				F732D23327CF8AED000B0F1B /* NCPlayerToolBar.xib in Resources */,
-				F7CF16A32A4D7C7A000FF107 /* NCMoreUserCell.xib in Resources */,
 				F73D11FA253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard in Resources */,
 				F7EDE51B262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib in Resources */,
 				F73B422B2476764F00A30FD3 /* NCNotification.storyboard in Resources */,
@@ -3176,7 +3253,7 @@
 				F73CB3B222E072A000AD728E /* NCShareHeaderView.xib in Resources */,
 				F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */,
 				F7EDE514262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib in Resources */,
-				F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */,
+				F7B398422A6A91D5007538D6 /* NCSectionHeaderMenu.xib in Resources */,
 				F7501C322212E57500FB1415 /* NCMedia.storyboard in Resources */,
 				F74DE14425135B6800917068 /* NCTransfers.storyboard in Resources */,
 				F77910A525DD517B00CEDB9E /* Settings.bundle in Resources */,
@@ -3251,6 +3328,7 @@
 				F72FD3BA297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
 				F7E98C1927E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				2C1D5D7623E2DE3300334ABB /* NCManageDatabase.swift in Sources */,
+				F7864AD22A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F314F1142A30E2DE00BC7FAB /* View+Extension.swift in Sources */,
 				F7D68FD028CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				F343A4C12A1E734600DDA874 /* Optional+Extension.swift in Sources */,
@@ -3259,6 +3337,7 @@
 				AF4BF617275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */,
 				F7BF9D872934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
 				F749B64F297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
+				F359D86D2A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				D575039F27146F93008DC9DC /* String+Extension.swift in Sources */,
 				F769CA1A2966EA3C00039397 /* ComponentView.swift in Sources */,
 				F757CC8829E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */,
@@ -3274,6 +3353,7 @@
 				F763D2A32A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
 				F749B656297B0F2400087535 /* NCManageDatabase+Avatar.swift in Sources */,
 				F782FDC424E6933900666099 /* NCUtility.swift in Sources */,
+				F74B6D9B2A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -3301,6 +3381,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F3953BD72A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift in Sources */,
 				F30A96322A27AEDD00D7BCFE /* EnvVars.generated.swift in Sources */,
 				C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */,
 			);
@@ -3318,6 +3399,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F74B6D9A2A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 				F7490E8329882C84009DCE94 /* NCManageDatabase+LayoutForView.swift in Sources */,
 				F7490E7629882BF3009DCE94 /* NCUserBaseUrl.swift in Sources */,
 				F7490E8029882C76009DCE94 /* NCManageDatabase+Avatar.swift in Sources */,
@@ -3333,6 +3415,7 @@
 				F7490E8C29882D02009DCE94 /* CCUtility.m in Sources */,
 				F7490E7729882C10009DCE94 /* UIColor+Extension.swift in Sources */,
 				F70716E62987F81500E72C1D /* DocumentActionViewController.swift in Sources */,
+				F359D86C2A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				F7490E8429882C89009DCE94 /* NCManageDatabase+Share.swift in Sources */,
 				F7490E6F29882B67009DCE94 /* UIImage+Extension.swift in Sources */,
 				F7490E7E29882C6E009DCE94 /* NCManageDatabase+Account.swift in Sources */,
@@ -3341,6 +3424,7 @@
 				F7490E6C29882AEA009DCE94 /* String+Extension.swift in Sources */,
 				F7490E6B29882A92009DCE94 /* NCGlobal.swift in Sources */,
 				F7490E7529882BE2009DCE94 /* NCManageDatabase+Directory.swift in Sources */,
+				F7864AD12A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F7490E8729882CA8009DCE94 /* ThreadSafeDictionary.swift in Sources */,
 				F757CC8729E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */,
 				F7490E8229882C80009DCE94 /* NCManageDatabase+E2EE.swift in Sources */,
@@ -3353,6 +3437,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F7864ACF2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */,
 				F7EDE4E5262D7BBE00414FE6 /* NCSectionHeaderMenu.swift in Sources */,
 				F7BF9D852934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
@@ -3362,11 +3447,13 @@
 				AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */,
 				F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */,
 				F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */,
+				F74B6D982A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 				F7707687263A853700A1BA94 /* NCContentPresenter.swift in Sources */,
 				F343A4B62A1E084200DDA874 /* PHAsset+Extension.swift in Sources */,
 				F70460532499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */,
 				F70BFC7520E0FA7D00C67599 /* NCUtility.swift in Sources */,
 				AF22B20C277C6F4D00DAB0CC /* NCShareCell.swift in Sources */,
+				F359D86A2A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				F79B646126CA661600838ACA /* UIControl+Extension.swift in Sources */,
 				F77C973A2953143A00FDDD09 /* NCCameraRoll.swift in Sources */,
@@ -3390,6 +3477,7 @@
 				F702F2D025EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
 				F343A4BE2A1E734600DDA874 /* Optional+Extension.swift in Sources */,
 				F7EDE4DB262D7BA200414FE6 /* NCCellProtocol.swift in Sources */,
+				F72944F62A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */,
 				F7EDE4D1262D7B8400414FE6 /* NCDataSource.swift in Sources */,
 				F71459D21D12E3B700CAFEEC /* CCUtility.m in Sources */,
 				F75A9EE723796C6F0044CFCE /* NCNetworking.swift in Sources */,
@@ -3398,7 +3486,6 @@
 				F72FD3B8297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
 				F7EDE4E0262D7BAF00414FE6 /* NCGridCell.swift in Sources */,
 				F7A76DC8256A71CD00119AB3 /* UIImage+Extension.swift in Sources */,
-				F7B8CD96261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F763D2A02A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
 				F757CC8529E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */,
 				F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
@@ -3406,6 +3493,7 @@
 				F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */,
 				F7C30DF7291BC0D30017149B /* NCNetworkingE2EEUpload.swift in Sources */,
 				F78A10C229322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */,
+				F79FFB272A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */,
 				F7D68FCE28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				AF22B217277D196700DAB0CC /* NCShareExtension+DataSource.swift in Sources */,
 				F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
@@ -3415,6 +3503,7 @@
 				AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */,
 				F7148041262EBE4000693E51 /* NCShareExtension.swift in Sources */,
 				F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */,
+				F72944F32A84246400246839 /* NCEndToEndMetadataV20.swift in Sources */,
 				F7BAADCC1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -3432,6 +3521,7 @@
 				F783030328B4C4DD00B84583 /* ThreadSafeDictionary.swift in Sources */,
 				F77ED59128C9CE9D00E24ED0 /* ToolbarData.swift in Sources */,
 				F78302F728B4C3C900B84583 /* NCManageDatabase.swift in Sources */,
+				F359D8682A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				F7346E1628B0EF5C006CE2D2 /* Widget.swift in Sources */,
 				F78302F828B4C3E100B84583 /* NCManageDatabase+Activity.swift in Sources */,
 				F783030228B4C4B800B84583 /* NCUtility.swift in Sources */,
@@ -3441,6 +3531,7 @@
 				F78302FE28B4C44700B84583 /* NCBrand.swift in Sources */,
 				F749B64B297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
 				F7817CF929801A3500FFBC65 /* Data+Extension.swift in Sources */,
+				F7864ACD2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F343A4B42A1E084100DDA874 /* PHAsset+Extension.swift in Sources */,
 				F793E59F28B764F6005E4B02 /* NCContentPresenter.swift in Sources */,
 				F76DEE9828F808AF0041B1C9 /* LockscreenWidgetProvider.swift in Sources */,
@@ -3452,6 +3543,7 @@
 				F793E59D28B761E7005E4B02 /* NCNetworking.swift in Sources */,
 				F7BF9D832934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
 				F757CC8329E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */,
+				F74B6D962A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 				F749B652297B0F2400087535 /* NCManageDatabase+Avatar.swift in Sources */,
 				F783030628B4C51E00B84583 /* String+Extension.swift in Sources */,
 				F763D29E2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
@@ -3461,7 +3553,6 @@
 				F78302FB28B4C3EE00B84583 /* NCManageDatabase+Video.swift in Sources */,
 				F72EA95228B7BA2A00C88F0C /* DashboardWidgetProvider.swift in Sources */,
 				F343A4BC2A1E734600DDA874 /* Optional+Extension.swift in Sources */,
-				F793E5A228B76580005E4B02 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F783031228B4C8EC00B84583 /* CCUtility.m in Sources */,
 				F72EA95828B7BC4F00C88F0C /* FilesData.swift in Sources */,
 				F793E59E28B763C2005E4B02 /* NCAskAuthorization.swift in Sources */,
@@ -3497,7 +3588,10 @@
 				F343A4B72A1E084300DDA874 /* PHAsset+Extension.swift in Sources */,
 				F7434B3620E23FE000417916 /* NCManageDatabase.swift in Sources */,
 				F798F0E725880609000DAFFD /* UIColor+Extension.swift in Sources */,
+				F74B6D992A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 				F7D68FCF28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
+				F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
+				F7864AD02A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				AF4BF61B27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
 				F70460542499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */,
 				F78A10C329322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */,
@@ -3509,7 +3603,6 @@
 				F749B64E297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
 				AF817EF3274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
 				F771E3F320E239A600AFB62D /* FileProviderData.swift in Sources */,
-				F7B8CD9B261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F7A0D1372591FBC5008F8A13 /* String+Extension.swift in Sources */,
 				F771E3D720E2392D00AFB62D /* FileProviderEnumerator.swift in Sources */,
 				F74AF3A6247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
@@ -3575,6 +3668,7 @@
 				F75A9EE623796C6F0044CFCE /* NCNetworking.swift in Sources */,
 				F758B460212C56A400515F55 /* NCScan.swift in Sources */,
 				F78ACD52219046DC0088454D /* NCSectionHeaderMenu.swift in Sources */,
+				F72944F52A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */,
 				F710D2022405826100A6033D /* NCViewer+Menu.swift in Sources */,
 				F765E9CD295C585800A09ED8 /* NCUploadScanDocument.swift in Sources */,
 				F77A697D250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift in Sources */,
@@ -3594,9 +3688,9 @@
 				F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
 				F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */,
 				F78ACD4A21903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */,
-				F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F757CC8D29E82D0500F31428 /* NCGroupfolders.swift in Sources */,
 				F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */,
+				F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */,
 				AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */,
 				F7682FE023C36B0500983A04 /* NCMainTabBar.swift in Sources */,
 				F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */,
@@ -3606,6 +3700,7 @@
 				F70460522499061800BB98A7 /* NotificationCenter+MainThread.swift in Sources */,
 				F78F74362163781100C2ADAD /* NCTrash.swift in Sources */,
 				AF817EF1274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
+				F39298972A3B12CB00509762 /* BaseNCMoreCell.swift in Sources */,
 				AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */,
 				F7E41316294A19B300839300 /* UIView+Extension.swift in Sources */,
 				F31F69502A2F707E00162F76 /* SwiftUIView+Extensions.swift in Sources */,
@@ -3617,6 +3712,7 @@
 				8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */,
 				F761856C29E98543006EB3B0 /* NCIntroCollectionViewCell.swift in Sources */,
 				F75DD765290ABB25002EB562 /* Intent.intentdefinition in Sources */,
+				F74B6D952A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 				F702F2F725EE5CED008F8E80 /* NCLogin.swift in Sources */,
 				F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */,
@@ -3627,6 +3723,7 @@
 				F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */,
 				F7C30DF6291BC0CA0017149B /* NCNetworkingE2EEUpload.swift in Sources */,
 				F7501C332212E57500FB1415 /* NCMedia.swift in Sources */,
+				F72944F22A84246400246839 /* NCEndToEndMetadataV20.swift in Sources */,
 				F70BFC7420E0FA7D00C67599 /* NCUtility.swift in Sources */,
 				F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */,
 				F7C1EEA525053A9C00866ACC /* NCDataSource.swift in Sources */,
@@ -3641,6 +3738,7 @@
 				F7CA212D25F1333300826ABB /* NCAccountRequest.swift in Sources */,
 				F765F73125237E3F00391DBE /* NCRecent.swift in Sources */,
 				F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */,
+				F359D8672A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				F7581D2425EFDDDF004DC699 /* NCMedia+Menu.swift in Sources */,
 				F738D4902756740100CD1D38 /* NCLoginNavigationController.swift in Sources */,
 				F77B0E981D118A16002130FE /* CCManageAccount.m in Sources */,
@@ -3654,7 +3752,6 @@
 				F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */,
 				F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */,
 				F7E8A391295DC5E0006CB2D0 /* View+Extension.swift in Sources */,
-				AFD33240276A02C100F5AE02 /* UIApplication+Extension.swift in Sources */,
 				F79B869B265E19D40085C0E0 /* NSMutableAttributedString+Extension.swift in Sources */,
 				F7B7504B2397D38F004E13EC /* UIImage+Extension.swift in Sources */,
 				F7EFC0CD256BF8DD00461AAD /* NCUserStatus.swift in Sources */,
@@ -3663,6 +3760,7 @@
 				F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F7020FCE2233D7F700B7297D /* NCCreateFormUploadVoiceNote.swift in Sources */,
 				F7134186259747BA00768D21 /* NCPushNotification.m in Sources */,
+				F3BB464F2A39EBE500461F6E /* NCMoreUserCell.swift in Sources */,
 				F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */,
 				F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
 				F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */,
@@ -3692,6 +3790,7 @@
 				F749B64A297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
 				F7C9555521F0C5470024296E /* NCActivity.swift in Sources */,
 				F7725A60251F33BB00D125E0 /* NCFiles.swift in Sources */,
+				F3BB46522A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift in Sources */,
 				F704B5E52430AA8000632F5F /* NCCreateFormUploadConflict.swift in Sources */,
 				F765608F23BF813600765969 /* NCContentPresenter.swift in Sources */,
 				F343A4BB2A1E734600DDA874 /* Optional+Extension.swift in Sources */,
@@ -3707,8 +3806,10 @@
 				F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
 				F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
 				F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */,
+				F7864ACC2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F7F4F11027ECDC4A008676F9 /* UIDevice+Extension.swift in Sources */,
 				F77B0ED11D118A16002130FE /* Acknowledgements.m in Sources */,
+				F79FFB262A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */,
 				F70D8D8124A4A9BF000A5756 /* NCNetworkingProcessUpload.swift in Sources */,
 				F7D96FCC246ED7E200536D73 /* NCNetworkingCheckRemoteUser.swift in Sources */,
 				F7E4D9C422ED929B003675FD /* NCShareCommentsCell.swift in Sources */,
@@ -3735,7 +3836,9 @@
 				F75DD767290ABB25002EB562 /* Intent.intentdefinition in Sources */,
 				F749B64C297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
 				F7A8D73F28F181EF008BBE1C /* NCGlobal.swift in Sources */,
+				F74B6D972A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */,
 				F749B653297B0F2400087535 /* NCManageDatabase+Avatar.swift in Sources */,
+				F359D8692A7D03420023F405 /* NCUtility+Exif.swift in Sources */,
 				F763D29F2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
 				F7A8D74328F1826F008BBE1C /* String+Extension.swift in Sources */,
 				F7A8D73728F17E1E008BBE1C /* NCManageDatabase+Account.swift in Sources */,
@@ -3750,6 +3853,7 @@
 				F72FD3B7297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
 				F7A8D74128F18254008BBE1C /* UIColor+Extension.swift in Sources */,
 				F7A8D73428F17E12008BBE1C /* NCDatabase.swift in Sources */,
+				F7864ACE2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F7A8D74028F18212008BBE1C /* UIImage+Extension.swift in Sources */,
 				F757CC8429E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */,
 				F78E2D6729AF02DB0024D4F3 /* Database.swift in Sources */,
@@ -4566,7 +4670,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 4;
+				CURRENT_PROJECT_VERSION = 16;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -4592,7 +4696,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.8.6;
+				MARKETING_VERSION = 4.9.0;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
@@ -4631,7 +4735,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 4;
+				CURRENT_PROJECT_VERSION = 16;
 				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
@@ -4654,7 +4758,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.8.6;
+				MARKETING_VERSION = 4.9.0;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
@@ -4938,7 +5042,7 @@
 			repositoryURL = "https://github.com/nextcloud/NextcloudKit";
 			requirement = {
 				kind = exactVersion;
-				version = 2.7.0;
+				version = 2.8.0;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {
@@ -4981,6 +5085,14 @@
 				minimumVersion = 10.1.1;
 			};
 		};
+		F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/1024jp/GzipSwift";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 6.0.0;
+			};
+		};
 /* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
@@ -5259,6 +5371,11 @@
 			package = F77BC3E9293E5268005F2B08 /* XCRemoteSwiftPackageReference "swifter" */;
 			productName = Swifter;
 		};
+		F77CB6A82AA08053000C3CA4 /* OpenSSL */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F77333862927A72100466E35 /* XCRemoteSwiftPackageReference "OpenSSL" */;
+			productName = OpenSSL;
+		};
 		F783030C28B4C59A00B84583 /* SwiftEntryKit */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = F73ADD1A265546880069EA0D /* XCRemoteSwiftPackageReference "SwiftEntryKit" */;
@@ -5334,6 +5451,16 @@
 			package = F7ED547A25EEA65400956C55 /* XCRemoteSwiftPackageReference "QRCodeReader" */;
 			productName = QRCodeReader;
 		};
+		F7F623B42A5EF4D30022D3D4 /* Gzip */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */;
+			productName = Gzip;
+		};
+		F7F623B62A5EFA0C0022D3D4 /* Gzip */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */;
+			productName = Gzip;
+		};
 /* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = F7F67BA01A24D27800EE80DA /* Project object */;

+ 0 - 1
Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme

@@ -96,7 +96,6 @@
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      enableThreadSanitizer = "YES"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       ignoresPersistentStateOnLaunch = "NO"

+ 2 - 2
Notification Service Extension/NotificationService.swift

@@ -46,7 +46,7 @@ class NotificationService: UNNotificationServiceExtension {
                         if var json = try JSONSerialization.jsonObject(with: data) as? [String: AnyObject],
                            let subject = json["subject"] as? String {
                             bestAttemptContent.body = subject
-                            if let pref = UserDefaults.init(suiteName: NCBrandOptions.shared.capabilitiesGroups) {
+                            if let pref = UserDefaults(suiteName: NCBrandOptions.shared.capabilitiesGroups) {
                                 json["account"] = tableAccount.account as AnyObject
                                 pref.set(json, forKey: "NOTIFICATION_DATA")
                                 pref.synchronize()
@@ -65,7 +65,7 @@ class NotificationService: UNNotificationServiceExtension {
     override func serviceExtensionTimeWillExpire() {
         // Called just before the extension will be terminated by the system.
         // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
-        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
+        if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
             bestAttemptContent.title = ""
             bestAttemptContent.body = "Nextcloud notification"
             contentHandler(bestAttemptContent)

+ 1 - 1
README.md

@@ -38,7 +38,7 @@ branch. Maybe start working on [starter issues](https://github.com/nextcloud/ios
 
 Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/ios/pulls)
 
-### Xcode 14.2 Project Setup
+### Xcode 15 Project Setup
 
 #### Dependencies
 

+ 1 - 0
Share/NCShareExtension+DataSource.swift

@@ -65,6 +65,7 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.delegate = self
 
         cell.fileObjectId = metadata.ocId
+        cell.indexPath = indexPath
         cell.fileUser = metadata.ownerId
         cell.labelTitle.text = metadata.fileNameView
         cell.labelTitle.textColor = .label

+ 2 - 1
Share/NCShareExtension+NCDelegate.swift

@@ -90,7 +90,7 @@ extension NCShareExtension: NCEmptyDataSetDelegate, NCAccountRequestDelegate {
             userId: activeAccount.userId,
             password: CCUtility.getPassword(activeAccount.account),
             urlBase: activeAccount.urlBase,
-            userAgent: CCUtility.getUserAgent(),
+            userAgent: userAgent,
             nextcloudVersion: 0,
             delegate: NCNetworking.shared)
 
@@ -127,6 +127,7 @@ extension NCShareExtension: NCShareCellDelegate, NCRenameFileDelegate, NCListCel
         let resultInternalType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false)
         vcRename.delegate = self
         vcRename.fileName = fileName
+        vcRename.indexPath = IndexPath()
         if let previewImage = UIImage.downsample(imageAt: URL(fileURLWithPath: NSTemporaryDirectory() + fileName), to: CGSize(width: 140, height: 140)) {
             vcRename.imagePreview = previewImage
         } else {

+ 12 - 4
Share/NCShareExtension.swift

@@ -65,7 +65,6 @@ class NCShareExtension: UIViewController {
     var autoUploadDirectory = ""
     let refreshControl = UIRefreshControl()
     var activeAccount: tableAccount!
-    let chunckSize = CCUtility.getChunkSize() * 1000000
     var progress: CGFloat = 0
     var counterUploaded: Int = 0
     var uploadErrors: [tableMetadata] = []
@@ -337,8 +336,18 @@ extension NCShareExtension {
         metadata.contentType = results.mimeType
         metadata.iconName = results.iconName
         metadata.classFile = results.classFile
-        // CHUNCK
-        metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
+        // CHUNK
+        var chunkSize = NCGlobal.shared.chunkSizeMBCellular
+        if NCNetworking.shared.networkReachability == NKCommon.TypeReachability.reachableEthernetOrWiFi {
+            chunkSize = NCGlobal.shared.chunkSizeMBEthernetOrWiFi
+        }
+        if metadata.size > chunkSize {
+            metadata.chunk = chunkSize
+        } else {
+            metadata.chunk = 0
+        }
+        // E2EE
+        metadata.e2eEncrypted = metadata.isDirectoryE2EE
 
         hud.textLabel.text = NSLocalizedString("_upload_file_", comment: "") + " \(counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(filesName.count)"
         hud.show(in: self.view)
@@ -351,7 +360,6 @@ extension NCShareExtension {
             if error != .success {
                 let path = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)!
                 NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId)
                 NCUtilityFileSystem.shared.deleteFile(filePath: path)
                 self.uploadErrors.append(metadata)
             }

+ 19 - 15
iOSClient/Extensions/UIApplication+Extension.swift → Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift

@@ -1,12 +1,11 @@
 //
-//  UIApplication+Extension.swift
-//  Nextcloud
-//
-//  Created by Henrik Storch on 15.12.2021.
-//  Copyright (c) 2021 Henrik Storch. All rights reserved.
-//
-//  Author Henrik Storch <henrik.storch@nextcloud.com>
+//  BaseIntegrationXCTestCase.swift
+//  
+// 
+//  Created by Milen Pivchev on 20.06.23.
+//  Copyright © 2023 Milen Pivchev. All rights reserved.
 //
+//  Author: Milen Pivchev <milen.pivchev@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
@@ -20,15 +19,20 @@
 //  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
 
-extension UIApplication {
-    // indicates if current device is in landscape orientation
-    var isLandscape: Bool {
-        if UIDevice.current.orientation.isValidInterfaceOrientation {
-            return UIDevice.current.orientation.isLandscape
-        } else {
-            return windows.first?.windowScene?.interfaceOrientation.isLandscape ?? false
+import XCTest
+@testable import NextcloudKit
+
+class BaseIntegrationXCTestCase: XCTestCase {
+    internal let baseUrl = EnvVars.testServerUrl
+    internal let user = EnvVars.testUser
+    internal let userId = EnvVars.testUser
+    internal let password = EnvVars.testAppPassword
+    internal lazy var account = "\(userId) \(baseUrl)"
+
+    internal var randomInt: Int {
+        get {
+            return Int.random(in: 1000...Int.max)
         }
     }
 }

+ 2 - 8
Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift

@@ -10,13 +10,7 @@ import XCTest
 import NextcloudKit
 @testable import Nextcloud
 
-final class FilesIntegrationTests: XCTestCase {
-    private let baseUrl = EnvVars.testServerUrl
-    private let user = EnvVars.testUser
-    private let userId = EnvVars.testUser
-    private let password = EnvVars.testAppPassword
-    private lazy var account = "\(userId) \(baseUrl)"
-
+final class FilesIntegrationTests: BaseIntegrationXCTestCase {
     private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
 
     override func setUp() {
@@ -26,7 +20,7 @@ final class FilesIntegrationTests: XCTestCase {
     func test_createReadDeleteFolder_withProperParams_shouldCreateReadDeleteFolder() throws {
         let expectation = expectation(description: "Should finish last callback")
 
-        let folderName = "TestFolder10"
+        let folderName = "TestFolder\(randomInt)"
         let serverUrl = "\(baseUrl)/remote.php/dav/files/\(userId)"
         let serverUrlFileName = "\(serverUrl)/\(folderName)"
 

+ 1 - 1
Tests/NextcloudUITests/LoginUITests.swift

@@ -47,7 +47,7 @@ final class LoginUITests: BaseUIXCTestCase {
         usernameTextField.typeText(user)
         let passwordTextField = element.children(matching: .other).element(boundBy: 4).children(matching: .secureTextField).element
         passwordTextField.tap()
-        passwordTextField.typeText(user)
+        passwordTextField.typeText(password)
         let loginButton3 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements[\"Login – Nextcloud\"]",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
         XCTAssert(loginButton3.waitForExistence(timeout: timeoutSeconds))
         loginButton3.tap()

+ 7 - 13
Widget/Dashboard/DashboardData.swift

@@ -51,12 +51,6 @@ struct DashboardData: Identifiable, Hashable {
     let imageColor: UIColor?
 }
 
-struct DashboardDataButton: Hashable {
-    let type: String
-    let Text: String
-    let link: String
-}
-
 let dashboardDatasTest: [DashboardData] = [
     .init(id: 0, title: "title0", subTitle: "subTitle-description0", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false, imageColor: nil),
     .init(id: 1, title: "title1", subTitle: "subTitle-description1", link: URL(string: "https://nextcloud.com/")!, icon: UIImage(named: "widget")!, template: true, avatar: false, imageColor: nil),
@@ -71,7 +65,7 @@ let dashboardDatasTest: [DashboardData] = [
 ]
 
 func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
-    
+
     if withButton {
         let height = Int((displaySize.height - 85) / 50)
         return height
@@ -81,7 +75,7 @@ func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
     }
 }
 
-func convertDataToImage(data: Data?, size:CGSize, fileNameToWrite: String?) -> UIImage? {
+func convertDataToImage(data: Data?, size: CGSize, fileNameToWrite: String?) -> UIImage? {
 
     guard let data = data else { return nil }
     var imageData: UIImage?
@@ -134,7 +128,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
     guard NCGlobal.shared.capabilityServerVersionMajor >= NCGlobal.shared.nextcloudVersion25 else {
         return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, isEmpty: false, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "xmark.icloud", footerText: NSLocalizedString("_widget_available_nc25_", comment: "")))
     }
-        
+
     // NETWORKING
     let password = CCUtility.getPassword(account.account)!
     NextcloudKit.shared.setup(
@@ -143,7 +137,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
         userId: account.userId,
         password: password,
         urlBase: account.urlBase,
-        userAgent: CCUtility.getUserAgent(),
+        userAgent: userAgent,
         nextcloudVersion: 0,
         delegate: NCNetworking.shared)
 
@@ -161,7 +155,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
     } else {
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) dashboard widget session with level \(levelLog) " + versionNextcloudiOS)
     }
-    
+
     let (tableDashboard, tableButton) = NCManageDatabase.shared.getDashboardWidget(account: account.account, id: id)
     let existsButton = (tableButton?.isEmpty ?? true) ? false : true
     let title = tableDashboard?.title ?? id
@@ -215,7 +209,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
                                     if let item = CCUtility.value(forKey: "fileId", fromQueryItems: queryItems) {
                                         iconFileName = item
                                     } else if pathComponents.contains("avatar") {
-                                        iconFileName = pathComponents[pathComponents.count-2]
+                                        iconFileName = pathComponents[pathComponents.count - 2]
                                         imageAvatar = true
                                     } else if pathComponents.contains("getCalendarDotSvg") {
                                         imageColorized = true
@@ -257,7 +251,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
             }
 
             let alias = (account.alias.isEmpty) ? "" : (" (" + account.alias + ")")
-            let footerText = "Dashboard " + NSLocalizedString("_of_", comment: "") +  " " + account.displayName + alias
+            let footerText = "Dashboard " + NSLocalizedString("_of_", comment: "") + " " + account.displayName + alias
 
             if error != .success {
                 completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: tableDashboard, buttons: buttons, isPlaceholder: true, isEmpty: false, titleImage: titleImage, title: title, footerImage: "xmark.icloud", footerText: error.errorDescription))

+ 8 - 7
Widget/Dashboard/DashboardWidgetView.swift

@@ -27,9 +27,9 @@ import WidgetKit
 struct DashboardWidgetView: View {
 
     var entry: DashboardDataEntry
-    
+
     var body: some View {
-        
+
         GeometryReader { geo in
 
             if entry.isEmpty {
@@ -49,14 +49,14 @@ struct DashboardWidgetView: View {
 
             ZStack(alignment: .topLeading) {
 
-                HStack() {
+                HStack {
 
                     Image(uiImage: entry.titleImage)
                         .renderingMode(.template)
                         .resizable()
                         .scaledToFill()
                         .frame(width: 20, height: 20)
-                    
+
                     Text(entry.title)
                         .font(.system(size: 15))
                         .fontWeight(.bold)
@@ -160,8 +160,8 @@ struct DashboardWidgetView: View {
                         let brandTextColor = Color(NCBrandColor.shared.brandText)
 
                         ForEach(buttons, id: \.index) { element in
-                            Link(destination: URL(string: element.link)! , label: {
-                                
+                            Link(destination: URL(string: element.link)!, label: {
+
                                 Text(element.text)
                                     .font(.system(size: 15))
                                     .padding(7)
@@ -174,7 +174,7 @@ struct DashboardWidgetView: View {
                     }
                     .frame(width: geo.size.width - 10, height: geo.size.height - 25, alignment: .bottomTrailing)
                 }
-                
+
                 HStack {
 
                     Image(systemName: entry.footerImage)
@@ -192,6 +192,7 @@ struct DashboardWidgetView: View {
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
             }
         }
+        .widgetBackground(Color(UIColor.systemBackground))
     }
 }
 

+ 5 - 5
Widget/Files/FilesData.swift

@@ -79,7 +79,7 @@ func getTitleFilesWidget(account: tableAccount?) -> String {
 }
 
 func getFilesItems(displaySize: CGSize) -> Int {
-    
+
     let height = Int((displaySize.height - 100) / 50)
     return height
 }
@@ -131,7 +131,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
         userId: account.userId,
         password: password,
         urlBase: account.urlBase,
-        userAgent: CCUtility.getUserAgent(),
+        userAgent: userAgent,
         nextcloudVersion: 0,
         delegate: NCNetworking.shared)
 
@@ -205,7 +205,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
     } else {
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) widget session with level \(levelLog) " + versionNextcloudiOS)
     }
-    
+
     let options = NKRequestOptions(timeout: 90, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
     NextcloudKit.shared.searchBodyRequest(serverUrl: account.urlBase, requestBody: requestBody, showHiddenFiles: CCUtility.getShowHiddenFiles(), options: options) { _, files, data, error in
         Task {
@@ -249,13 +249,13 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
 
                 // DATA
-                let data = FilesData.init(id: metadata.ocId, image: imageRecent, title: metadata.fileNameView, subTitle: subTitle, url: url)
+                let data = FilesData(id: metadata.ocId, image: imageRecent, title: metadata.fileNameView, subTitle: subTitle, url: url)
                 datas.append(data)
                 if datas.count == filesItems { break}
             }
 
             let alias = (account.alias.isEmpty) ? "" : (" (" + account.alias + ")")
-            let footerText = "Files " + NSLocalizedString("_of_", comment: "") +  " " + account.displayName + alias
+            let footerText = "Files " + NSLocalizedString("_of_", comment: "") + " " + account.displayName + alias
 
             if error != .success {
                 completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: account.userId, url: account.urlBase, tile: title, footerImage: "xmark.icloud", footerText: error.errorDescription))

+ 6 - 5
Widget/Files/FilesWidgetView.swift

@@ -25,7 +25,7 @@ import SwiftUI
 import WidgetKit
 
 struct FilesWidgetView: View {
-    
+
     var entry: FilesDataEntry
 
     var body: some View {
@@ -55,9 +55,9 @@ struct FilesWidgetView: View {
             }
 
             ZStack(alignment: .topLeading) {
-                
-                HStack() {
-                    
+
+                HStack {
+
                     Text(entry.tile)
                         .font(.system(size: 12))
                         .fontWeight(.bold)
@@ -172,7 +172,7 @@ struct FilesWidgetView: View {
                         .scaledToFit()
                         .frame(width: 15, height: 15)
                         .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
-                
+
                     Text(entry.footerText)
                         .font(.caption2)
                         .lineLimit(1)
@@ -182,6 +182,7 @@ struct FilesWidgetView: View {
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
             }
         }
+        .widgetBackground(Color(UIColor.systemBackground))
     }
 }
 

+ 1 - 1
Widget/Lockscreen/LockscreenData.swift

@@ -70,7 +70,7 @@ func getLockscreenDataEntry(configuration: AccountIntent?, isPreview: Bool, fami
         userId: account.userId,
         password: password,
         urlBase: account.urlBase,
-        userAgent: CCUtility.getUserAgent(),
+        userAgent: userAgent,
         nextcloudVersion: 0,
         delegate: NCNetworking.shared)
 

+ 3 - 0
Widget/Lockscreen/LockscreenWidgetView.swift

@@ -43,6 +43,7 @@ struct LockscreenWidgetView: View {
                     }
                 )
                 .gaugeStyle(.accessoryCircularCapacity)
+                .widgetBackground(Color.clear)
             } else {
                 Gauge(
                     value: entry.quotaRelative,
@@ -56,6 +57,7 @@ struct LockscreenWidgetView: View {
                 )
                 .gaugeStyle(.accessoryCircular)
                 .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+                .widgetBackground(Color.clear)
             }
         case .accessoryRectangular:
             VStack(alignment: .leading, spacing: 1) {
@@ -84,6 +86,7 @@ struct LockscreenWidgetView: View {
             }
             .widgetURL(entry.link)
             .redacted(reason: entry.isPlaceholder ? .placeholder : [])
+            .widgetBackground(Color.clear)
         default:
             Text("Not implemented")
         }

+ 1 - 3
Widget/Toolbar/ToolbarWidgetView.swift

@@ -41,9 +41,6 @@ struct ToolbarWidgetView: View {
 
             ZStack(alignment: .topLeading) {
 
-                Color(.black).opacity(0.9)
-                    .ignoresSafeArea()
-
                 HStack(spacing: 0) {
 
                     let sizeButton: CGFloat = 65
@@ -114,6 +111,7 @@ struct ToolbarWidgetView: View {
                 .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
             }
         }
+        .widgetBackground(Color.black.opacity(0.9))
     }
 }
 

+ 16 - 0
Widget/Widget.swift

@@ -47,6 +47,7 @@ struct DashboardWidget: Widget {
         .supportedFamilies([.systemLarge])
         .configurationDisplayName("Dashboard")
         .description(NSLocalizedString("_description_dashboardwidget_", comment: ""))
+        .contentMarginsDisabled()
     }
 }
 
@@ -60,6 +61,7 @@ struct FilesWidget: Widget {
         .supportedFamilies([.systemLarge])
         .configurationDisplayName("Files")
         .description(NSLocalizedString("_description_fileswidget_", comment: ""))
+        .contentMarginsDisabled()
     }
 }
 
@@ -73,6 +75,7 @@ struct ToolbarWidget: Widget {
         .supportedFamilies([.systemMedium])
         .configurationDisplayName("Toolbar")
         .description(NSLocalizedString("_description_toolbarwidget_", comment: ""))
+        .contentMarginsDisabled()
     }
 }
 
@@ -87,8 +90,21 @@ struct LockscreenWidget: Widget {
             .supportedFamilies([.accessoryRectangular, .accessoryCircular])
             .configurationDisplayName(NSLocalizedString("_title_lockscreenwidget_", comment: ""))
             .description(NSLocalizedString("_description_lockscreenwidget_", comment: ""))
+            .contentMarginsDisabled()
         } else {
             return EmptyWidgetConfiguration()
         }
     }
 }
+
+extension View {
+    func widgetBackground(_ backgroundView: some View) -> some View {
+        if #available(iOSApplicationExtension 17.0, *) {
+            return containerBackground(for: .widget) {
+                backgroundView
+            }
+        } else {
+            return background(backgroundView)
+        }
+    }
+}

+ 16 - 23
iOSClient/Activity/NCActivity.swift

@@ -39,7 +39,9 @@ class NCActivity: UIViewController, NCSharePagingContent {
     var metadata: tableMetadata?
     var showComments: Bool = false
 
+    // swiftlint:disable force_cast
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
 
     var allItems: [DateCompareable] = []
     var sectionDates: [Date] = []
@@ -101,13 +103,11 @@ class NCActivity: UIViewController, NCSharePagingContent {
 
         navigationController?.setFileAppreance()
 
-        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
-        initialize()
+        fetchAll(isInitial: true)
     }
 
     override func viewWillDisappear(_ animated: Bool) {
         super.viewWillDisappear(animated)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
     }
 
     override func viewWillLayoutSubviews() {
@@ -119,14 +119,6 @@ class NCActivity: UIViewController, NCSharePagingContent {
         viewContainerConstraint.constant = height
     }
 
-    // MARK: - NotificationCenter
-
-    @objc func initialize() {
-        loadDataSource()
-        fetchAll(isInitial: true)
-        view.setNeedsLayout()
-    }
-
     func makeTableFooterView() -> UIView {
         let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 100))
         view.backgroundColor = .clear
@@ -172,7 +164,7 @@ extension NCActivity: UITableViewDelegate {
         label.textAlignment = .center
         label.layer.cornerRadius = 11
         label.layer.masksToBounds = true
-        label.layer.backgroundColor = UIColor(red: 152.0/255.0, green: 167.0/255.0, blue: 181.0/255.0, alpha: 0.8).cgColor
+        label.layer.backgroundColor = UIColor(red: 152.0 / 255.0, green: 167.0 / 255.0, blue: 181.0 / 255.0, alpha: 0.8).cgColor
         let widthFrame = label.intrinsicContentSize.width + 30
         let xFrame = tableView.bounds.width / 2 - widthFrame / 2
         label.frame = CGRect(x: xFrame, y: 10, width: widthFrame, height: 22)
@@ -213,6 +205,7 @@ extension NCActivity: UITableViewDataSource {
             return UITableViewCell()
         }
 
+        cell.indexPath = indexPath
         cell.tableComments = comment
         cell.delegate = self
         cell.sizeToFit()
@@ -247,7 +240,7 @@ extension NCActivity: UITableViewDataSource {
         var orderKeysId: [String] = []
 
         cell.idActivity = activity.idActivity
-
+        cell.indexPath = indexPath
         cell.avatar.image = nil
         cell.avatar.isHidden = true
         cell.subjectTrailingConstraint.constant = 10
@@ -256,7 +249,7 @@ extension NCActivity: UITableViewDataSource {
         cell.viewController = self
 
         // icon
-        if activity.icon.count > 0 {
+        if !activity.icon.isEmpty {
 
             let fileNameIcon = (activity.icon as NSString).lastPathComponent
             let fileNameLocalPath = CCUtility.getDirectoryUserData() + "/" + fileNameIcon
@@ -278,7 +271,7 @@ extension NCActivity: UITableViewDataSource {
         }
 
         // avatar
-        if activity.user.count > 0 && activity.user != appDelegate.userId {
+        if !activity.user.isEmpty && activity.user != appDelegate.userId {
 
             cell.subjectTrailingConstraint.constant = 50
             cell.avatar.isHidden = false
@@ -291,7 +284,7 @@ extension NCActivity: UITableViewDataSource {
 
         // subject
         cell.subject.text = activity.subject
-        if activity.subjectRich.count > 0 {
+        if !activity.subjectRich.isEmpty {
 
             var subject = activity.subjectRich
             var keys: [String] = []
@@ -325,7 +318,7 @@ extension NCActivity: UITableViewDataSource {
 
         // CollectionView
         cell.activityPreviews = NCManageDatabase.shared.getActivityPreview(account: activity.account, idActivity: activity.idActivity, orderKeysId: orderKeysId)
-        if cell.activityPreviews.count == 0 {
+        if cell.activityPreviews.isEmpty {
             cell.collectionViewHeightConstraint.constant = 0
         } else {
             cell.collectionViewHeightConstraint.constant = 60
@@ -358,9 +351,9 @@ extension NCActivity {
 
         var bottom: CGFloat = 0
         if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
-            bottom = -mainTabBar.getHight()
+            bottom = -mainTabBar.getHeight()
         }
-        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .medium)
+        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom - 5, style: .medium)
 
         let dispatchGroup = DispatchGroup()
         loadComments(disptachGroup: dispatchGroup)
@@ -407,7 +400,7 @@ extension NCActivity {
         guard showComments, let metadata = metadata else { return }
         disptachGroup?.enter()
 
-        NextcloudKit.shared.getComments(fileId: metadata.fileId) { account, comments, data, error in
+        NextcloudKit.shared.getComments(fileId: metadata.fileId) { _, comments, _, error in
             if error == .success, let comments = comments {
                 NCManageDatabase.shared.addComments(comments, account: metadata.account, objectId: metadata.fileId)
             } else if error.errorCode != NCGlobal.shared.errorResourceNotFound {
@@ -436,7 +429,7 @@ extension NCActivity {
             limit: 1,
             objectId: nil,
             objectType: objectType,
-            previews: true) { account, _, activityFirstKnown, activityLastGiven, data, error in
+            previews: true) { account, _, activityFirstKnown, activityLastGiven, _, error in
                 defer { disptachGroup.leave() }
 
                 let largestActivityId = max(activityFirstKnown, activityLastGiven)
@@ -463,7 +456,7 @@ extension NCActivity {
             limit: min(limit, 200),
             objectId: metadata?.fileId,
             objectType: objectType,
-            previews: true) { account, activities, activityFirstKnown, activityLastGiven, data, error in
+            previews: true) { account, activities, activityFirstKnown, activityLastGiven, _, error in
                 defer { disptachGroup.leave() }
                 guard error == .success,
                       account == self.appDelegate.account,
@@ -516,7 +509,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
                     })
 
                     alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
-                        guard let message = alert.textFields?.first?.text, message != "" else { return }
+                        guard let message = alert.textFields?.first?.text, !message.isEmpty else { return }
 
                         NextcloudKit.shared.updateComments(fileId: metadata.fileId, messageId: tableComments.messageId, message: message) { _, error in
                             if error == .success {

+ 14 - 5
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -31,6 +31,7 @@ class NCActivityCollectionViewCell: UICollectionViewCell {
     @IBOutlet weak var imageView: UIImageView!
 
     var fileId = ""
+    var indexPath = IndexPath()
 
     override func awakeFromNib() {
         super.awakeFromNib()
@@ -39,7 +40,9 @@ class NCActivityCollectionViewCell: UICollectionViewCell {
 
 class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
 
+    // swiftlint:disable force_cast
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
 
     @IBOutlet weak var collectionView: UICollectionView!
     @IBOutlet weak var icon: UIImageView!
@@ -49,14 +52,19 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
     @IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
 
     private var user: String = ""
+    private var index = IndexPath()
 
     var idActivity: Int = 0
     var activityPreviews: [tableActivityPreview] = []
     var didSelectItemEnable: Bool = true
     var viewController = UIViewController()
 
+    var indexPath: IndexPath {
+        get { return index }
+        set { index = newValue }
+    }
     var fileAvatarImageView: UIImageView? {
-        get { return avatar }
+        return avatar
     }
     var fileUser: String? {
         get { return user }
@@ -89,8 +97,6 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
             return
         }
 
-        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as? NCActivityCollectionViewCell
-
         let activityPreview = activityPreviews[indexPath.row]
 
         if activityPreview.view == "trashbin" {
@@ -142,9 +148,12 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 
-        let cell: NCActivityCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! NCActivityCollectionViewCell
+        guard let cell: NCActivityCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as? NCActivityCollectionViewCell else {
+            return UICollectionViewCell()
+        }
 
         cell.imageView.image = nil
+        cell.indexPath = indexPath
 
         let activityPreview = activityPreviews[indexPath.row]
         let fileId = String(activityPreview.fileId)
@@ -181,7 +190,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                 if let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: idActivity, id: fileId) {
 
                     let fileNamePath = CCUtility.getDirectoryUserData() + "/" + activitySubjectRich.name
-                    
+
                     if FileManager.default.fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
                         cell.imageView.image = image
                     } else {

+ 161 - 204
iOSClient/AppDelegate.swift

@@ -47,22 +47,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     @objc var activeViewController: UIViewController?
     var mainTabBar: NCMainTabBar?
     var activeMetadata: tableMetadata?
-
-    let listFilesVC = ThreadSafeDictionary<String,NCFiles>()
-    let listFavoriteVC = ThreadSafeDictionary<String,NCFavorite>()
-    let listOfflineVC = ThreadSafeDictionary<String,NCOffline>()
-    let listGroupfoldersVC = ThreadSafeDictionary<String,NCGroupfolders>()
+    let listFilesVC = ThreadSafeDictionary<String, NCFiles>()
 
     var disableSharesView: Bool = false
     var documentPickerViewController: NCDocumentPickerViewController?
     var timerErrorNetworking: Timer?
 
+    var isAppRefresh: Bool = false
+    var isAppProcessing: Bool = false
+
     private var privacyProtectionWindow: UIWindow?
 
     var isUiTestingEnabled: Bool {
-         get {
-             return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
-         }
+        return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
      }
 
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
@@ -72,12 +69,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0)
 
-        let userAgent = CCUtility.getUserAgent() as String
         let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp())
 
-        // Register initialize
-        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
-
         UserDefaults.standard.register(defaults: ["UserAgent": userAgent])
         if !CCUtility.getDisableCrashservice() && !NCBrandOptions.shared.disable_crash_service {
             FirebaseApp.configure()
@@ -86,12 +79,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         CCUtility.createDirectoryStandard()
         CCUtility.emptyTemporaryDirectory()
 
+        // Activated singleton
+        _ = NCActionCenter.shared
+        _ = NCNetworking.shared
+
         NextcloudKit.shared.setup(delegate: NCNetworking.shared)
         NextcloudKit.shared.setup(userAgent: userAgent)
 
         startTimerErrorNetworking()
 
-        // LOG
         var levelLog = 0
         if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
             NextcloudKit.shared.nkCommonInstance.pathLog = pathDirectoryGroup
@@ -107,10 +103,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             levelLog = CCUtility.getLogLevel()
             NextcloudKit.shared.nkCommonInstance.levelLog = levelLog
             NextcloudKit.shared.nkCommonInstance.copyLogToDocumentDirectory = true
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(levelLog) " + versionNextcloudiOS + " in state \(UIApplication.shared.applicationState.rawValue) where (0 active, 1 inactive, 2 background).")
+            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(levelLog) " + versionNextcloudiOS)
         }
 
-        // LOG Account
         if let account = NCManageDatabase.shared.getActiveAccount() {
             NextcloudKit.shared.nkCommonInstance.writeLog("Account active \(account.account)")
             if CCUtility.getPassword(account.account).isEmpty {
@@ -118,12 +113,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             }
         }
 
-        // Activate user account
         if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
 
-            settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account), initialize: false)
+            account = activeAccount.account
+            urlBase = activeAccount.urlBase
+            user = activeAccount.user
+            userId = activeAccount.userId
+            password = CCUtility.getPassword(account)
+
+            NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
+            NCManageDatabase.shared.setCapabilities(account: account)
+
             NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
-            initialize()
 
         } else {
 
@@ -131,10 +132,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             if let bundleID = Bundle.main.bundleIdentifier {
                 UserDefaults.standard.removePersistentDomain(forName: bundleID)
             }
+
             NCBrandColor.shared.createImagesThemingColor()
         }
 
-        // Create user color
         NCBrandColor.shared.createUserColors()
 
         // Push Notification & display notification
@@ -142,16 +143,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         UNUserNotificationCenter.current().delegate = self
         UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
 
-        // Store review
         if !NCUtility.shared.isSimulatorOrTestFlight() {
             let review = NCStoreReview()
             review.incrementAppRuns()
             review.showStoreReview()
         }
 
-        // Background task: register
+        // Background task register
         BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in
-            self.handleRefreshTask(task)
+            self.handleAppRefresh(task)
         }
         BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in
             self.handleProcessingTask(task)
@@ -166,14 +166,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         } else {
             if !CCUtility.getIntro() {
                 if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() {
-                    let navigationController = NCLoginNavigationController.init(rootViewController: viewController)
+                    let navigationController = NCLoginNavigationController(rootViewController: viewController)
                     window?.rootViewController = navigationController
                     window?.makeKeyAndVisible()
                 }
             }
         }
 
-        // Passcode
         self.presentPasscode {
             self.enableTouchFaceID()
         }
@@ -190,7 +189,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         NCSettingsBundleHelper.setVersionAndBuildNumber()
         NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0.5)
-        
+
         // START OBSERVE/TIMER UPLOAD PROCESS
         NCNetworkingProcessUpload.shared.observeTableMetadata()
         NCNetworkingProcessUpload.shared.startTimer()
@@ -199,11 +198,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             hidePrivacyProtectionWindow()
         }
 
-        if !account.isEmpty {
-            NCNetworkingProcessUpload.shared.verifyUploadZombie()
-        }
+        NCService.shared.startRequestServicesServer()
 
-        // Start Auto Upload
         NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
         }
@@ -211,36 +207,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidBecomeActive)
     }
 
-    // L' applicazione entrerà in primo piano (dopo il background)
-    func applicationWillEnterForeground(_ application: UIApplication) {
-        guard !account.isEmpty, let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return }
-
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application will enter in foreground")
-
-        if activeAccount.account != account {
-            settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account))
-        } else {
-            // Request Service Server Nextcloud
-            NCService.shared.startRequestServicesServer()
-        }
-
-        // Required unsubscribing / subscribing
-        NCPushNotification.shared().pushNotification()
-
-        // Request TouchID, FaceID
-        enableTouchFaceID()
-        
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetwork, second: 2)
-    }
-
     // L' applicazione si dimetterà dallo stato di attivo
     func applicationWillResignActive(_ application: UIApplication) {
-        // Nextcloud update share accounts
-        if let error = updateShareAccounts() {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Create share accounts \(error.localizedDescription)")
-        }
+
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application will resign active")
+
         guard !account.isEmpty else { return }
 
         // STOP OBSERVE/TIMER UPLOAD PROCESS
@@ -248,18 +219,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NCNetworkingProcessUpload.shared.stopTimer()
 
         if CCUtility.getPrivacyScreenEnabled() {
-            // Privacy
             showPrivacyProtectionWindow()
         }
 
         // Reload Widget
         WidgetCenter.shared.reloadAllTimelines()
 
-        // Clear operation queue
-        NCOperationQueue.shared.cancelAllQueue()
-        // Clear download
-        NCNetworking.shared.cancelAllDownloadTransfer()
-
         // Clear older files
         let days = CCUtility.getCleanUpDay()
         if let directory = CCUtility.getDirectoryProviderStorage() {
@@ -269,16 +234,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillResignActive)
     }
 
+    // L' applicazione entrerà in primo piano (dopo il background)
+    func applicationWillEnterForeground(_ application: UIApplication) {
+
+        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application will enter in foreground")
+
+        guard !account.isEmpty else { return }
+
+        enableTouchFaceID()
+
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetwork, second: 2)
+    }
+
     // L' applicazione è entrata nello sfondo
     func applicationDidEnterBackground(_ application: UIApplication) {
-        guard !account.isEmpty else { return }
 
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application did enter in background")
 
-        scheduleAppRefresh()
-        scheduleAppProcessing()
+        guard !account.isEmpty else { return }
+
+        if let error = updateShareAccounts() {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Create share accounts \(error.localizedDescription)")
+        }
+
+        NCNetworking.shared.cancelSessions(inBackground: false)
 
-        // Passcode
         presentPasscode { }
 
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidEnterBackground)
@@ -287,7 +268,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // L'applicazione terminerà
     func applicationWillTerminate(_ application: UIApplication) {
 
-        NCNetworking.shared.cancelAllDownloadTransfer()
+        NCNetworking.shared.cancelSessions(inBackground: false)
 
         if UIApplication.shared.backgroundRefreshStatus == .available {
 
@@ -302,34 +283,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NextcloudKit.shared.nkCommonInstance.writeLog("bye bye")
     }
 
-    // MARK: -
-
-    @objc private func initialize() {
-        guard !account.isEmpty else { return }
-
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] initialize Main")
-
-        // Registeration push notification
-        NCPushNotification.shared().pushNotification()
-
-        // Unlock E2EE
-        NCNetworkingE2EE.shared.unlockAll(account: account)
-
-        // Start services
-        NCService.shared.startRequestServicesServer()
-
-        // close detail
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuDetailClose)
-
-        // Reload Widget
-        WidgetCenter.shared.reloadAllTimelines()
-
-        // Registeration domain File Provider
-        // FileProviderDomain *fileProviderDomain = [FileProviderDomain new];
-        // [fileProviderDomain removeAllDomains];
-        // [fileProviderDomain registerDomains];
-    }
-
     // MARK: - Background Task
 
     /*
@@ -342,7 +295,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds.
         do {
             try BGTaskScheduler.shared.submit(request)
-            NextcloudKit.shared.nkCommonInstance.writeLog("[SUCCESS] Refresh task success submit request 60 seconds \(request)")
         } catch {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)")
         }
@@ -360,20 +312,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         request.requiresExternalPower = false
         do {
             try BGTaskScheduler.shared.submit(request)
-            NextcloudKit.shared.nkCommonInstance.writeLog("[SUCCESS] Background Processing task success submit request 5 minutes \(request)")
         } catch {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)")
         }
     }
 
-    func handleRefreshTask(_ task: BGTask) {
+    func handleAppRefresh(_ task: BGTask) {
         scheduleAppRefresh()
-        
-        guard !account.isEmpty else {
-            task.setTaskCompleted(success: true)
-            return
+
+        guard !account.isEmpty, !isAppProcessing else {
+            return task.setTaskCompleted(success: true)
         }
 
+        isAppRefresh = true
         NextcloudKit.shared.setup(delegate: NCNetworking.shared)
 
         NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
@@ -381,20 +332,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             NCNetworkingProcessUpload.shared.start { items in
                 NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Refresh task upload process with \(items) uploads")
                 task.setTaskCompleted(success: true)
+                self.isAppRefresh = false
             }
         }
     }
 
     func handleProcessingTask(_ task: BGTask) {
         scheduleAppProcessing()
-        
-        guard !account.isEmpty else {
-            task.setTaskCompleted(success: true)
-            return
+
+        guard !account.isEmpty, !isAppRefresh else {
+            return task.setTaskCompleted(success: true)
         }
 
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Processing task")
-        task.setTaskCompleted(success: true)
+        isAppProcessing = true
+        NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+
+        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
+            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Processing task auto upload with \(items) uploads")
+            NCNetworkingProcessUpload.shared.start { items in
+                NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Processing task upload process with \(items) uploads")
+                task.setTaskCompleted(success: true)
+                self.isAppProcessing = false
+            }
+        }
     }
 
     // MARK: - Background Networking Session
@@ -402,7 +362,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
 
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start handle Events For Background URLSession: \(identifier)")
-        // Reload Widget
         WidgetCenter.shared.reloadAllTimelines()
         backgroundSessionCompletionHandler = completionHandler
     }
@@ -415,7 +374,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
 
-        if let pref = UserDefaults.init(suiteName: NCBrandOptions.shared.capabilitiesGroups),
+        if let pref = UserDefaults(suiteName: NCBrandOptions.shared.capabilitiesGroups),
            let data = pref.object(forKey: "NOTIFICATION_DATA") as? [String: AnyObject] {
             nextcloudPushNotificationAction(data: data)
             pref.set(nil, forKey: "NOTIFICATION_DATA")
@@ -439,36 +398,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     func nextcloudPushNotificationAction(data: [String: AnyObject]) {
-        NCApplicationHandle().nextcloudPushNotificationAction(data: data) {
 
-            var findAccount: Bool = false
+        guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data) else { return }
+        var findAccount: Bool = false
 
-            if let accountPush = data["account"] as? String,
-               let app = data["app"] as? String,
-               app == NCGlobal.shared.twoFactorNotificatioName {
-                if accountPush == self.account {
-                    findAccount = true
-                } else {
-                    let accounts = NCManageDatabase.shared.getAllAccount()
-                    for account in accounts {
-                        if account.account == accountPush {
-                            self.changeAccount(account.account)
-                            findAccount = true
-                        }
+        if let accountPush = data["account"] as? String {
+            if accountPush == self.account {
+                findAccount = true
+            } else {
+                let accounts = NCManageDatabase.shared.getAllAccount()
+                for account in accounts {
+                    if account.account == accountPush {
+                        self.changeAccount(account.account, userProfile: nil)
+                        findAccount = true
                     }
                 }
-                if findAccount, let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
-                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-                        let navigationController = UINavigationController(rootViewController: viewController)
-                        navigationController.modalPresentationStyle = .fullScreen
-                        self.window?.rootViewController?.present(navigationController, animated: true)
-                    }
-                } else if !findAccount {
-                    let message = NSLocalizedString("_the_account_", comment: "") + " " + accountPush + " " + NSLocalizedString("_does_not_exist_", comment: "")
-                    let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                    self.window?.rootViewController?.present(alertController, animated: true, completion: { })
+            }
+            if findAccount, let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
+                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+                    let navigationController = UINavigationController(rootViewController: viewController)
+                    navigationController.modalPresentationStyle = .fullScreen
+                    self.window?.rootViewController?.present(navigationController, animated: true)
                 }
+            } else if !findAccount {
+                let message = NSLocalizedString("_the_account_", comment: "") + " " + accountPush + " " + NSLocalizedString("_does_not_exist_", comment: "")
+                let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                self.window?.rootViewController?.present(alertController, animated: true, completion: { })
             }
         }
     }
@@ -532,7 +488,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         if contextViewController == nil {
             if let viewController = viewController {
-                let navigationController = NCLoginNavigationController.init(rootViewController: viewController)
+                let navigationController = NCLoginNavigationController(rootViewController: viewController)
                 navigationController.navigationBar.barStyle = .black
                 navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
                 navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
@@ -542,11 +498,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             }
         } else if contextViewController is UINavigationController {
             if let contextViewController = contextViewController, let viewController = viewController {
-                (contextViewController as! UINavigationController).pushViewController(viewController, animated: true)
+                (contextViewController as? UINavigationController)?.pushViewController(viewController, animated: true)
             }
         } else {
             if let viewController = viewController, let contextViewController = contextViewController {
-                let navigationController = NCLoginNavigationController.init(rootViewController: viewController)
+                let navigationController = NCLoginNavigationController(rootViewController: viewController)
                 navigationController.modalPresentationStyle = .fullScreen
                 navigationController.navigationBar.barStyle = .black
                 navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
@@ -556,17 +512,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             }
         }
     }
-    
+
     @objc func startTimerErrorNetworking() {
         timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking), userInfo: nil, repeats: true)
     }
 
     @objc private func checkErrorNetworking() {
-        if account != "" && CCUtility.getPassword(account)!.count == 0 {
-            openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true)
-        }
+        guard !account.isEmpty, CCUtility.getPassword(account)!.isEmpty else { return }
+        openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true)
     }
-    
+
     func trustCertificateError(host: String) {
 
         guard let currentHost = URL(string: self.urlBase)?.host,
@@ -584,15 +539,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert)
 
-        alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { action in
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
             NCNetworking.shared.writeCertificate(host: host)
         }))
 
-        alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { action in }))
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in }))
 
-        alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { action in
-            if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController {
-                let viewController = navigationController.topViewController as! NCViewCertificateDetails
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in
+            if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController,
+               let viewController = navigationController.topViewController as? NCViewCertificateDetails {
                 viewController.delegate = self
                 viewController.host = host
                 self.window?.rootViewController?.present(navigationController, animated: true)
@@ -608,31 +563,38 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - Account
 
-    @objc func settingAccount(_ account: String, urlBase: String, user: String, userId: String, password: String, initialize: Bool = true) {
+    @objc func changeAccount(_ account: String, userProfile: NKUserProfile?) {
 
-        let accountTestBackup = self.account + "/" + self.userId
-        let accountTest = account +  "/" + userId
+        guard let tableAccount = NCManageDatabase.shared.setAccountActive(account) else { return }
 
-        self.account = account
-        self.urlBase = urlBase
-        self.user = user
-        self.userId = userId
-        self.password = password
+        NCNetworking.shared.cancelSessions(inBackground: false)
 
-        _ = NCActionCenter.shared
+        self.account = tableAccount.account
+        self.urlBase = tableAccount.urlBase
+        self.user = tableAccount.user
+        self.userId = tableAccount.userId
+        self.password = CCUtility.getPassword(tableAccount.account)
 
         NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
         NCManageDatabase.shared.setCapabilities(account: account)
 
+        if let userProfile {
+            NCManageDatabase.shared.setAccountUserProfile(account: account, userProfile: userProfile)
+        }
+
         if NCGlobal.shared.capabilityServerVersionMajor > 0 {
             NextcloudKit.shared.setup(nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor)
         }
 
-        DispatchQueue.main.async {
-            if initialize, UIApplication.shared.applicationState != .background && accountTestBackup != accountTest {
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize, second: 0.2)
-            }
+        NCPushNotification.shared().pushNotification()
+
+        NCService.shared.startRequestServicesServer()
+
+        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
+            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
         }
+
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser)
     }
 
     @objc func deleteAccount(_ account: String, wipe: Bool) {
@@ -640,23 +602,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
             NCPushNotification.shared().unsubscribingNextcloudServerPushNotification(account.account, urlBase: account.urlBase, user: account.user, withSubscribing: false)
         }
-        
+
         let results = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@", account), sorted: "ocId", ascending: false)
         for result in results {
             CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(result.ocId))
         }
         NCManageDatabase.shared.clearDatabase(account: account, removeAccount: true)
-        
+
         CCUtility.clearAllKeysEnd(toEnd: account)
         CCUtility.clearAllKeysPushNotification(account)
         CCUtility.setPassword(account, password: nil)
 
+        self.account = ""
+        self.urlBase = ""
+        self.user = ""
+        self.userId = ""
+        self.password = ""
+
         if wipe {
-            settingAccount("", urlBase: "", user: "", userId: "", password: "")
             let accounts = NCManageDatabase.shared.getAccounts()
             if accounts?.count ?? 0 > 0 {
                 if let newAccount = accounts?.first {
-                    self.changeAccount(newAccount)
+                    self.changeAccount(newAccount, userProfile: nil)
                 }
             } else {
                 openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
@@ -664,25 +631,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
     }
 
-    @objc func deleteAllAccounts() {
+    func deleteAllAccounts() {
         let accounts = NCManageDatabase.shared.getAccounts()
         accounts?.forEach({ account in
             deleteAccount(account, wipe: true)
         })
     }
 
-    @objc func changeAccount(_ account: String) {
-
-        NCManageDatabase.shared.setAccountActive(account)
-        if let tableAccount = NCManageDatabase.shared.getActiveAccount() {
-
-            NCOperationQueue.shared.cancelAllQueue()
-            NCNetworking.shared.cancelAllTask()
-
-            settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account))
-        }
-    }
-
     func updateShareAccounts() -> Error? {
         guard let dirGroupApps = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroupApps) else { return nil }
 
@@ -702,9 +657,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // MARK: - Account Request
 
     func accountRequestChangeAccount(account: String) {
-        changeAccount(account)
+        changeAccount(account, userProfile: nil)
     }
-    
+
     func requestAccount() {
 
         if isPasscodePresented() { return }
@@ -713,7 +668,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         let accounts = NCManageDatabase.shared.getAllAccount()
 
         if accounts.count > 1 {
-            
+
             if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
 
                 vcAccountRequest.activeAccount = NCManageDatabase.shared.getActiveAccount()
@@ -723,15 +678,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 vcAccountRequest.dismissDidEnterBackground = false
                 vcAccountRequest.delegate = self
 
-                let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
+                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)
+                let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height + 20)
                 popup.backgroundAlpha = 0.8
 
                 window?.rootViewController?.present(popup, animated: true)
-                
+
                 vcAccountRequest.startTimer()
             }
         }
@@ -739,7 +694,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - Passcode
 
-    func presentPasscode(completion: @escaping () -> ()) {
+    func presentPasscode(completion: @escaping () -> Void) {
 
         let laContext = LAContext()
         var error: NSError?
@@ -757,9 +712,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         passcodeViewController.keypadButtonShowLettering = false
         if CCUtility.getEnableTouchFaceID() && laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
             if error == nil {
-                if laContext.biometryType == .faceID  {
+                if laContext.biometryType == .faceID {
                     passcodeViewController.biometryType = .faceID
-                } else if laContext.biometryType == .touchID  {
+                } else if laContext.biometryType == .touchID {
                     passcodeViewController.biometryType = .touchID
                 }
                 passcodeViewController.allowBiometricValidation = true
@@ -785,7 +740,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         else { return }
 
         DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-            LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: NCBrandOptions.shared.brand) { (success, error) in
+            LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: NCBrandOptions.shared.brand) { success, _ in
                 if success {
                     DispatchQueue.main.async {
                         passcodeViewController.dismiss(animated: true) {
@@ -884,27 +839,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                     return false
                 }
 
-
                 switch actionScheme {
                 case NCGlobal.shared.actionUploadAsset:
 
                     NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: rootViewController) { hasPermission in
                         if hasPermission {
-                            NCPhotosPickerViewController.init(viewController: rootViewController, maxSelectedAssets: 0, singleSelectedMode: false)
+                            NCPhotosPickerViewController(viewController: rootViewController, maxSelectedAssets: 0, singleSelectedMode: false)
                         }
                     }
-                    
+
                 case NCGlobal.shared.actionScanDocument:
 
                     NCDocumentCamera.shared.openScannerDocument(viewController: rootViewController)
 
                 case NCGlobal.shared.actionTextDocument:
-                    
-                    guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController(), let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account), let directEditingCreator = directEditingCreators.first(where: { $0.editor == NCGlobal.shared.editorText}) else { return false }
-                    
+
+                    guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController(),
+                          let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account),
+                          let directEditingCreator = directEditingCreators.first(where: { $0.editor == NCGlobal.shared.editorText}),
+                          let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments else { return false }
+
                     navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                    let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
                     viewController.editorId = NCGlobal.shared.editorText
                     viewController.creatorId = directEditingCreator.identifier
                     viewController.typeTemplate = NCGlobal.shared.templateDocument
@@ -912,20 +868,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                     viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
 
                     rootViewController.present(navigationController, animated: true, completion: nil)
-                    
+
                 case NCGlobal.shared.actionVoiceMemo:
-                    
+
                     NCAskAuthorization.shared.askAuthorizationAudioRecord(viewController: rootViewController) { hasPermission in
                         if hasPermission {
                             let fileName = CCUtility.createFileNameDate(NSLocalizedString("_voice_memo_filename_", comment: ""), extension: "m4a")!
-                            let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as! NCAudioRecorderViewController
+                            if let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController {
 
-                            viewController.delegate = self
-                            viewController.createRecorder(fileName: fileName)
-                            viewController.modalTransitionStyle = .crossDissolve
-                            viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
+                                viewController.delegate = self
+                                viewController.createRecorder(fileName: fileName)
+                                viewController.modalTransitionStyle = .crossDissolve
+                                viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
 
-                            rootViewController.present(viewController, animated: true, completion: nil)
+                                rootViewController.present(viewController, animated: true, completion: nil)
+                            }
                         }
                     }
 
@@ -1019,7 +976,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 for account in accounts {
                     let urlBase = URL(string: account.urlBase)
                     if url.contains(urlBase?.host ?? "") && userId == account.userId {
-                        changeAccount(account.account)
+                        changeAccount(account.account, userProfile: nil)
                         return account
                     }
                 }

+ 2 - 2
iOSClient/BrowserWeb/NCBrowserWeb.swift

@@ -94,8 +94,8 @@ class NCBrowserWeb: UIViewController {
 
         request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
         request.addValue(language, forHTTPHeaderField: "Accept-Language")
-        
-        webView.customUserAgent = CCUtility.getUserAgent()
+
+        webView.customUserAgent = userAgent
         webView.load(request)
     }
 }

+ 1 - 1
iOSClient/Color/NCColorPicker.swift

@@ -199,7 +199,7 @@ class NCColorPicker: UIViewController {
             let serverUrl = metadata.serverUrl + "/" + metadata.fileName
             if NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, colorFolder: hexColor, account: metadata.account) != nil {
                 self.dismiss(animated: true)
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl])
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
             }
         }
         self.dismiss(animated: true)

+ 7 - 96
iOSClient/Data/NCDataSource.swift

@@ -35,7 +35,6 @@ class NCDataSource: NSObject {
     private var sectionsValue: [String] = []
     private var providers: [NKSearchProvider]?
     private var searchResults: [NKSearchResult]?
-    private var localFiles: [tableLocalFile] = []
 
     private var ascending: Bool = true
     private var sort: String = ""
@@ -51,10 +50,9 @@ class NCDataSource: NSObject {
         super.init()
 
         self.metadatas = metadatas.filter({
-            !NCGlobal.shared.includeHiddenFiles.contains($0.fileNameView)
+            !(NCGlobal.shared.includeHiddenFiles.contains($0.fileNameView) || $0.isTransferInForeground)
         })
         self.directory = directory
-        self.localFiles = NCManageDatabase.shared.getTableLocalFile(account: account)
         self.sort = sort ?? "none"
         self.ascending = ascending ?? false
         self.directoryOnTop = directoryOnTop ?? true
@@ -78,7 +76,6 @@ class NCDataSource: NSObject {
         self.sectionsValue.removeAll()
         self.providers = nil
         self.searchResults = nil
-        self.localFiles.removeAll()
     }
 
     func clearDirectory() {
@@ -175,7 +172,6 @@ class NCDataSource: NSObject {
         let metadatas = self.metadatas.filter({ getSectionValue(metadata: $0) == sectionValue})
         let metadataForSection = NCMetadataForSection(sectionValue: sectionValue,
                                                       metadatas: metadatas,
-                                                      localFiles: self.localFiles,
                                                       lastSearchResult: searchResult,
                                                       sort: self.sort,
                                                       ascending: self.ascending,
@@ -218,79 +214,6 @@ class NCDataSource: NSObject {
         return indexPaths
     }
 
-    @discardableResult
-    func addMetadata(_ metadata: tableMetadata) -> (indexPath: IndexPath?, sameSections: Bool) {
-
-        let numberOfSections = self.numberOfSections()
-        let sectionValue = getSectionValue(metadata: metadata)
-
-        // ADD metadatasSource
-        if let rowIndex = self.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
-            self.metadatas[rowIndex] = metadata
-        } else {
-            self.metadatas.append(metadata)
-        }
-
-        // ADD metadataForSection
-        if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionIndex) {
-            if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
-                metadataForSection.metadatas[rowIndex] = metadata
-                return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-            } else {
-                metadataForSection.metadatas.append(metadata)
-                metadataForSection.createMetadatas()
-                if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
-                    return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-                }
-                return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-            }
-        } else {
-            // NEW section
-            createSections()
-            // get IndexPath of new section
-            if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionIndex) {
-                if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
-                    return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-                }
-            }
-        }
-
-        return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-    }
-
-    func deleteMetadata(ocId: String) -> (indexPath: IndexPath?, sameSections: Bool) {
-
-        let numberOfSections = self.numberOfSections()
-        var indexPathReturn: IndexPath?
-        var sectionValue = ""
-
-        // DELETE metadataForSection (IMPORTANT FIRST)
-        let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId)
-        if let indexPath = indexPath, let metadataForSection = metadataForSection, indexPath.row < metadataForSection.metadatas.count {
-            metadataForSection.metadatas.remove(at: indexPath.row)
-            if metadataForSection.metadatas.isEmpty {
-                // REMOVE sectionsValue / metadatasForSection
-                sectionValue = metadataForSection.sectionValue
-                if let sectionIndex = getSectionIndex(sectionValue) {
-                    self.sectionsValue.remove(at: sectionIndex)
-                }
-                if let index = getIndexMetadatasForSection(sectionValue) {
-                    self.metadatasForSection.remove(at: index)
-                }
-            } else {
-                metadataForSection.createMetadatas()
-            }
-            indexPathReturn = indexPath
-        } else { return (nil, false) }
-
-        // DELETE metadatasSource (IMPORTANT LAST)
-        if let rowIndex = self.metadatas.firstIndex(where: {$0.ocId == ocId}) {
-            self.metadatas.remove(at: rowIndex)
-        }
-
-        return (indexPathReturn, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
-    }
-
     @discardableResult
     func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> (indexPath: IndexPath?, sameSections: Bool) {
 
@@ -328,11 +251,6 @@ class NCDataSource: NSObject {
         return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
     }
 
-    func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
-        guard !self.metadatasForSection.isEmpty else { return false }
-        return numberOfSections == self.numberOfSections()
-    }
-
     func numberOfSections() -> Int {
         guard !self.sectionsValue.isEmpty else { return 1 }
         return self.sectionsValue.count
@@ -348,11 +266,6 @@ class NCDataSource: NSObject {
         return metadataForSection.metadatas[indexPath.row]
     }
 
-    func getSectionValue(indexPath: IndexPath) -> String {
-        guard !metadatasForSection.isEmpty, let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""}
-        return metadataForSection.sectionValue
-    }
-
     func getSectionValueLocalization(indexPath: IndexPath) -> String {
         guard !metadatasForSection.isEmpty, let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""}
         if let searchResults = self.searchResults, let searchResult = searchResults.filter({ $0.id == metadataForSection.sectionValue}).first {
@@ -378,6 +291,11 @@ class NCDataSource: NSObject {
 
     // MARK: -
 
+    internal func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
+        guard !self.metadatasForSection.isEmpty else { return false }
+        return numberOfSections == self.numberOfSections()
+    }
+
     internal func getSectionValue(metadata: tableMetadata) -> String {
 
         switch self.groupByField {
@@ -419,7 +337,6 @@ class NCMetadataForSection: NSObject {
 
     var sectionValue: String
     var metadatas: [tableMetadata]
-    var localFiles: [tableLocalFile]
     var lastSearchResult: NKSearchResult?
     var unifiedSearchInProgress: Bool = false
 
@@ -438,14 +355,11 @@ class NCMetadataForSection: NSObject {
     public var numDirectory: Int = 0
     public var numFile: Int = 0
     public var totalSize: Int64 = 0
-    public var metadataOffLine: [String] = []
-    public var directories: [tableDirectory]?
 
-    init(sectionValue: String, metadatas: [tableMetadata], localFiles: [tableLocalFile], lastSearchResult: NKSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
+    init(sectionValue: String, metadatas: [tableMetadata], lastSearchResult: NKSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
 
         self.sectionValue = sectionValue
         self.metadatas = metadatas
-        self.localFiles = localFiles
         self.lastSearchResult = lastSearchResult
         self.sort = sort
         self.ascending = ascending
@@ -467,7 +381,6 @@ class NCMetadataForSection: NSObject {
         metadatasFavoriteFile.removeAll()
         metadatasDirectory.removeAll()
         metadatasFile.removeAll()
-        metadataOffLine.removeAll()
 
         numDirectory = 0
         numFile = 0
@@ -552,8 +465,6 @@ class NCMetadataForSection: NSObject {
             }
         }
 
-        directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "ocId IN %@", ocIds), sorted: "serverUrl", ascending: true)
-
         metadatas.removeAll()
 
         // Struct view : favorite dir -> favorite file -> directory -> files

+ 2 - 40
iOSClient/Data/NCDatabase.swift

@@ -30,20 +30,6 @@ protocol DateCompareable {
     var dateKey: Date { get }
 }
 
-class tableChunk: Object {
-
-    @objc dynamic var account = ""
-    @objc dynamic var chunkFolder = ""
-    @objc dynamic var fileName = ""
-    @objc dynamic var index = ""
-    @objc dynamic var ocId = ""
-    @objc dynamic var size: Int64 = 0
-
-    override static func primaryKey() -> String {
-        return "index"
-    }
-}
-
 class tableComments: Object, DateCompareable {
     var dateKey: Date { creationDateTime as Date }
 
@@ -98,33 +84,9 @@ class tableExternalSites: Object {
 }
 
 class tableGPS: Object {
-
-    @objc dynamic var latitude = ""
+    @objc dynamic var latitude: Double = 0
+    @objc dynamic var longitude: Double = 0
     @objc dynamic var location = ""
-    @objc dynamic var longitude = ""
-    @objc dynamic var placemarkAdministrativeArea = ""
-    @objc dynamic var placemarkCountry = ""
-    @objc dynamic var placemarkLocality = ""
-    @objc dynamic var placemarkPostalCode = ""
-    @objc dynamic var placemarkThoroughfare = ""
-}
-
-class tableLocalFile: Object {
-
-    @objc dynamic var account = ""
-    @objc dynamic var etag = ""
-    @objc dynamic var exifDate: NSDate?
-    @objc dynamic var exifLatitude = ""
-    @objc dynamic var exifLongitude = ""
-    @objc dynamic var exifLensModel: String?
-    @objc dynamic var favorite: Bool = false
-    @objc dynamic var fileName = ""
-    @objc dynamic var ocId = ""
-    @objc dynamic var offline: Bool = false
-
-    override static func primaryKey() -> String {
-        return "ocId"
-    }
 }
 
 class tablePhotoLibrary: Object {

+ 20 - 20
iOSClient/Data/NCManageDatabase+Account.swift

@@ -88,7 +88,7 @@ class tableAccount: Object, NCUserBaseUrl {
 
 extension NCManageDatabase {
 
-    @objc func addAccount(_ account: String, urlBase: String, user: String, password: String) {
+    func addAccount(_ account: String, urlBase: String, user: String, userId: String, password: String) {
 
         do {
             let realm = try Realm()
@@ -110,7 +110,7 @@ extension NCManageDatabase {
 
                 addObject.urlBase = urlBase
                 addObject.user = user
-                addObject.userId = user
+                addObject.userId = userId
 
                 realm.add(addObject, update: .all)
             }
@@ -148,10 +148,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return nil }
             return tableAccount.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -161,12 +162,13 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableAccount.self).sorted(byKeyPath: "account", ascending: true)
             if !results.isEmpty {
                 return Array(results.map { $0.account })
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -176,10 +178,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableAccount.self).filter(predicate).first else { return nil }
             return tableAccount.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -189,11 +192,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sorted = [SortDescriptor(keyPath: "active", ascending: false), SortDescriptor(keyPath: "user", ascending: true)]
             let results = realm.objects(tableAccount.self).sorted(by: sorted)
             return Array(results.map { tableAccount.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -203,11 +207,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sorted = [SortDescriptor(keyPath: "active", ascending: false), SortDescriptor(keyPath: "alias", ascending: true), SortDescriptor(keyPath: "user", ascending: true)]
             let results = realm.objects(tableAccount.self).sorted(by: sorted)
             return Array(results.map { tableAccount.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -217,6 +222,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return "" }
             if result.autoUploadFileName.isEmpty {
                 return NCBrandOptions.shared.folderDefaultAutoUpload
@@ -224,7 +230,7 @@ extension NCManageDatabase {
                 return result.autoUploadFileName
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return ""
@@ -234,6 +240,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return "" }
             if result.autoUploadDirectory.isEmpty {
                 return NCUtilityFileSystem.shared.getHomeServer(urlBase: urlBase, userId: userId)
@@ -246,7 +253,7 @@ extension NCManageDatabase {
                 }
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return ""
@@ -266,24 +273,23 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return NCGlobal.shared.subfolderGranularityMonthly }
             return result.autoUploadSubfolderGranularity
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return NCGlobal.shared.subfolderGranularityMonthly
     }
 
-    @discardableResult
-    @objc func setAccountActive(_ account: String) -> tableAccount? {
+    func setAccountActive(_ account: String) -> tableAccount? {
 
         var accountReturn = tableAccount()
 
         do {
             let realm = try Realm()
             try realm.write {
-
                 let results = realm.objects(tableAccount.self)
                 for result in results {
                     if result.account == account {
@@ -307,7 +313,6 @@ extension NCManageDatabase {
         do {
             let realm = try Realm()
             try realm.write {
-
                 if let result = realm.objects(tableAccount.self).filter("account == %@", account).first {
                     result.password = "********"
                 }
@@ -383,7 +388,7 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func setAccountUserProfile(account: String, userProfile: NKUserProfile) -> tableAccount? {
+    @objc func setAccountUserProfile(account: String, userProfile: NKUserProfile) {
 
         do {
             let realm = try Realm()
@@ -414,14 +419,9 @@ extension NCManageDatabase {
                     result.website = userProfile.website
                 }
             }
-            if let result = realm.objects(tableAccount.self).filter("account == %@", account).first {
-                return tableAccount.init(value: result)
-            }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
         }
-
-        return nil
     }
 
     @objc func setAccountMediaPath(_ path: String, account: String) {

+ 12 - 6
iOSClient/Data/NCManageDatabase+Activity.swift

@@ -196,6 +196,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableActivity.self).filter(predicate).sorted(byKeyPath: "idActivity", ascending: false)
             let allActivity = Array(results.map(tableActivity.init))
             guard let filterFileId = filterFileId else {
@@ -205,7 +206,7 @@ extension NCManageDatabase {
             let filtered = allActivity.filter({ String($0.objectId) == filterFileId && $0.type != "comments" })
             return (all: allActivity, filter: filtered)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return([], [])
@@ -215,10 +216,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d && key == %@", account, idActivity, key).first
             return results.map { tableActivitySubjectRich.init(value: $0) }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -228,6 +230,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d && id == %@", account, idActivity, id)
             var activitySubjectRich = results.first
             if results.count == 2 {
@@ -239,7 +242,7 @@ extension NCManageDatabase {
             }
             return activitySubjectRich.map { tableActivitySubjectRich.init(value: $0) }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -251,6 +254,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             for id in orderKeysId {
                 if let result = realm.objects(tableActivityPreview.self).filter("account == %@ && idActivity == %d && fileId == %d", account, idActivity, Int(id) ?? 0).first {
                     results.append(result)
@@ -258,7 +262,7 @@ extension NCManageDatabase {
             }
             return results
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -284,9 +288,10 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             return realm.objects(tableActivityLatestId.self).filter("account == %@", account).first
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -328,10 +333,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableComments.self).filter("account == %@ AND objectId == %@", account, objectId).sorted(byKeyPath: "creationDateTime", ascending: false)
             return Array(results.map(tableComments.init))
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []

+ 4 - 2
iOSClient/Data/NCManageDatabase+Avatar.swift

@@ -60,10 +60,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableAvatar.self).filter("fileName == %@", fileName).first else { return nil }
             return tableAvatar.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -116,6 +117,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let result = realm.objects(tableAvatar.self).filter("fileName == %@", fileName).first
             if result == nil {
                 NCUtilityFileSystem.shared.deleteFile(filePath: fileNameLocalPath)
@@ -125,7 +127,7 @@ extension NCManageDatabase {
             }
             return UIImage(contentsOfFile: fileNameLocalPath)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         NCUtilityFileSystem.shared.deleteFile(filePath: fileNameLocalPath)

+ 7 - 1
iOSClient/Data/NCManageDatabase+Capabilities.swift

@@ -56,10 +56,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableCapabilities.self).filter("account == %@", account).first else { return nil }
             return result.jsondata
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -285,6 +286,10 @@ extension NCManageDatabase {
             NCGlobal.shared.capabilityServerVersion = json.ocs.data.version.string
             NCGlobal.shared.capabilityServerVersionMajor = json.ocs.data.version.major
 
+            if NCGlobal.shared.capabilityServerVersionMajor > 0 {
+                NextcloudKit.shared.setup(nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor)
+            }
+
             NCGlobal.shared.capabilityFileSharingApiEnabled = json.ocs.data.capabilities.filessharing?.apienabled ?? false
             NCGlobal.shared.capabilityFileSharingDefaultPermission = json.ocs.data.capabilities.filessharing?.defaultpermissions ?? 0
             NCGlobal.shared.capabilityFileSharingPubPasswdEnforced = json.ocs.data.capabilities.filessharing?.ncpublic?.password?.enforced ?? false
@@ -328,6 +333,7 @@ extension NCManageDatabase {
             NCGlobal.shared.capabilityFilesUndelete = json.ocs.data.capabilities.files?.undelete ?? false
             NCGlobal.shared.capabilityFilesLockVersion = json.ocs.data.capabilities.files?.locking ?? ""
             NCGlobal.shared.capabilityFilesComments = json.ocs.data.capabilities.files?.comments ?? false
+            NCGlobal.shared.capabilityFilesBigfilechunking = json.ocs.data.capabilities.files?.bigfilechunking ?? false
 
             NCGlobal.shared.capabilityUserStatusEnabled = json.ocs.data.capabilities.files?.undelete ?? false
             if json.ocs.data.capabilities.external != nil {

+ 127 - 0
iOSClient/Data/NCManageDatabase+Chunk.swift

@@ -0,0 +1,127 @@
+//
+//  NCManageDatabase+Chunk.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 05/08/23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import RealmSwift
+import NextcloudKit
+
+class tableChunk: Object {
+
+    @Persisted var account = ""
+    @Persisted var chunkFolder = ""
+    @Persisted(primaryKey: true) var index = ""
+    @Persisted var fileName: Int = 0
+    @Persisted var ocId = ""
+    @Persisted var size: Int64 = 0
+}
+
+extension NCManageDatabase {
+
+    func getChunkFolder(account: String, ocId: String) -> String {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            guard let result = realm.objects(tableChunk.self).filter("account == %@ AND ocId == %@", account, ocId).first else { return NSUUID().uuidString }
+            return result.chunkFolder
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+
+        return NSUUID().uuidString
+    }
+
+    func getChunks(account: String, ocId: String) -> [(fileName: String, size: Int64)] {
+
+        var filesChunk: [(fileName: String, size: Int64)] = []
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            let results = realm.objects(tableChunk.self).filter("account == %@ AND ocId == %@", account, ocId).sorted(byKeyPath: "fileName", ascending: true)
+            for result in results {
+                filesChunk.append((fileName: "\(result.fileName)", size: result.size))
+            }
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+
+        return filesChunk
+    }
+
+    func addChunks(account: String, ocId: String, chunkFolder: String, filesChunk: [(fileName: String, size: Int64)]) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let results = realm.objects(tableChunk.self).filter(NSPredicate(format: "account == %@ AND ocId == %@", account, ocId))
+                realm.delete(results)
+                for fileChunk in filesChunk {
+                    let object = tableChunk()
+                    object.account = account
+                    object.chunkFolder = chunkFolder
+                    object.fileName = Int(fileChunk.fileName) ?? 0
+                    object.index = ocId + fileChunk.fileName
+                    object.ocId = ocId
+                    object.size = fileChunk.size
+                    realm.add(object, update: .all)
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func deleteChunk(account: String, ocId: String, fileChunk: (fileName: String, size: Int64), directory: String) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let result = realm.objects(tableChunk.self).filter(NSPredicate(format: "account == %@ AND ocId == %@ AND fileName == %d", account, ocId, Int(fileChunk.fileName) ?? 0))
+                realm.delete(result)
+                let filePath = directory + "/\(fileChunk.fileName)"
+                NCUtilityFileSystem.shared.deleteFile(filePath: filePath)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func deleteChunks(account: String, ocId: String, directory: String) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let results = realm.objects(tableChunk.self).filter(NSPredicate(format: "account == %@ AND ocId == %@", account, ocId))
+                for result in results {
+                    let filePath = directory + "/\(result.fileName)"
+                    NCUtilityFileSystem.shared.deleteFile(filePath: filePath)
+                }
+                realm.delete(results)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+}

+ 4 - 2
iOSClient/Data/NCManageDatabase+DashboardWidget.swift

@@ -54,11 +54,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let resultDashboard = realm.objects(tableDashboardWidget.self).filter("account == %@ AND id == %@", account, id).first else { return (nil, nil) }
             let resultsButton = realm.objects(tableDashboardWidgetButton.self).filter("account == %@ AND id == %@", account, id).sorted(byKeyPath: "type", ascending: true)
             return (tableDashboardWidget.init(value: resultDashboard), Array(resultsButton.map { tableDashboardWidgetButton.init(value: $0) }))
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return (nil, nil)
@@ -68,11 +69,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sortProperties = [SortDescriptor(keyPath: "order", ascending: true), SortDescriptor(keyPath: "title", ascending: true)]
             let results = realm.objects(tableDashboardWidget.self).filter("account == %@", account).sorted(by: sortProperties)
             return Array(results.map { tableDashboardWidget.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []

+ 31 - 5
iOSClient/Data/NCManageDatabase+Directory.swift

@@ -52,9 +52,8 @@ extension NCManageDatabase {
             let realm = try Realm()
             try realm.write {
                 var addObject = tableDirectory()
-                let result = realm.objects(tableDirectory.self).filter("ocId == %@", ocId).first
-                if result != nil {
-                    addObject = result!
+                if let result = realm.objects(tableDirectory.self).filter("ocId == %@", ocId).first {
+                    addObject = result
                 } else {
                     addObject.ocId = ocId
                 }
@@ -128,19 +127,46 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableDirectory.self).filter(predicate).first else { return nil }
             return tableDirectory.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
+
+    func getTableDirectory(account: String, serverUrl: String) -> tableDirectory? {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            return realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
     }
 
+    func getTableDirectory(ocId: String) -> tableDirectory? {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            return realm.objects(tableDirectory.self).filter("ocId == %@", ocId).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+        return nil
+    }
+
     func getTablesDirectory(predicate: NSPredicate, sorted: String, ascending: Bool) -> [tableDirectory]? {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableDirectory.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             if results.isEmpty {
                 return nil
@@ -148,7 +174,7 @@ extension NCManageDatabase {
                 return Array(results.map { tableDirectory.init(value: $0) })
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil

+ 289 - 79
iOSClient/Data/NCManageDatabase+E2EE.swift

@@ -25,42 +25,47 @@ import Foundation
 import RealmSwift
 import NextcloudKit
 
-class tableE2eEncryption: Object {
-
-    @objc dynamic var account = ""
-    @objc dynamic var authenticationTag: String = ""
-    @objc dynamic var blob = "files"
-    @objc dynamic var fileName = ""
-    @objc dynamic var fileNameIdentifier = ""
-    @objc dynamic var fileNamePath = ""
-    @objc dynamic var key = ""
-    @objc dynamic var initializationVector = ""
-    @objc dynamic var metadataKey = ""
-    @objc dynamic var metadataKeyFiledrop = ""
-    @objc dynamic var metadataKeyIndex: Int = 0
-    @objc dynamic var metadataVersion: Double = 0
-    @objc dynamic var mimeType = ""
-    @objc dynamic var serverUrl = ""
-
-    override static func primaryKey() -> String {
-        return "fileNamePath"
-    }
-}
-
 class tableE2eEncryptionLock: Object {
 
-    @objc dynamic var account = ""
-    @objc dynamic var date = NSDate()
-    @objc dynamic var fileId = ""
-    @objc dynamic var serverUrl = ""
-    @objc dynamic var e2eToken = ""
+    @Persisted(primaryKey: true) var fileId = ""
+    @Persisted var account = ""
+    @Persisted var date = Date()
+    @Persisted var serverUrl = ""
+    @Persisted var e2eToken = ""
+}
 
-    override static func primaryKey() -> String {
-        return "fileId"
-    }
+typealias tableE2eEncryption = tableE2eEncryptionV3
+class tableE2eEncryptionV3: Object {
+
+    @Persisted(primaryKey: true) var primaryKey = ""
+    @Persisted var account = ""
+    @Persisted var authenticationTag: String = ""
+    @Persisted var blob = "files"
+    @Persisted var fileName = ""
+    @Persisted var fileNameIdentifier = ""
+    @Persisted var key = ""
+    @Persisted var initializationVector = ""
+    @Persisted var metadataKey = ""
+    @Persisted var metadataKeyFiledrop = ""
+    @Persisted var metadataKeyIndex: Int = 0
+    @Persisted var metadataVersion: Double = 0
+    @Persisted var mimeType = ""
+    @Persisted var ocIdServerUrl: String = ""
+    @Persisted var serverUrl = ""
+
+    convenience init(account: String, ocIdServerUrl: String, fileNameIdentifier: String) {
+        self.init()
+        self.primaryKey = account + ocIdServerUrl + fileNameIdentifier
+        self.account = account
+        self.ocIdServerUrl = ocIdServerUrl
+        self.fileNameIdentifier = fileNameIdentifier
+     }
 }
 
-class tableE2eMetadata: Object {
+// MARK: -
+// MARK: Table V1, V1.2
+
+class tableE2eMetadata12: Object {
 
     @Persisted(primaryKey: true) var serverUrl = ""
     @Persisted var account = ""
@@ -68,24 +73,102 @@ class tableE2eMetadata: Object {
     @Persisted var version: Double = 0
 }
 
+// MARK: -
+// MARK: Table V2
+
+typealias tableE2eMetadata = tableE2eMetadataV2
+class tableE2eMetadataV2: Object {
+
+    @Persisted(primaryKey: true) var primaryKey = ""
+    @Persisted var account = ""
+    @Persisted var deleted: Bool = false
+    @Persisted var folders = Map<String, String>()
+    @Persisted var keyChecksums = List<String>()
+    @Persisted var ocIdServerUrl: String = ""
+    @Persisted var serverUrl: String = ""
+    @Persisted var version: String = NCGlobal.shared.e2eeVersionV20
+
+    convenience init(account: String, ocIdServerUrl: String) {
+        self.init()
+        self.account = account
+        self.ocIdServerUrl = ocIdServerUrl
+        self.primaryKey = account + ocIdServerUrl
+     }
+}
+
+class tableE2eCounter: Object {
+
+    @Persisted(primaryKey: true) var primaryKey: String
+    @Persisted var account: String
+    @Persisted var counter: Int
+    @Persisted var ocIdServerUrl: String
+
+    convenience init(account: String, ocIdServerUrl: String, counter: Int) {
+        self.init()
+        self.account = account
+        self.ocIdServerUrl = ocIdServerUrl
+        self.primaryKey = account + ocIdServerUrl
+        self.counter = counter
+     }
+}
+
+class tableE2eUsers: Object {
+
+    @Persisted(primaryKey: true) var primaryKey = ""
+    @Persisted var account = ""
+    @Persisted var certificate = ""
+    @Persisted var encryptedMetadataKey: String?
+    @Persisted var metadataKey: Data?
+    @Persisted var ocIdServerUrl: String = ""
+    @Persisted var serverUrl: String = ""
+    @Persisted var userId = ""
+
+    convenience init(account: String, ocIdServerUrl: String, userId: String) {
+        self.init()
+        self.primaryKey = account + ocIdServerUrl + userId
+        self.account = account
+        self.ocIdServerUrl = ocIdServerUrl
+        self.userId = userId
+     }
+}
+
+class tableE2eUsersFiledrop: Object {
+
+    @Persisted(primaryKey: true) var primaryKey = ""
+    @Persisted var account = ""
+    @Persisted var certificate = ""
+    @Persisted var encryptedFiledropKey: String?
+    @Persisted var ocIdServerUrl: String = ""
+    @Persisted var serverUrl: String = ""
+    @Persisted var userId = ""
+
+    convenience init(account: String, ocIdServerUrl: String, userId: String) {
+        self.init()
+        self.primaryKey = account + ocIdServerUrl + userId
+        self.account = account
+        self.ocIdServerUrl = ocIdServerUrl
+        self.userId = userId
+     }
+}
+
 extension NCManageDatabase {
 
     // MARK: -
-    // MARK: Table e2e Encryption
+    // MARK: tableE2eEncryption
 
-    @objc func addE2eEncryption(_ e2e: tableE2eEncryption) {
+    func addE2eEncryption(_ object: tableE2eEncryption) {
 
         do {
             let realm = try Realm()
             try realm.write {
-                realm.add(e2e, update: .all)
+                realm.add(object, update: .all)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
         }
     }
 
-    @objc func deleteE2eEncryption(predicate: NSPredicate) {
+    func deleteE2eEncryption(predicate: NSPredicate) {
 
         do {
             let realm = try Realm()
@@ -98,52 +181,41 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func getE2eEncryption(predicate: NSPredicate) -> tableE2eEncryption? {
+    func getE2eEncryption(predicate: NSPredicate) -> tableE2eEncryption? {
 
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableE2eEncryption.self).filter(predicate).sorted(byKeyPath: "metadataKeyIndex", ascending: false).first else { return nil }
             return tableE2eEncryption.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
     }
 
-    @objc func getE2eEncryptions(predicate: NSPredicate) -> [tableE2eEncryption]? {
-
-        guard self.getActiveAccount() != nil else { return nil }
+    func getE2eEncryptions(predicate: NSPredicate) -> [tableE2eEncryption] {
 
         do {
             let realm = try Realm()
             let results: Results<tableE2eEncryption>
             results = realm.objects(tableE2eEncryption.self).filter(predicate)
-            if results.isEmpty {
-                return nil
-            } else {
-                return Array(results.map { tableE2eEncryption.init(value: $0) })
-            }
+            return Array(results.map { tableE2eEncryption.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
-        return nil
+        return []
     }
 
-    @objc func renameFileE2eEncryption(serverUrl: String, fileNameIdentifier: String, newFileName: String, newFileNamePath: String) {
-
-        guard let activeAccount = self.getActiveAccount() else { return }
+    func renameFileE2eEncryption(account: String, serverUrl: String, fileNameIdentifier: String, newFileName: String, newFileNamePath: String) {
 
         do {
             let realm = try Realm()
             try realm.write {
-                guard let result = realm.objects(tableE2eEncryption.self).filter("account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", activeAccount.account, serverUrl, fileNameIdentifier).first else { return }
-                let object = tableE2eEncryption.init(value: result)
-                realm.delete(result)
-                object.fileName = newFileName
-                object.fileNamePath = newFileNamePath
-                realm.add(object)
+                guard let result = realm.objects(tableE2eEncryption.self).filter("account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", account, serverUrl, fileNameIdentifier).first else { return }
+                result.fileName = newFileName
+                realm.add(result, update: .all)
             }
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
@@ -153,20 +225,20 @@ extension NCManageDatabase {
     // MARK: -
     // MARK: Table e2e Encryption Lock
 
-    @objc func getE2ETokenLock(account: String, serverUrl: String) -> tableE2eEncryptionLock? {
+    func getE2ETokenLock(account: String, serverUrl: String) -> tableE2eEncryptionLock? {
 
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableE2eEncryptionLock.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
             return tableE2eEncryptionLock.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
     }
 
-    @objc func getE2EAllTokenLock(account: String) -> [tableE2eEncryptionLock] {
+    func getE2EAllTokenLock(account: String) -> [tableE2eEncryptionLock] {
 
         do {
             let realm = try Realm()
@@ -177,30 +249,30 @@ extension NCManageDatabase {
                 return Array(results.map { tableE2eEncryptionLock.init(value: $0) })
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
     }
 
-    @objc func setE2ETokenLock(account: String, serverUrl: String, fileId: String, e2eToken: String) {
+    func setE2ETokenLock(account: String, serverUrl: String, fileId: String, e2eToken: String) {
 
         do {
             let realm = try Realm()
             try realm.write {
-                let addObject = tableE2eEncryptionLock()
-                addObject.account = account
-                addObject.fileId = fileId
-                addObject.serverUrl = serverUrl
-                addObject.e2eToken = e2eToken
-                realm.add(addObject, update: .all)
+                let object = tableE2eEncryptionLock()
+                object.account = account
+                object.fileId = fileId
+                object.serverUrl = serverUrl
+                object.e2eToken = e2eToken
+                realm.add(object, update: .all)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
         }
     }
 
-    @objc func deleteE2ETokenLock(account: String, serverUrl: String) {
+    func deleteE2ETokenLock(account: String, serverUrl: String) {
 
         do {
             let realm = try Realm()
@@ -215,16 +287,16 @@ extension NCManageDatabase {
     }
 
     // MARK: -
-    // MARK: Table e2ee Metadata
+    // MARK: V1
 
-    func getE2eMetadata(account: String, serverUrl: String) -> tableE2eMetadata? {
+    func getE2eMetadata(account: String, serverUrl: String) -> tableE2eMetadata12? {
 
         do {
             let realm = try Realm()
-            guard let result = realm.objects(tableE2eMetadata.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
-            return tableE2eMetadata.init(value: result)
+            guard let result = realm.objects(tableE2eMetadata12.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
+            return tableE2eMetadata12.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -235,15 +307,153 @@ extension NCManageDatabase {
         do {
             let realm = try Realm()
             try realm.write {
-                let addObject = tableE2eMetadata()
-                addObject.account = account
-                addObject.metadataKey = metadataKey
-                addObject.serverUrl = serverUrl
-                addObject.version = version
-                realm.add(addObject, update: .all)
+                let object = tableE2eMetadata12()
+                object.account = account
+                object.metadataKey = metadataKey
+                object.serverUrl = serverUrl
+                object.version = version
+                realm.add(object, update: .all)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    // MARK: -
+    // MARK: V2
+
+    func addE2EUsers(account: String,
+                     serverUrl: String,
+                     ocIdServerUrl: String,
+                     userId: String,
+                     certificate: String,
+                     encryptedMetadataKey: String?,
+                     metadataKey: Data?) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let object = tableE2eUsers.init(account: account, ocIdServerUrl: ocIdServerUrl, userId: userId)
+                object.certificate = certificate
+                object.encryptedMetadataKey = encryptedMetadataKey
+                object.metadataKey = metadataKey
+                object.serverUrl = serverUrl
+                realm.add(object, update: .all)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func deleteE2EUsers(account: String, ocIdServerUrl: String, userId: String) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                if let result = realm.objects(tableE2eUsers.self).filter("account == %@ AND ocIdServerUrl == %@ AND userId == %@", account, ocIdServerUrl, userId).first {
+                    realm.delete(result)
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func getE2EUsers(account: String, ocIdServerUrl: String) -> Results<tableE2eUsers>? {
+
+        do {
+            let realm = try Realm()
+            return realm.objects(tableE2eUsers.self).filter("account == %@ AND ocIdServerUrl == %@", account, ocIdServerUrl)
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
+
+    func getE2EUsers(account: String, ocIdServerUrl: String, userId: String) -> tableE2eUsers? {
+
+        do {
+            let realm = try Realm()
+            return realm.objects(tableE2eUsers.self).filter("account == %@ && ocIdServerUrl == %@ AND userId == %@", account, ocIdServerUrl, userId).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
+
+    func getE2EUsersFiledrop(account: String, ocIdServerUrl: String, userId: String) -> tableE2eUsersFiledrop? {
+
+        do {
+            let realm = try Realm()
+            return realm.objects(tableE2eUsersFiledrop.self).filter("account == %@ && ocIdServerUrl == %@ AND userId == %@", account, ocIdServerUrl, userId).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
+
+    func getE2eMetadata(account: String, ocIdServerUrl: String) -> tableE2eMetadata? {
+
+        do {
+            let realm = try Realm()
+            return realm.objects(tableE2eMetadata.self).filter("account == %@ && ocIdServerUrl == %@", account, ocIdServerUrl).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
+
+    func addE2eMetadata(account: String, serverUrl: String, ocIdServerUrl: String, keyChecksums: [String]?, deleted: Bool, folders: [String: String]?, version: String) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let object = tableE2eMetadata.init(account: account, ocIdServerUrl: ocIdServerUrl)
+                if let keyChecksums {
+                    object.keyChecksums.append(objectsIn: keyChecksums)
+                }
+                object.deleted = deleted
+                let foldersDictionary = object.folders
+                if let folders {
+                    for folder in folders {
+                        foldersDictionary[folder.key] = folder.value
+                    }
+                }
+                object.serverUrl = serverUrl
+                object.version = version
+                realm.add(object, update: .all)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func updateCounterE2eMetadata(account: String, ocIdServerUrl: String, counter: Int) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let object = tableE2eCounter.init(account: account, ocIdServerUrl: ocIdServerUrl, counter: counter)
+                realm.add(object, update: .all)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
         }
     }
+
+    func getCounterE2eMetadata(account: String, ocIdServerUrl: String) -> Int? {
+
+        do {
+            let realm = try Realm()
+            return realm.objects(tableE2eCounter.self).filter("account == %@ && ocIdServerUrl == %@", account, ocIdServerUrl).first?.counter
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
 }

+ 2 - 1
iOSClient/Data/NCManageDatabase+LayoutForView.swift

@@ -117,13 +117,14 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             if let result = realm.objects(NCDBLayoutForView.self).filter("index == %@", index).first {
                 return NCDBLayoutForView(value: result)
             } else {
                 return setLayoutForView(account: account, key: key, serverUrl: serverUrl)
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return setLayoutForView(account: account, key: key, serverUrl: serverUrl)

+ 209 - 0
iOSClient/Data/NCManageDatabase+LocalFile.swift

@@ -0,0 +1,209 @@
+//
+//  NCManageDatabase+LocalFile.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 01/08/23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import RealmSwift
+import NextcloudKit
+
+class tableLocalFile: Object {
+
+    @objc dynamic var account = ""
+    @objc dynamic var etag = ""
+    @objc dynamic var exifDate: NSDate?
+    @objc dynamic var exifLatitude = ""
+    @objc dynamic var exifLongitude = ""
+    @objc dynamic var exifLensModel: String?
+    @objc dynamic var favorite: Bool = false
+    @objc dynamic var fileName = ""
+    @objc dynamic var ocId = ""
+    @objc dynamic var offline: Bool = false
+
+    override static func primaryKey() -> String {
+        return "ocId"
+    }
+}
+extension NCManageDatabase {
+
+    // MARK: -
+    // MARK: Table LocalFile - return RESULT
+
+    func getTableLocalFile(ocId: String) -> tableLocalFile? {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            return realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+        return nil
+    }
+
+    // MARK: -
+    // MARK: Table LocalFile
+
+    func addLocalFile(metadata: tableMetadata) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let addObject = getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) ?? tableLocalFile()
+                addObject.account = metadata.account
+                addObject.etag = metadata.etag
+                addObject.exifDate = NSDate()
+                addObject.exifLatitude = "-1"
+                addObject.exifLongitude = "-1"
+                addObject.ocId = metadata.ocId
+                addObject.fileName = metadata.fileName
+                realm.add(addObject, update: .all)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func addLocalFile(account: String, etag: String, ocId: String, fileName: String) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let addObject = tableLocalFile()
+                addObject.account = account
+                addObject.etag = etag
+                addObject.exifDate = NSDate()
+                addObject.exifLatitude = "-1"
+                addObject.exifLongitude = "-1"
+                addObject.ocId = ocId
+                addObject.fileName = fileName
+                realm.add(addObject, update: .all)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func deleteLocalFile(predicate: NSPredicate) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let results = realm.objects(tableLocalFile.self).filter(predicate)
+                realm.delete(results)
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func setLocalFile(ocId: String, fileName: String?, etag: String?) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let result = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first
+                if let fileName = fileName {
+                    result?.fileName = fileName
+                }
+                if let etag = etag {
+                    result?.etag = etag
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    @objc func setLocalFile(ocId: String, exifDate: NSDate?, exifLatitude: String, exifLongitude: String, exifLensModel: String?) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                if let result = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first {
+                    result.exifDate = exifDate
+                    result.exifLatitude = exifLatitude
+                    result.exifLongitude = exifLongitude
+                    if exifLensModel?.count ?? 0 > 0 {
+                        result.exifLensModel = exifLensModel
+                    }
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func setLocalFile(ocId: String, offline: Bool) {
+
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let result = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first
+                result?.offline = offline
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
+
+    func getTableLocalFile(account: String) -> [tableLocalFile] {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            let results = realm.objects(tableLocalFile.self).filter("account == %@", account)
+            return Array(results.map { tableLocalFile.init(value: $0) })
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return []
+    }
+
+    func getTableLocalFile(predicate: NSPredicate) -> tableLocalFile? {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            guard let result = realm.objects(tableLocalFile.self).filter(predicate).first else { return nil }
+            return tableLocalFile.init(value: result)
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return nil
+    }
+
+    func getTableLocalFiles(predicate: NSPredicate, sorted: String, ascending: Bool) -> [tableLocalFile] {
+
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            let results = realm.objects(tableLocalFile.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
+            return Array(results.map { tableLocalFile.init(value: $0) })
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+
+        return []
+    }
+}

+ 97 - 52
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -38,7 +38,7 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var account = ""
     @objc dynamic var assetLocalIdentifier = ""
     @objc dynamic var checksums = ""
-    @objc dynamic var chunk: Bool = false
+    @objc dynamic var chunk: Int = 0
     @objc dynamic var classFile = ""
     @objc dynamic var commentsUnread: Bool = false
     @objc dynamic var contentType = ""
@@ -100,6 +100,10 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var urlBase = ""
     @objc dynamic var user = ""
     @objc dynamic var userId = ""
+    @objc dynamic var latitude: Double = 0
+    @objc dynamic var longitude: Double = 0
+    @objc dynamic var height: Int = 0
+    @objc dynamic var width: Int = 0
 
     override static func primaryKey() -> String {
         return "ocId"
@@ -211,10 +215,18 @@ extension tableMetadata {
         return classFile == NKCommon.TypeClassFile.document.rawValue && editors.contains(NCGlobal.shared.editorText) && ((editors.contains(NCGlobal.shared.editorOnlyoffice) || isRichDocument))
     }
 
-    var isDownloadUpload: Bool {
+    var isWaitingTransfer: Bool {
+        status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploadError
+    }
+
+    var isInTransfer: Bool {
         status == NCGlobal.shared.metadataStatusInDownload || status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusInUpload || status == NCGlobal.shared.metadataStatusUploading
     }
 
+    var isTransferInForeground: Bool {
+        (status > 0 && (chunk > 0 || e2eEncrypted))
+    }
+
     var isDownload: Bool {
         status == NCGlobal.shared.metadataStatusInDownload || status == NCGlobal.shared.metadataStatusDownloading
     }
@@ -224,7 +236,11 @@ extension tableMetadata {
     }
 
     @objc var isDirectoryE2EE: Bool {
-        NCUtility.shared.isDirectoryE2EE(serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId)
+        NCUtility.shared.isDirectoryE2EE(account: account, urlBase: urlBase, userId: userId, serverUrl: serverUrl)
+    }
+
+    var isDirectoryE2EETop: Bool {
+        NCUtility.shared.isDirectoryE2EETop(account: account, serverUrl: serverUrl)
     }
 
     /// Returns false if the user is lokced out of the file. I.e. The file is locked but by somone else
@@ -232,19 +248,14 @@ extension tableMetadata {
         return !lock || (lockOwner == user && lockOwnerType == 0)
     }
 
-    // Return if is sharable (temp)
-    // TODO: modifify for E2EE 2.0
+    // Return if is sharable
     func isSharable() -> Bool {
         guard NCGlobal.shared.capabilityFileSharingApiEnabled else { return false }
-
-        if !e2eEncrypted && !isDirectoryE2EE {
+        if isDirectoryE2EE || e2eEncrypted {
+            guard directory, NCGlobal.shared.capabilityE2EEEnabled else { return false }
             return true
-        } else if NCGlobal.shared.capabilityServerVersionMajor >= NCGlobal.shared.nextcloudVersion26 && directory {
-            // E2EE DIRECTORY SECURE FILE DROP (SHARE AVAILABLE)
-            return true
-        } else {
-            return false
         }
+        return true
     }
 }
 
@@ -319,6 +330,10 @@ extension NCManageDatabase {
         metadata.urlBase = file.urlBase
         metadata.user = file.user
         metadata.userId = file.userId
+        metadata.latitude = file.latitude
+        metadata.longitude = file.longitude
+        metadata.height = file.height
+        metadata.width = file.width
 
         // E2EE find the fileName for fileNameView
         if isDirectoryE2EE || file.e2eEncrypted {
@@ -416,7 +431,6 @@ extension NCManageDatabase {
         let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
 
         metadata.account = account
-        metadata.chunk = false
         metadata.creationDate = Date() as NSDate
         metadata.date = Date() as NSDate
         metadata.hasPreview = true
@@ -518,7 +532,7 @@ extension NCManageDatabase {
     }
 
     @discardableResult
-    func updateMetadatas(_ metadatas: [tableMetadata], metadatasResult: [tableMetadata], addCompareLivePhoto: Bool = true, addExistsInLocal: Bool = false, addCompareEtagLocal: Bool = false, addDirectorySynchronized: Bool = false) -> (metadatasUpdate: [tableMetadata], metadatasLocalUpdate: [tableMetadata], metadatasDelete: [tableMetadata]) {
+    func updateMetadatas(_ metadatas: [tableMetadata], metadatasResult: [tableMetadata], addCompareLivePhoto: Bool = true, addExistsInLocal: Bool = false, addCompareEtagLocal: Bool = false) -> (metadatasUpdate: [tableMetadata], metadatasLocalUpdate: [tableMetadata], metadatasDelete: [tableMetadata]) {
 
         var ocIdsUdate: [String] = []
         var ocIdsLocalUdate: [String] = []
@@ -617,28 +631,29 @@ extension NCManageDatabase {
         do {
             let realm = try Realm()
             try realm.write {
-                let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
-                if let newFileName = newFileName {
-                    result?.fileName = newFileName
-                    result?.fileNameView = newFileName
-                }
-                if let session = session {
-                    result?.session = session
-                }
-                if let sessionError = sessionError {
-                    result?.sessionError = sessionError
-                }
-                if let sessionSelector = sessionSelector {
-                    result?.sessionSelector = sessionSelector
-                }
-                if let sessionTaskIdentifier = sessionTaskIdentifier {
-                    result?.sessionTaskIdentifier = sessionTaskIdentifier
-                }
-                if let status = status {
-                    result?.status = status
-                }
-                if let etag = etag {
-                    result?.etag = etag
+                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+                    if let newFileName = newFileName {
+                        result.fileName = newFileName
+                        result.fileNameView = newFileName
+                    }
+                    if let session = session {
+                        result.session = session
+                    }
+                    if let sessionError = sessionError {
+                        result.sessionError = sessionError
+                    }
+                    if let sessionSelector = sessionSelector {
+                        result.sessionSelector = sessionSelector
+                    }
+                    if let sessionTaskIdentifier = sessionTaskIdentifier {
+                        result.sessionTaskIdentifier = sessionTaskIdentifier
+                    }
+                    if let status = status {
+                        result.status = status
+                    }
+                    if let etag = etag {
+                        result.etag = etag
+                    }
                 }
             }
         } catch let error {
@@ -744,10 +759,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -757,10 +773,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -773,6 +790,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             if (tableMetadata().objectSchema.properties.contains { $0.name == sorted }) {
                 results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             } else {
@@ -797,7 +815,7 @@ extension NCManageDatabase {
                 }
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         if finals.isEmpty {
@@ -811,10 +829,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableMetadata.self).filter(predicate)
             return Array(results.map { tableMetadata.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -826,6 +845,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             if !results.isEmpty {
                 if page == 0 || limit == 0 {
@@ -842,7 +862,7 @@ extension NCManageDatabase {
                 }
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return metadatas
@@ -852,6 +872,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             if results.isEmpty {
                 return nil
@@ -859,7 +880,7 @@ extension NCManageDatabase {
                 return tableMetadata.init(value: results[index])
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -867,27 +888,42 @@ extension NCManageDatabase {
 
     func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
 
+        guard let ocId else { return nil }
         do {
             let realm = try Realm()
-            guard let ocId = ocId else { return nil }
+            realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
     }
 
+    func getTableMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
+
+        guard let ocId else { return nil }
+        do {
+            let realm = try Realm()
+            realm.refresh()
+            return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
+        }
+        return nil
+    }
+
     func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let fileId = fileId else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -911,10 +947,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -927,13 +964,14 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableMetadata.self).filter("account == %@ AND directory == true AND favorite == true", account).sorted(byKeyPath: "fileNameView", ascending: true)
             for result in results {
                 counter += 1
                 listIdentifierRank[result.ocId] = NSNumber(value: Int64(counter))
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return listIdentifierRank
@@ -973,12 +1011,13 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != '' AND deleteAssetLocalIdentifier == true", account)
             for result in results {
                 assetLocalIdentifiers.append(result.assetLocalIdentifier)
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return assetLocalIdentifiers
@@ -988,6 +1027,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             try realm.write {
                 let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier IN %@", account, assetLocalIdentifiers)
                 for result in results {
@@ -1019,10 +1059,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView CONTAINS[cd] %@ AND ocId != %@ AND classFile == %@", metadata.account, metadata.serverUrl, fileName, metadata.ocId, classFile)).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -1034,6 +1075,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             try realm.write {
                 let sortProperties = [SortDescriptor(keyPath: "serverUrl", ascending: false), SortDescriptor(keyPath: "fileNameView", ascending: false)]
                 let results = realm.objects(tableMetadata.self).filter(predicate).sorted(by: sortProperties)
@@ -1118,9 +1160,10 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             return realm.objects(tableMetadata.self).filter(NSPredicate(format: "status == %i || status == %i", NCGlobal.shared.metadataStatusInUpload, NCGlobal.shared.metadataStatusUploading)).count
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return 0
@@ -1130,11 +1173,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first else { return nil }
             return tableMetadata.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -1147,6 +1191,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let groupfolders = realm.objects(TableGroupfolders.self).filter("account == %@", account)
             for groupfolder in groupfolders {
                 let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
@@ -1157,7 +1202,7 @@ extension NCManageDatabase {
                 }
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return metadatas

+ 23 - 8
iOSClient/Data/NCManageDatabase+Share.swift

@@ -81,8 +81,6 @@ extension NCManageDatabase {
         do {
             let realm = try Realm()
             try realm.write {
-                let result = realm.objects(tableShare.self).filter("account == %@", account)
-                realm.delete(result)
                 for share in shares {
                     let serverUrlPath = home + share.path
                     guard let serverUrl = NCUtilityFileSystem.shared.deleteLastPath(serverUrlPath: serverUrlPath, home: home) else { continue }
@@ -141,11 +139,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sortProperties = [SortDescriptor(keyPath: "shareType", ascending: false), SortDescriptor(keyPath: "idShare", ascending: false)]
             let results = realm.objects(tableShare.self).filter("account == %@", account).sorted(by: sortProperties)
             return Array(results.map { tableShare.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -155,6 +154,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sortProperties = [SortDescriptor(keyPath: "shareType", ascending: false), SortDescriptor(keyPath: "idShare", ascending: false)]
             let firstShareLink = realm.objects(tableShare.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@ AND shareType == 3", metadata.account, metadata.serverUrl, metadata.fileName).first
             if let firstShareLink = firstShareLink {
@@ -165,7 +165,7 @@ extension NCManageDatabase {
                 return(firstShareLink: firstShareLink, share: Array(results.map { tableShare.init(value: $0) }))
             }
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return (nil, nil)
@@ -175,10 +175,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableShare.self).filter("account = %@ AND idShare = %d", account, idShare).first else { return nil }
             return tableShare.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil
@@ -188,11 +189,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sortProperties = [SortDescriptor(keyPath: "shareType", ascending: false), SortDescriptor(keyPath: "idShare", ascending: false)]
             let results = realm.objects(tableShare.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).sorted(by: sortProperties)
             return Array(results.map { tableShare.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -202,11 +204,12 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let sortProperties = [SortDescriptor(keyPath: "shareType", ascending: false), SortDescriptor(keyPath: "idShare", ascending: false)]
             let results = realm.objects(tableShare.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).sorted(by: sortProperties)
             return Array(results.map { tableShare.init(value: $0) })
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return []
@@ -225,11 +228,23 @@ extension NCManageDatabase {
         }
     }
 
-    func deleteTableShare(account: String) {
+    func deleteTableShare(account: String, path: String) {
 
         do {
             let realm = try Realm()
+            try realm.write {
+                let result = realm.objects(tableShare.self).filter("account == %@ AND path == %@", account, path)
+                realm.delete(result)
+            }
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+    }
 
+    func deleteTableShare(account: String) {
+
+        do {
+            let realm = try Realm()
             try realm.write {
                 let result = realm.objects(tableShare.self).filter("account == %@", account)
                 realm.delete(result)

+ 2 - 1
iOSClient/Data/NCManageDatabase+Video.swift

@@ -140,10 +140,11 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { return nil }
             return tableVideo.init(value: result)
         } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
 
         return nil

+ 54 - 260
iOSClient/Data/NCManageDatabase.swift

@@ -34,6 +34,8 @@ class NCManageDatabase: NSObject {
         return instance
     }()
 
+    let serialQueue = DispatchQueue(label: "realmSerialQueue")
+
     override init() {
 
         let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroups)
@@ -69,7 +71,26 @@ class NCManageDatabase: NSObject {
             let config = Realm.Configuration(
                 fileURL: dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + databaseName),
                 schemaVersion: databaseSchemaVersion,
-                objectTypes: [tableMetadata.self, tableLocalFile.self, tableDirectory.self, tableTag.self, tableAccount.self, tableCapabilities.self, tablePhotoLibrary.self, tableE2eEncryption.self, tableE2eEncryptionLock.self, tableE2eMetadata.self, tableShare.self, tableChunk.self, tableAvatar.self, tableDashboardWidget.self, tableDashboardWidgetButton.self, NCDBLayoutForView.self]
+                objectTypes: [tableMetadata.self,
+                              tableLocalFile.self,
+                              tableDirectory.self,
+                              tableTag.self,
+                              tableAccount.self,
+                              tableCapabilities.self,
+                              tablePhotoLibrary.self,
+                              tableE2eEncryption.self,
+                              tableE2eEncryptionLock.self,
+                              tableE2eMetadata12.self,
+                              tableE2eMetadata.self,
+                              tableE2eUsers.self,
+                              tableE2eCounter.self,
+                              tableE2eUsersFiledrop.self,
+                              tableShare.self,
+                              tableChunk.self,
+                              tableAvatar.self,
+                              tableDashboardWidget.self,
+                              tableDashboardWidgetButton.self,
+                              NCDBLayoutForView.self]
             )
 
             Realm.Configuration.defaultConfiguration = config
@@ -98,6 +119,13 @@ class NCManageDatabase: NSObject {
                         migration.deleteData(forType: tableVideo.className())
                     }
 
+                    if oldSchemaVersion < 306 {
+                        migration.deleteData(forType: tableChunk.className())
+                        migration.deleteData(forType: tableMetadata.className())
+                        migration.deleteData(forType: tableDirectory.className())
+                        migration.deleteData(forType: tableE2eEncryptionLock.className())
+                    }
+
                 }, shouldCompactOnLaunch: { totalBytes, usedBytes in
 
                     // totalBytes refers to the size of the file on disk in bytes (data + free space)
@@ -194,9 +222,6 @@ class NCManageDatabase: NSObject {
         self.clearTable(tableDirectEditingCreators.self, account: account)
         self.clearTable(tableDirectEditingEditors.self, account: account)
         self.clearTable(tableDirectory.self, account: account)
-        self.clearTable(tableE2eEncryption.self, account: account)
-        self.clearTable(tableE2eEncryptionLock.self, account: account)
-        self.clearTable(tableE2eMetadata.self, account: account)
         self.clearTable(tableExternalSites.self, account: account)
         self.clearTable(tableGPS.self, account: nil)
         self.clearTable(TableGroupfolders.self, account: account)
@@ -211,12 +236,24 @@ class NCManageDatabase: NSObject {
         self.clearTable(tableTrash.self, account: account)
         self.clearTable(tableUserStatus.self, account: account)
         self.clearTable(tableVideo.self, account: account)
+        self.clearTablesE2EE(account: account)
 
         if removeAccount {
             self.clearTable(tableAccount.self, account: account)
         }
     }
 
+    func clearTablesE2EE(account: String?) {
+
+        self.clearTable(tableE2eEncryption.self, account: account)
+        self.clearTable(tableE2eEncryptionLock.self, account: account)
+        self.clearTable(tableE2eMetadata12.self, account: account)
+        self.clearTable(tableE2eMetadata.self, account: account)
+        self.clearTable(tableE2eUsers.self, account: account)
+        self.clearTable(tableE2eCounter.self, account: account)
+        self.clearTable(tableE2eUsersFiledrop.self, account: account)
+    }
+
     @objc func removeDB() {
 
         let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
@@ -257,108 +294,6 @@ class NCManageDatabase: NSObject {
         return object.isInvalidated
     }
 
-    // MARK: -
-    // MARK: Table Chunk
-
-    func getChunkFolder(account: String, ocId: String) -> String {
-
-        do {
-            let realm = try Realm()
-            guard let result = realm.objects(tableChunk.self).filter("account == %@ AND ocId == %@", account, ocId).first else { return NSUUID().uuidString }
-            return result.chunkFolder
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-
-        return NSUUID().uuidString
-    }
-
-    func getChunks(account: String, ocId: String) -> [String] {
-
-        var filesNames: [String] = []
-
-        do {
-            let realm = try Realm()
-            let results = realm.objects(tableChunk.self).filter("account == %@ AND ocId == %@", account, ocId).sorted(byKeyPath: "fileName", ascending: true)
-            for result in results {
-                filesNames.append(result.fileName)
-            }
-            return filesNames
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-
-        return filesNames
-    }
-
-    func addChunks(account: String, ocId: String, chunkFolder: String, fileNames: [String]) {
-
-        var size: Int64 = 0
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-
-                for fileName in fileNames {
-
-                    let object = tableChunk()
-                    size += NCUtilityFileSystem.shared.getFileSize(filePath: CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!)
-
-                    object.account = account
-                    object.chunkFolder = chunkFolder
-                    object.fileName = fileName
-                    object.index = ocId + fileName
-                    object.ocId = ocId
-                    object.size = size
-
-                    realm.add(object, update: .all)
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    func getChunk(account: String, fileName: String) -> tableChunk? {
-
-        do {
-            let realm = try Realm()
-            guard let result = realm.objects(tableChunk.self).filter("account == %@ AND fileName == %@", account, fileName).first else { return nil }
-            return tableChunk.init(value: result)
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-
-        return nil
-    }
-
-    func deleteChunk(account: String, ocId: String, fileName: String) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let result = realm.objects(tableChunk.self).filter(NSPredicate(format: "account == %@ AND ocId == %@ AND fileName == %@", account, ocId, fileName))
-                realm.delete(result)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    func deleteChunks(account: String, ocId: String) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-
-                let result = realm.objects(tableChunk.self).filter(NSPredicate(format: "account == %@ AND ocId == %@", account, ocId))
-                realm.delete(result)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
     // MARK: -
     // MARK: Table Direct Editing
 
@@ -420,6 +355,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableDirectEditingCreators.self).filter("account == %@", account)
             if results.isEmpty {
                 return nil
@@ -437,6 +373,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableDirectEditingCreators.self).filter(predicate)
             if results.isEmpty {
                 return nil
@@ -454,6 +391,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableDirectEditingEditors.self).filter("account == %@", account)
             if results.isEmpty {
                 return nil
@@ -509,6 +447,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableExternalSites.self).filter("account == %@", account).sorted(byKeyPath: "idExternalSite", ascending: true)
             if results.isEmpty {
                 return nil
@@ -525,21 +464,17 @@ class NCManageDatabase: NSObject {
     // MARK: -
     // MARK: Table GPS
 
-    @objc func addGeocoderLocation(_ location: String, placemarkAdministrativeArea: String, placemarkCountry: String, placemarkLocality: String, placemarkPostalCode: String, placemarkThoroughfare: String, latitude: String, longitude: String) {
+    @objc func addGeocoderLocation(_ location: String, latitude: Double, longitude: Double) {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard realm.objects(tableGPS.self).filter("latitude == %@ AND longitude == %@", latitude, longitude).first == nil else { return }
             try realm.write {
                 let addObject = tableGPS()
                 addObject.latitude = latitude
                 addObject.location = location
                 addObject.longitude = longitude
-                addObject.placemarkAdministrativeArea = placemarkAdministrativeArea
-                addObject.placemarkCountry = placemarkCountry
-                addObject.placemarkLocality = placemarkLocality
-                addObject.placemarkPostalCode = placemarkPostalCode
-                addObject.placemarkThoroughfare = placemarkThoroughfare
                 realm.add(addObject)
             }
         } catch let error as NSError {
@@ -547,10 +482,10 @@ class NCManageDatabase: NSObject {
         }
     }
 
-    @objc func getLocationFromGeoLatitude(_ latitude: String, longitude: String) -> String? {
-
+    @objc func getLocationFromLatAndLong(latitude: Double, longitude: Double) -> String? {
         do {
             let realm = try Realm()
+            realm.refresh()
             let result = realm.objects(tableGPS.self).filter("latitude == %@ AND longitude == %@", latitude, longitude).first
             return result?.location
         } catch let error as NSError {
@@ -560,152 +495,6 @@ class NCManageDatabase: NSObject {
         return nil
     }
 
-    // MARK: -
-    // MARK: Table LocalFile
-
-    func addLocalFile(metadata: tableMetadata) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let addObject = getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) ?? tableLocalFile()
-                addObject.account = metadata.account
-                addObject.etag = metadata.etag
-                addObject.exifDate = NSDate()
-                addObject.exifLatitude = "-1"
-                addObject.exifLongitude = "-1"
-                addObject.ocId = metadata.ocId
-                addObject.fileName = metadata.fileName
-                realm.add(addObject, update: .all)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    func addLocalFile(account: String, etag: String, ocId: String, fileName: String) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let addObject = tableLocalFile()
-                addObject.account = account
-                addObject.etag = etag
-                addObject.exifDate = NSDate()
-                addObject.exifLatitude = "-1"
-                addObject.exifLongitude = "-1"
-                addObject.ocId = ocId
-                addObject.fileName = fileName
-                realm.add(addObject, update: .all)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    func deleteLocalFile(predicate: NSPredicate) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let results = realm.objects(tableLocalFile.self).filter(predicate)
-                realm.delete(results)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    func setLocalFile(ocId: String, fileName: String?, etag: String?) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let result = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first
-                if let fileName = fileName {
-                    result?.fileName = fileName
-                }
-                if let etag = etag {
-                    result?.etag = etag
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    @objc func setLocalFile(ocId: String, exifDate: NSDate?, exifLatitude: String, exifLongitude: String, exifLensModel: String?) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                if let result = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first {
-                    result.exifDate = exifDate
-                    result.exifLatitude = exifLatitude
-                    result.exifLongitude = exifLongitude
-                    if exifLensModel?.count ?? 0 > 0 {
-                        result.exifLensModel = exifLensModel
-                    }
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    func getTableLocalFile(account: String) -> [tableLocalFile] {
-
-        do {
-            let realm = try Realm()
-            let results = realm.objects(tableLocalFile.self).filter("account == %@", account)
-            return Array(results.map { tableLocalFile.init(value: $0) })
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-
-        return []
-    }
-
-    func getTableLocalFile(predicate: NSPredicate) -> tableLocalFile? {
-
-        do {
-            let realm = try Realm()
-            guard let result = realm.objects(tableLocalFile.self).filter(predicate).first else { return nil }
-            return tableLocalFile.init(value: result)
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-
-        return nil
-    }
-
-    func getTableLocalFiles(predicate: NSPredicate, sorted: String, ascending: Bool) -> [tableLocalFile] {
-
-        do {
-            let realm = try Realm()
-            let results = realm.objects(tableLocalFile.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
-            return Array(results.map { tableLocalFile.init(value: $0) })
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-
-        return []
-    }
-
-    func setLocalFile(ocId: String, offline: Bool) {
-
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let result = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId).first
-                result?.offline = offline
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
-        }
-    }
-
-    // MARK: -
     // MARK: Table Photo Library
 
     @discardableResult
@@ -753,6 +542,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tablePhotoLibrary.self).filter(predicate)
             let idsAsset = results.map { $0.idAsset }
             return Array(idsAsset)
@@ -799,6 +589,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableTag.self).filter(predicate)
             return Array(results.map { tableTag.init(value: $0) })
         } catch let error as NSError {
@@ -812,6 +603,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableTag.self).filter(predicate).first else { return nil }
             return tableTag.init(value: result)
         } catch let error as NSError {
@@ -933,6 +725,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             let results = realm.objects(tableTrash.self).filter("account == %@ AND filePath == %@", account, filePath).sorted(byKeyPath: sort, ascending: ascending)
             return Array(results.map { tableTrash.init(value: $0) })
         } catch let error as NSError {
@@ -946,6 +739,7 @@ class NCManageDatabase: NSObject {
 
         do {
             let realm = try Realm()
+            realm.refresh()
             guard let result = realm.objects(tableTrash.self).filter("account == %@ AND fileId == %@", account, fileId).first else { return nil }
             return tableTrash.init(value: result)
         } catch let error as NSError {

+ 0 - 1
iOSClient/EmptyView/NCEmptyDataSet.swift

@@ -45,7 +45,6 @@ class NCEmptyDataSet: NSObject {
     private var centerXAnchor: NSLayoutConstraint?
     private var centerYAnchor: NSLayoutConstraint?
 
-
     init(view: UIView, offset: CGFloat = 0, delegate: NCEmptyDataSetDelegate?) {
         super.init()
 

+ 9 - 3
iOSClient/Extensions/UIAlertController+Extension.swift

@@ -38,9 +38,15 @@ extension UIAlertController {
             guard let fileNameFolder = alertController.textFields?.first?.text else { return }
             if markE2ee {
                 Task {
-                    let error = await NCNetworkingE2EECreateFolder.shared.createFolderAndMarkE2EE(fileName: fileNameFolder, serverUrl: serverUrl, account: urlBase.account)
-                    if error != .success {
-                        NCContentPresenter.shared.showError(error: error)
+
+                    let createFolderResults = await NextcloudKit.shared.createFolder(serverUrlFileName: serverUrl + "/" + fileNameFolder)
+                    if createFolderResults.error == .success {
+                        let error = await NCNetworkingE2EEMarkFolder().markFolderE2ee(account: urlBase.account, fileName: fileNameFolder, serverUrl: serverUrl, userId: urlBase.userId)
+                        if error != .success {
+                            NCContentPresenter.shared.showError(error: error)
+                        }
+                    } else {
+                        NCContentPresenter.shared.showError(error: createFolderResults.error)
                     }
                 }
             } else {

+ 13 - 0
iOSClient/Extensions/UIDevice+Extension.swift

@@ -36,3 +36,16 @@ extension UIDevice {
         }
     }
 }
+
+extension UIDeviceOrientation {
+    /// According to Apple... if the device is laid flat the UI is neither portrait nor landscape, so this flag ignores that and checks if the UI is REALLY in landscape. Thanks Apple.
+    /// 
+    /// Unless you really need to use this, you can instead try `traitCollection.verticalSizeClass` and `traitCollection.horizontalSizeClass`.
+    var isLandscapeHardCheck: Bool {
+        if UIDevice.current.orientation.isValidInterfaceOrientation {
+            return UIDevice.current.orientation.isLandscape
+        } else {
+            return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape ?? false
+        }
+    }
+}

+ 1 - 1
iOSClient/Extensions/UINavigationController+Extension.swift

@@ -51,7 +51,7 @@ extension UINavigationController {
         navigationBar.scrollEdgeAppearance = scrollEdgeAppearance
     }
 
-    func setGroupeAppreance() {
+    func setGroupAppearance() {
 
         navigationBar.tintColor = .systemBlue
 

+ 2 - 2
iOSClient/Extensions/UIToolbar+Extension.swift

@@ -24,7 +24,7 @@
 import UIKit
 
 extension UIToolbar {
-    static func toolbar(onClear: (() -> Void)?, completion: @escaping () -> Void) -> UIToolbar {
+    static func toolbar(onClear: (() -> Void)?, onDone: @escaping () -> Void) -> UIToolbar {
         let toolbar = UIToolbar()
         toolbar.sizeToFit()
         var buttons: [UIBarButtonItem] = []
@@ -37,7 +37,7 @@ extension UIToolbar {
         }
         buttons.append(UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil))
         let doneButton = UIBarButtonItem(title: NSLocalizedString("_done_", comment: ""), style: .done) {
-            completion()
+            onDone()
         }
         buttons.append(doneButton)
         toolbar.setItems(buttons, animated: false)

+ 34 - 51
iOSClient/Favorites/NCFavorite.swift

@@ -49,29 +49,33 @@ class NCFavorite: NCCollectionViewCommon {
 
     // MARK: - DataSource + NC Endpoint
 
-    override func reloadDataSource(forced: Bool = true) {
-        super.reloadDataSource()
+    override func queryDB(isForced: Bool) {
 
-        DispatchQueue.global().async {
-            var metadatas: [tableMetadata] = []
+        var metadatas: [tableMetadata] = []
 
-            if self.serverUrl.isEmpty {
-                metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account))
-            } else {
-                metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
-            }
+        if self.serverUrl.isEmpty {
+            metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account))
+        } else {
+            metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+        }
 
-            self.dataSource = NCDataSource(metadatas: metadatas,
-                                           account: self.appDelegate.account,
-                                           sort: self.layoutForView?.sort,
-                                           ascending: self.layoutForView?.ascending,
-                                           directoryOnTop: self.layoutForView?.directoryOnTop,
-                                           favoriteOnTop: true,
-                                           filterLivePhoto: true,
-                                           groupByField: self.groupByField,
-                                           providers: self.providers,
-                                           searchResults: self.searchResults)
+        self.dataSource = NCDataSource(metadatas: metadatas,
+                                       account: self.appDelegate.account,
+                                       sort: self.layoutForView?.sort,
+                                       ascending: self.layoutForView?.ascending,
+                                       directoryOnTop: self.layoutForView?.directoryOnTop,
+                                       favoriteOnTop: true,
+                                       filterLivePhoto: true,
+                                       groupByField: self.groupByField,
+                                       providers: self.providers,
+                                       searchResults: self.searchResults)
+    }
+
+    override func reloadDataSource(isForced: Bool = true) {
+        super.reloadDataSource()
 
+        DispatchQueue.global().async {
+            self.queryDB(isForced: isForced)
             DispatchQueue.main.async {
                 self.refreshControl.endRefreshing()
                 self.collectionView.reloadData()
@@ -79,46 +83,25 @@ class NCFavorite: NCCollectionViewCommon {
         }
     }
 
-    override func reloadDataSourceNetwork(forced: Bool = false) {
-        super.reloadDataSourceNetwork(forced: forced)
+    override func reloadDataSourceNetwork(isForced: Bool = false) {
+        super.reloadDataSourceNetwork(isForced: isForced)
+
+        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Reload data source network favorite forced \(isForced)")
 
         isReloadDataSourceNetworkInProgress = true
         collectionView?.reloadData()
 
-        if serverUrl.isEmpty {
+        NextcloudKit.shared.listingFavorites(showHiddenFiles: CCUtility.getShowHiddenFiles(),
+                                             options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, files, _, error in
 
-            NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorListingFavorite) { _, _, error in
-                if error != .success {
-                    NCContentPresenter.shared.showError(error: error)
-                }
-
-                DispatchQueue.main.async {
-                    self.refreshControl.endRefreshing()
-                    self.isReloadDataSourceNetworkInProgress = false
+            self.isReloadDataSourceNetworkInProgress = false
+            if error == .success {
+                NCManageDatabase.shared.convertFilesToMetadatas(files, useMetadataFolder: false) { _, _, metadatas in
+                    NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas)
                     self.reloadDataSource()
                 }
             }
-
-        } else {
-
-            networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, error in
-                if error == .success, let metadatas = metadatas {
-                    for metadata in metadatas where (!metadata.directory && NCManageDatabase.shared.isDownloadMetadata(metadata, download: false)) {
-                        NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
-                    }
-                }
-
-                DispatchQueue.main.async {
-                    self.refreshControl.endRefreshing()
-                    self.isReloadDataSourceNetworkInProgress = false
-                    self.richWorkspaceText = tableDirectory?.richWorkspace
-                    if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
-                        self.reloadDataSource()
-                    } else {
-                        self.collectionView?.reloadData()
-                    }
-                }
-            }
+            self.reloadDataSource()
         }
     }
 }

+ 70 - 44
iOSClient/Files/NCFiles.swift

@@ -40,11 +40,45 @@ class NCFiles: NCCollectionViewCommon {
         enableSearchBar = true
         headerMenuButtonsView = true
         headerRichWorkspaceDisable = false
+        headerMenuTransferView = true
         emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = "_no_file_pull_down_"
     }
 
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        if isRoot {
+            NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { _ in
+
+                self.navigationController?.popToRootViewController(animated: false)
+
+                self.serverUrl = NCUtilityFileSystem.shared.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
+                self.appDelegate.activeServerUrl = self.serverUrl
+
+                self.isSearchingMode = false
+                self.isEditMode = false
+                self.selectOcId.removeAll()
+                self.selectIndexPath.removeAll()
+
+                self.layoutForView = NCManageDatabase.shared.getLayoutForView(account: self.appDelegate.account, key: self.layoutKey, serverUrl: self.serverUrl)
+                self.gridLayout.itemForLine = CGFloat(self.layoutForView?.itemForLine ?? 3)
+                if self.layoutForView?.layout == NCGlobal.shared.layoutList {
+                    self.collectionView?.collectionViewLayout = self.listLayout
+                } else {
+                    self.collectionView?.collectionViewLayout = self.gridLayout
+                }
+
+                self.titleCurrentFolder = self.getNavigationTitle()
+                self.setNavigationItem()
+
+                self.reloadDataSource(isForced: false)
+                self.reloadDataSourceNetwork()
+            }
+        }
+    }
+
     override func viewWillAppear(_ animated: Bool) {
 
         if isRoot {
@@ -52,7 +86,6 @@ class NCFiles: NCCollectionViewCommon {
             titleCurrentFolder = getNavigationTitle()
         }
         super.viewWillAppear(animated)
-
         navigationController?.setFileAppreance()
     }
 
@@ -63,57 +96,47 @@ class NCFiles: NCCollectionViewCommon {
         fileNameOpen = nil
     }
 
-    // MARK: - NotificationCenter
+    // MARK: - DataSource + NC Endpoint
+    //
+    // forced: do no make the etag of directory test (default)
+    //
 
-    override func initialize() {
+    override func queryDB(isForced: Bool) {
 
-        if isRoot {
-            serverUrl = NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
-            titleCurrentFolder = getNavigationTitle()
+        let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+        let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+        let metadataTransfer = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "status != %i AND serverUrl == %@", NCGlobal.shared.metadataStatusNormal, self.serverUrl))
+        if self.metadataFolder == nil {
+            self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId, serverUrl: self.serverUrl)
         }
-        super.initialize()
 
-        reloadDataSource(forced: false)
-        reloadDataSourceNetwork()
-    }
+        if !isForced, let directory, directory.etag == self.dataSource.directory?.etag, metadataTransfer == nil, self.fileNameBlink == nil, self.fileNameOpen == nil {
+            return
+        }
 
-    // MARK: - DataSource + NC Endpoint
-    //
-    // forced: do no make the etag of directory test (default)
-    //
+        self.richWorkspaceText = directory?.richWorkspace
+        self.dataSource = NCDataSource(
+            metadatas: metadatas,
+            account: self.appDelegate.account,
+            directory: directory,
+            sort: self.layoutForView?.sort,
+            ascending: self.layoutForView?.ascending,
+            directoryOnTop: self.layoutForView?.directoryOnTop,
+            favoriteOnTop: true,
+            filterLivePhoto: true,
+            groupByField: self.groupByField,
+            providers: self.providers,
+            searchResults: self.searchResults)
+    }
 
-    override func reloadDataSource(forced: Bool = true) {
+    override func reloadDataSource(isForced: Bool = true) {
         super.reloadDataSource()
 
         DispatchQueue.main.async { self.refreshControl.endRefreshing() }
         DispatchQueue.global().async {
             guard !self.isSearchingMode, !self.appDelegate.account.isEmpty, !self.appDelegate.urlBase.isEmpty, !self.serverUrl.isEmpty else { return }
 
-            let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
-            if self.metadataFolder == nil {
-                self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId, serverUrl: self.serverUrl)
-            }
-            let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
-            let metadataTransfer = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "status != %i AND serverUrl == %@", NCGlobal.shared.metadataStatusNormal, self.serverUrl))
-            self.richWorkspaceText = directory?.richWorkspace
-
-            // FORCED false: test the directory.etag
-            if !forced, let directory = directory, directory.etag == self.dataSource.directory?.etag, metadataTransfer == nil, self.fileNameBlink == nil, self.fileNameOpen == nil {
-                return
-            }
-
-            self.dataSource = NCDataSource(
-                metadatas: metadatas,
-                account: self.appDelegate.account,
-                directory: directory,
-                sort: self.layoutForView?.sort,
-                ascending: self.layoutForView?.ascending,
-                directoryOnTop: self.layoutForView?.directoryOnTop,
-                favoriteOnTop: true,
-                filterLivePhoto: true,
-                groupByField: self.groupByField,
-                providers: self.providers,
-                searchResults: self.searchResults)
+            self.queryDB(isForced: isForced)
 
             DispatchQueue.main.async {
                 self.collectionView.reloadData()
@@ -127,16 +150,19 @@ class NCFiles: NCCollectionViewCommon {
         }
     }
 
-    override func reloadDataSourceNetwork(forced: Bool = false) {
-        super.reloadDataSourceNetwork(forced: forced)
+    override func reloadDataSourceNetwork(isForced: Bool = false) {
+        super.reloadDataSourceNetwork(isForced: isForced)
         guard !isSearchingMode else {
             networkSearch()
             return
         }
+
+        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Reload data source network files forced \(isForced)")
+
         isReloadDataSourceNetworkInProgress = true
         collectionView?.reloadData()
 
-        networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, error in
+        networkReadFolder(isForced: isForced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, error in
             if error == .success {
                 for metadata in metadatas ?? [] where !metadata.directory && NCManageDatabase.shared.isDownloadMetadata(metadata, download: false) {
                     NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
@@ -146,7 +172,7 @@ class NCFiles: NCCollectionViewCommon {
             self.isReloadDataSourceNetworkInProgress = false
             self.richWorkspaceText = tableDirectory?.richWorkspace
 
-            if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+            if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || isForced {
                 self.reloadDataSource()
             } else if self.dataSource.getMetadataSourceForAllSections().isEmpty {
                 DispatchQueue.main.async {

+ 28 - 35
iOSClient/Groupfolders/NCGroupfolders.swift

@@ -49,8 +49,7 @@ class NCGroupfolders: NCCollectionViewCommon {
 
     // MARK: - DataSource + NC Endpoint
 
-    override func reloadDataSource(forced: Bool = true) {
-        super.reloadDataSource()
+    override func queryDB(isForced: Bool) {
 
         var metadatas: [tableMetadata] = []
 
@@ -71,7 +70,12 @@ class NCGroupfolders: NCCollectionViewCommon {
             groupByField: self.groupByField,
             providers: self.providers,
             searchResults: self.searchResults)
+    }
+
+    override func reloadDataSource(isForced: Bool = true) {
+        super.reloadDataSource()
 
+        self.queryDB(isForced: isForced)
         DispatchQueue.main.async {
             self.isReloadDataSourceNetworkInProgress = false
             self.refreshControl.endRefreshing()
@@ -79,48 +83,37 @@ class NCGroupfolders: NCCollectionViewCommon {
         }
     }
 
-    override func reloadDataSourceNetwork(forced: Bool = false) {
-        super.reloadDataSourceNetwork(forced: forced)
+    override func reloadDataSourceNetwork(isForced: Bool = false) {
+        super.reloadDataSourceNetwork(isForced: isForced)
+
+        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Reload data source network groupfolders forced \(isForced)")
 
         isReloadDataSourceNetworkInProgress = true
         collectionView?.reloadData()
 
-        if serverUrl.isEmpty {
-
-            let homeServerUrl = NCUtilityFileSystem.shared.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
-            let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
-
-            NextcloudKit.shared.getGroupfolders(options: options) { account, results, _, error in
-
-                if error == .success, let groupfolders = results {
-
-                    NCManageDatabase.shared.addGroupfolders(account: account, groupfolders: groupfolders)
-
-                    Task {
-                        for groupfolder in groupfolders {
-                            let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
-                            let serverUrlFileName = homeServerUrl + mountPoint
-                            if NCManageDatabase.shared.getMetadataFromDirectory(account: self.appDelegate.account, serverUrl: serverUrlFileName) == nil {
-                                let results = await NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles())
-                                if results.error == .success, let file = results.files.first {
-                                    let isDirectoryE2EE = NCUtility.shared.isDirectoryE2EE(file: file)
-                                    let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
-                                    NCManageDatabase.shared.addMetadata(metadata)
-                                    NCManageDatabase.shared.addDirectory(encrypted: isDirectoryE2EE, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
-                                }
+        let homeServerUrl = NCUtilityFileSystem.shared.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
+
+        NextcloudKit.shared.getGroupfolders(options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, results, _, error in
+
+            if error == .success, let groupfolders = results {
+                NCManageDatabase.shared.addGroupfolders(account: account, groupfolders: groupfolders)
+                Task {
+                    for groupfolder in groupfolders {
+                        let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
+                        let serverUrlFileName = homeServerUrl + mountPoint
+                        if NCManageDatabase.shared.getMetadataFromDirectory(account: self.appDelegate.account, serverUrl: serverUrlFileName) == nil {
+                            let results = await NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles())
+                            if results.error == .success, let file = results.files.first {
+                                let isDirectoryE2EE = NCUtility.shared.isDirectoryE2EE(file: file)
+                                let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+                                NCManageDatabase.shared.addMetadata(metadata)
+                                NCManageDatabase.shared.addDirectory(encrypted: isDirectoryE2EE, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
                             }
                         }
-                        self.reloadDataSource()
                     }
-                } else if error != .success {
                     self.reloadDataSource()
-                    NCContentPresenter.shared.showError(error: error)
                 }
-            }
-        } else {
-
-            networkReadFolder(forced: forced) { _, _, _, _, _ in
-
+            } else if error != .success {
                 self.reloadDataSource()
             }
         }

+ 40 - 29
iOSClient/Login/NCLogin.swift

@@ -36,7 +36,10 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     @IBOutlet weak var qrCode: UIButton!
     @IBOutlet weak var certificate: UIButton!
 
+    // swiftlint:disable force_cast
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
     private var textColor: UIColor = .white
     private var textColorOpponent: UIColor = .black
     private var activeTextfieldDiff: CGFloat = 0
@@ -153,7 +156,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         if self.shareAccounts != nil, let image = UIImage(systemName: "person.badge.plus")?.withTintColor(.white, renderingMode: .alwaysOriginal), let backgroundColor = NCBrandColor.shared.brandElement.lighter(by: 10) {
             let title = String(format: NSLocalizedString("_apps_nextcloud_detect_", comment: ""), NCBrandOptions.shared.brand)
             let description = String(format: NSLocalizedString("_add_existing_account_", comment: ""), NCBrandOptions.shared.brand)
-            NCContentPresenter.shared.alertAction(image: image, contentModeImage: .scaleAspectFit, sizeImage: CGSize(width: 45, height: 45),backgroundColor: backgroundColor, textColor: textColor, title: title, description: description, textCancelButton: "_cancel_", textOkButton: "_ok_", attributes: EKAttributes.topFloat) { identifier in
+            NCContentPresenter.shared.alertAction(image: image, contentModeImage: .scaleAspectFit, sizeImage: CGSize(width: 45, height: 45), backgroundColor: backgroundColor, textColor: textColor, title: title, description: description, textCancelButton: "_cancel_", textOkButton: "_ok_", attributes: EKAttributes.topFloat) { identifier in
                 if identifier == "ok" {
                     self.openShareAccountsViewController()
                 }
@@ -213,7 +216,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
 
         guard var url = baseUrl.text?.trimmingCharacters(in: .whitespacesAndNewlines) else { return }
         if url.hasSuffix("/") { url = String(url.dropLast()) }
-        if url.count == 0 { return }
+        if url.isEmpty { return }
 
         // Check whether baseUrl contain protocol. If not add https:// by default.
         if url.hasPrefix("https") == false && url.hasPrefix("http") == false {
@@ -244,11 +247,11 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             vc.dismissDidEnterBackground = false
             vc.delegate = self
 
-            let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
+            let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height / 5)
             let numberCell = shareAccounts.count
             let height = min(CGFloat(numberCell * Int(vc.heightCell) + 45), screenHeighMax)
 
-            let popup = NCPopupViewController(contentController: vc, popupWidth: 300, popupHeight: height+20)
+            let popup = NCPopupViewController(contentController: vc, popupWidth: 300, popupHeight: height + 20)
 
             self.present(popup, animated: true)
         }
@@ -269,7 +272,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                     NCNetworking.shared.writeCertificate(host: host)
                 }
 
-                NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { token, endpoint, login, data, error in
+                NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { token, endpoint, login, _, error in
 
                     self.loginButton.isEnabled = true
 
@@ -327,8 +330,8 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in }))
 
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in
-                        if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController {
-                            let viewController = navigationController.topViewController as! NCViewCertificateDetails
+                        if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController,
+                           let viewController = navigationController.topViewController as? NCViewCertificateDetails {
                             if let host = URL(string: url)?.host {
                                 viewController.host = host
                             }
@@ -387,31 +390,41 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             if let host = URL(string: url)?.host {
                 NCNetworking.shared.writeCertificate(host: host)
             }
+            let urlBase = url
+            let account = user + " " + user
 
-            let account = user + " " + url
+            NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
+            NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
 
-            if NCManageDatabase.shared.getAccounts() == nil {
-                NCUtility.shared.removeAllSettings()
-            }
-                           
-            NCManageDatabase.shared.deleteAccount(account)
-            NCManageDatabase.shared.addAccount(account, urlBase: url, user: user, password: password)
+                if error == .success, let userProfile {
 
-            if let activeAccount = NCManageDatabase.shared.setAccountActive(account) {
-                appDelegate.settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account))
-            }
+                    if NCManageDatabase.shared.getAccounts() == nil {
+                        NCUtility.shared.removeAllSettings()
+                    }
 
-            if CCUtility.getIntro() {
-                self.dismiss(animated: true)
-            } else {
-                CCUtility.setIntro(true)
-                if self.presentingViewController == nil {
-                    let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
-                    viewController?.modalPresentationStyle = .fullScreen
-                    self.appDelegate.window?.rootViewController = viewController
-                    self.appDelegate.window?.makeKey()
+                    NCManageDatabase.shared.deleteAccount(account)
+                    NCManageDatabase.shared.addAccount(account, urlBase: url, user: user, userId: userProfile.userId, password: password)
+
+                    self.appDelegate.changeAccount(account, userProfile: userProfile)
+
+                    if CCUtility.getIntro() {
+                        self.dismiss(animated: true)
+                    } else {
+                        CCUtility.setIntro(true)
+                        if self.presentingViewController == nil {
+                            let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
+                            viewController?.modalPresentationStyle = .fullScreen
+                            self.appDelegate.window?.rootViewController = viewController
+                            self.appDelegate.window?.makeKey()
+                        } else {
+                            self.dismiss(animated: true)
+                        }
+                    }
                 } else {
-                    self.dismiss(animated: true)
+
+                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                    self.present(alertController, animated: true)
                 }
             }
 
@@ -439,9 +452,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
 
             let message = NSLocalizedString("_not_possible_connect_to_server_", comment: "") + ".\n" + error.errorDescription
             let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: message, preferredStyle: .alert)
-
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-
             self.present(alertController, animated: true, completion: { })
         }
     }

+ 47 - 58
iOSClient/Login/NCLoginWeb.swift

@@ -29,12 +29,16 @@ import FloatingPanel
 class NCLoginWeb: UIViewController {
 
     var webView: WKWebView?
+
+    // swiftlint:disable force_cast
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
     var titleView: String = ""
 
     var urlBase = ""
     var user: String?
-    
+
     var configServerUrl: String?
     var configUsername: String?
     var configPassword: String?
@@ -55,7 +59,7 @@ class NCLoginWeb: UIViewController {
         // load AppConfig
         if (NCBrandOptions.shared.disable_multiaccount == false) || (NCBrandOptions.shared.disable_multiaccount == true && accountCount == 0) {
             if let configurationManaged = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"), NCBrandOptions.shared.use_AppConfig {
-                
+
                 if let serverUrl = configurationManaged[NCGlobal.shared.configuration_serverUrl] as? String {
                     self.configServerUrl = serverUrl
                 }
@@ -104,7 +108,7 @@ class NCLoginWeb: UIViewController {
                 urlBase = serverUrl
             }
         }
-        
+
         // ADD end point for Web Flow
         if urlBase != NCBrandOptions.shared.linkloginPreferredProviders {
             if loginFlowV2Available {
@@ -171,7 +175,7 @@ class NCLoginWeb: UIViewController {
             let deviceUserAgent = String(cString: deviceName, encoding: .ascii) {
             webView.customUserAgent = deviceUserAgent
         } else {
-            webView.customUserAgent = CCUtility.getUserAgent()
+            webView.customUserAgent = userAgent
         }
 
         request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
@@ -179,10 +183,10 @@ class NCLoginWeb: UIViewController {
 
         webView.load(request)
     }
-    
+
     func getAppPassword(serverUrl: String, username: String, password: String) {
-        
-        NextcloudKit.shared.getAppPassword(serverUrl: serverUrl, username: username, password: password) { token, data, error in
+
+        NextcloudKit.shared.getAppPassword(serverUrl: serverUrl, username: username, password: password) { token, _, error in
             if error == .success, let password = token {
                 self.createAccount(server: serverUrl, username: username, password: password)
             } else {
@@ -232,7 +236,7 @@ extension NCLoginWeb: WKNavigationDelegate {
                 if value.contains("password:") { password = value }
             }
 
-            if server != "" && user != "" && password != "" {
+            if !server.isEmpty, !user.isEmpty, !password.isEmpty {
 
                 let server: String = server.replacingOccurrences(of: "/server:", with: "")
                 let username: String = user.replacingOccurrences(of: "user:", with: "").replacingOccurrences(of: "+", with: " ")
@@ -243,22 +247,6 @@ extension NCLoginWeb: WKNavigationDelegate {
         }
     }
 
-    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
-
-        var errorMessage = error.localizedDescription
-
-        for (key, value) in (error as NSError).userInfo {
-            let message = "\(key) \(value)\n"
-            errorMessage += message
-        }
-
-        let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: errorMessage, preferredStyle: .alert)
-
-        alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-
-        self.present(alertController, animated: true)
-    }
-
     func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
         DispatchQueue.global().async {
             if let serverTrust = challenge.protectionSpace.serverTrust {
@@ -283,7 +271,7 @@ extension NCLoginWeb: WKNavigationDelegate {
 
         if loginFlowV2Available {
             DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-                NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, data, error in
+                NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in
                     if error == .success && server != nil && loginName != nil && appPassword != nil {
                         self.createAccount(server: server!, username: loginName!, password: appPassword!)
                     }
@@ -297,47 +285,48 @@ extension NCLoginWeb: WKNavigationDelegate {
     func createAccount(server: String, username: String, password: String) {
 
         var urlBase = server
-
-        // Normalized
-        if urlBase.last == "/" {
-            urlBase = String(urlBase.dropLast())
-        }
-
-        // Create account
+        if urlBase.last == "/" { urlBase = String(urlBase.dropLast()) }
         let account: String = "\(username) \(urlBase)"
+        let user = username
 
-        // NO account found, clear all
-        if NCManageDatabase.shared.getAccounts() == nil {
-            NCUtility.shared.removeAllSettings()
-        }
-        
-        // Add new account
-        NCManageDatabase.shared.deleteAccount(account)
-        NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: username, password: password)
+        NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
+        NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
 
-        guard let tableAccount = NCManageDatabase.shared.setAccountActive(account) else {
-            self.dismiss(animated: true, completion: nil)
-            return
-        }
+            if error == .success, let userProfile {
 
-        appDelegate.settingAccount(account, urlBase: urlBase, user: username, userId: tableAccount.userId, password: password)
+                if NCManageDatabase.shared.getAccounts() == nil {
+                    NCUtility.shared.removeAllSettings()
+                }
 
-        if CCUtility.getIntro() {
-            self.dismiss(animated: true)
-        } else {
-            CCUtility.setIntro(true)
-            if self.presentingViewController == nil {
-                if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() {
-                    viewController.modalPresentationStyle = .fullScreen
-                    viewController.view.alpha = 0
-                    appDelegate.window?.rootViewController = viewController
-                    appDelegate.window?.makeKeyAndVisible()
-                    UIView.animate(withDuration: 0.5) {
-                        viewController.view.alpha = 1
+                NCManageDatabase.shared.deleteAccount(account)
+                NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
+
+                self.appDelegate.changeAccount(account, userProfile: userProfile)
+
+                if CCUtility.getIntro() {
+                    self.dismiss(animated: true)
+                } else {
+                    CCUtility.setIntro(true)
+                    if self.presentingViewController == nil {
+                        if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() {
+                            viewController.modalPresentationStyle = .fullScreen
+                            viewController.view.alpha = 0
+                            self.appDelegate.window?.rootViewController = viewController
+                            self.appDelegate.window?.makeKeyAndVisible()
+                            UIView.animate(withDuration: 0.5) {
+                                viewController.view.alpha = 1
+                            }
+                        }
+                    } else {
+                        self.dismiss(animated: true)
                     }
                 }
+
             } else {
-                self.dismiss(animated: true)
+
+                let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+                self.present(alertController, animated: true)
             }
         }
     }

+ 1 - 1
iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift

@@ -143,7 +143,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
         voiceRecordHUD.update(CGFloat(rate))
         voiceRecordHUD.fillColor = UIColor.green
-        
+
         let formatter = DateComponentsFormatter()
         formatter.allowedUnits = [.second]
         formatter.unitsStyle = .full

文件差異過大導致無法顯示
+ 226 - 345
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift


+ 15 - 14
iOSClient/Main/Collection Common/NCGridCell.swift

@@ -36,7 +36,8 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     @IBOutlet weak var imageVisualEffect: UIVisualEffectView!
     @IBOutlet weak var progressView: UIProgressView!
 
-    internal var objectId = ""
+    var objectId = ""
+    var indexPath = IndexPath()
     private var user = ""
 
     weak var delegate: NCGridCellDelegate?
@@ -99,7 +100,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         imageVisualEffect.clipsToBounds = true
         imageVisualEffect.alpha = 0.5
 
-        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.tintColor = NCBrandColor.shared.brand
         progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5)
         progressView.trackTintColor = .clear
 
@@ -134,20 +135,20 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     @IBAction func touchUpInsideMore(_ sender: Any) {
-        delegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, sender: sender)
+        delegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
     }
 
     @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, gestureRecognizer: gestureRecognizer)
+        delegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressGridItem(with: objectId, gestureRecognizer: gestureRecognizer)
+        delegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     fileprivate func setA11yActions() {
         let moreName = namedButtonMore == NCGlobal.shared.buttonMoreStop ? "_cancel_" : "_more_"
-        
+
         self.accessibilityCustomActions = [
             UIAccessibilityCustomAction(
                 name: NSLocalizedString(moreName, comment: ""),
@@ -155,7 +156,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
                 selector: #selector(touchUpInsideMore))
         ]
     }
-    
+
     func setButtonMore(named: String, image: UIImage) {
         namedButtonMore = named
         buttonMore.setImage(image, for: .normal)
@@ -180,7 +181,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     func selected(_ status: Bool) {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isDownloadUpload else {
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isInTransfer else {
             imageSelect.isHidden = true
             imageVisualEffect.isHidden = true
             return
@@ -218,16 +219,16 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 }
 
 protocol NCGridCellDelegate: AnyObject {
-    func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any)
-    func longPressMoreGridItem(with objectId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer)
-    func longPressGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer)
+    func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any)
+    func longPressMoreGridItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
+    func longPressGridItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
 }
 
 // optional func
 extension NCGridCellDelegate {
-    func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) {}
-    func longPressMoreGridItem(with objectId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) {}
-    func longPressGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) {}
+    func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) {}
+    func longPressMoreGridItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
+    func longPressGridItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
 }
 
 // MARK: - Grid Layout

+ 19 - 18
iOSClient/Main/Collection Common/NCListCell.swift

@@ -48,12 +48,13 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 
     private var objectId = ""
     private var user = ""
+    var indexPath = IndexPath()
 
     weak var delegate: NCListCellDelegate?
     var namedButtonMore = ""
 
     var fileAvatarImageView: UIImageView? {
-        get { return imageShared }
+        return imageShared
     }
     var fileObjectId: String? {
         get { return objectId }
@@ -120,7 +121,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         accessibilityValue = nil
         isAccessibilityElement = true
 
-        progressView.tintColor = NCBrandColor.shared.brandElement
+        progressView.tintColor = NCBrandColor.shared.brand
         progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5)
         progressView.trackTintColor = .clear
 
@@ -156,21 +157,21 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
         return nil
     }
-    
+
     @IBAction func touchUpInsideShare(_ sender: Any) {
-        delegate?.tapShareListItem(with: objectId, sender: sender)
+        delegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender)
     }
 
     @IBAction func touchUpInsideMore(_ sender: Any) {
-        delegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, sender: sender)
+        delegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
     }
 
     @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, gestureRecognizer: gestureRecognizer)
+        delegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressListItem(with: objectId, gestureRecognizer: gestureRecognizer)
+        delegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     fileprivate func setA11yActions() {
@@ -196,7 +197,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         titleTrailingConstraint.constant = 90
         infoTrailingConstraint.constant = 90
     }
-    
+
     func setButtonMore(named: String, image: UIImage) {
         namedButtonMore = named
         imageMore.image = image
@@ -240,7 +241,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     func selected(_ status: Bool) {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isDownloadUpload else {
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId), !metadata.isInTransfer else {
             backgroundView = nil
             separator.isHidden = false
             return
@@ -291,7 +292,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
                 tag0.text = tag
                 if tags.count > 1 {
                     tag1.isHidden = false
-                    tag1.text = "+\(tags.count-1)"
+                    tag1.text = "+\(tags.count - 1)"
                 }
             }
         }
@@ -299,18 +300,18 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 }
 
 protocol NCListCellDelegate: AnyObject {
-    func tapShareListItem(with objectId: String, sender: Any)
-    func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any)
-    func longPressMoreListItem(with objectId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer)
-    func longPressListItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer)
+    func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any)
+    func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any)
+    func longPressMoreListItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
+    func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
 }
 
 // optional func
 extension NCListCellDelegate {
-    func tapShareListItem(with objectId: String, sender: Any) {}
-    func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) {}
-    func longPressMoreListItem(with objectId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) {}
-    func longPressListItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) {}
+    func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) {}
+    func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) {}
+    func longPressMoreListItem(with objectId: String, namedButtonMore: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
+    func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {}
 }
 
 // MARK: - List Layout

+ 7 - 5
iOSClient/Main/Collection Common/NCSelectableNavigationView.swift

@@ -40,13 +40,14 @@ protocol NCSelectableNavigationView: AnyObject {
     var collectionView: UICollectionView! { get set }
     var isEditMode: Bool { get set }
     var selectOcId: [String] { get set }
+    var selectIndexPath: [IndexPath] { get set }
     var titleCurrentFolder: String { get }
     var navigationItem: UINavigationItem { get }
     var navigationController: UINavigationController? { get }
     var layoutKey: String { get }
     var selectActions: [NCMenuAction] { get }
 
-    func reloadDataSource(forced: Bool)
+    func reloadDataSource(isForced: Bool)
     func setNavigationItem()
 
     func tapSelectMenu()
@@ -77,6 +78,7 @@ extension NCSelectableNavigationView {
     func tapSelect() {
         isEditMode = !isEditMode
         selectOcId.removeAll()
+        selectIndexPath.removeAll()
         self.setNavigationItem()
         self.collectionView.reloadData()
     }
@@ -152,13 +154,13 @@ extension NCSelectableNavigationView where Self: UIViewController {
             actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, completion: tapSelect))
         }
         actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: {
-            self.reloadDataSource(forced: true)
+            self.reloadDataSource(isForced: true)
             self.tapSelect()
         }))
 
-        actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, completion: tapSelect))
-        actions.append(.copyAction(selectOcId: selectOcId, hudView: self.view, completion: tapSelect))
-        actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect))
+        actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, indexPath: selectIndexPath, completion: tapSelect))
+        actions.append(.copyAction(selectOcId: selectOcId, completion: tapSelect))
+        actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, indexPath: selectIndexPath, viewController: self, completion: tapSelect))
         return actions
     }
 }

+ 18 - 19
iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift

@@ -27,7 +27,7 @@ import NextcloudKit
 import Photos
 import JGProgressHUD
 
-protocol NCCreateFormUploadConflictDelegate {
+protocol NCCreateFormUploadConflictDelegate: AnyObject {
     func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?)
 }
 
@@ -58,7 +58,7 @@ class NCCreateFormUploadConflict: UIViewController {
 
     var metadatasConflictNewFiles: [String] = []
     var metadatasConflictAlreadyExistingFiles: [String] = []
-    let fileNamesPath = ThreadSafeDictionary<String,String>()
+    let fileNamesPath = ThreadSafeDictionary<String, String>()
     var blurView: UIVisualEffectView!
 
     // MARK: - View Life Cycle
@@ -256,7 +256,7 @@ class NCCreateFormUploadConflict: UIViewController {
                 metadata.fileNameView = newFileName
 
                 // This is not an asset - [file]
-                if metadata.assetLocalIdentifier == "" || metadata.isExtractFile {
+                if metadata.assetLocalIdentifier.isEmpty || metadata.isExtractFile {
                     let newPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: newFileName)
                     CCUtility.moveFile(atPath: oldPath, toPath: newPath)
                 }
@@ -311,8 +311,8 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
             cell.backgroundColor = tableView.backgroundColor
             cell.switchNewFile.onTintColor = NCBrandColor.shared.brand
             cell.switchAlreadyExistingFile.onTintColor = NCBrandColor.shared.brand
-            
-            let metadataNewFile =  tableMetadata.init(value: metadatasUploadInConflict[indexPath.row])
+
+            let metadataNewFile = tableMetadata.init(value: metadatasUploadInConflict[indexPath.row])
 
             cell.ocId = metadataNewFile.ocId
             cell.delegate = self
@@ -324,7 +324,7 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
 
             guard let metadataAlreadyExists = NCManageDatabase.shared.getMetadataConflict(account: metadataNewFile.account, serverUrl: metadataNewFile.serverUrl, fileNameView: metadataNewFile.fileNameView) else { return UITableViewCell() }
             if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadataAlreadyExists.ocId, etag: metadataAlreadyExists.etag)) {
-                cell.imageAlreadyExistingFile.image =  UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadataAlreadyExists.ocId, etag: metadataAlreadyExists.etag))
+                cell.imageAlreadyExistingFile.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadataAlreadyExists.ocId, etag: metadataAlreadyExists.etag))
             } else if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageOcId(metadataAlreadyExists.ocId, fileNameView: metadataAlreadyExists.fileNameView)) && metadataAlreadyExists.contentType == "application/pdf" {
 
                 let url = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadataAlreadyExists.ocId, fileNameView: metadataAlreadyExists.fileNameView))
@@ -335,10 +335,10 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
                 }
 
             } else {
-                if metadataAlreadyExists.iconName.count > 0 {
-                    cell.imageAlreadyExistingFile.image = UIImage(named: metadataAlreadyExists.iconName)
-                } else {
+                if metadataAlreadyExists.iconName.isEmpty {
                     cell.imageAlreadyExistingFile.image = UIImage(named: "file")
+                } else {
+                    cell.imageAlreadyExistingFile.image = UIImage(named: metadataAlreadyExists.iconName)
                 }
             }
             cell.labelDetailAlreadyExistingFile.text = CCUtility.dateDiff(metadataAlreadyExists.date as Date) + "\n" + CCUtility.transformedSize(metadataAlreadyExists.size)
@@ -351,13 +351,13 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
 
             // -----> New File
 
-            if metadataNewFile.iconName.count > 0 {
-                cell.imageNewFile.image = UIImage(named: metadataNewFile.iconName)
-            } else {
+            if metadataNewFile.iconName.isEmpty {
                 cell.imageNewFile.image = UIImage(named: "file")
+            } else {
+                cell.imageNewFile.image = UIImage(named: metadataNewFile.iconName)
             }
             let filePathNewFile = CCUtility.getDirectoryProviderStorageOcId(metadataNewFile.ocId, fileNameView: metadataNewFile.fileNameView)!
-            if metadataNewFile.assetLocalIdentifier.count > 0 {
+            if !metadataNewFile.assetLocalIdentifier.isEmpty {
 
                 let result = PHAsset.fetchAssets(withLocalIdentifiers: [metadataNewFile.assetLocalIdentifier], options: nil)
                 let date = result.firstObject!.modificationDate
@@ -378,7 +378,7 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
                         }
 
                         let fileDictionary = try FileManager.default.attributesOfItem(atPath: fileNamePath)
-                        let fileSize = fileDictionary[FileAttributeKey.size] as! Int64
+                        let fileSize = fileDictionary[FileAttributeKey.size] as? Int64 ?? 0
 
                         cell.labelDetailNewFile.text = CCUtility.dateDiff(date) + "\n" + CCUtility.transformedSize(fileSize)
 
@@ -388,12 +388,12 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
 
                     // PREVIEW
                     let cameraRoll = NCCameraRoll()
-                    cameraRoll.extractImageVideoFromAssetLocalIdentifier(metadata: metadataNewFile, modifyMetadataForUpload: false, viewController: self, hud: JGProgressHUD()) { metadata, fileNamePath, error in
+                    cameraRoll.extractImageVideoFromAssetLocalIdentifier(metadata: metadataNewFile, modifyMetadataForUpload: false, viewController: self, hud: JGProgressHUD()) { _, fileNamePath, error in
                         if !error {
                             self.fileNamesPath[metadataNewFile.fileNameView] = fileNamePath!
                             do {
                                 let fileDictionary = try FileManager.default.attributesOfItem(atPath: fileNamePath!)
-                                let fileSize = fileDictionary[FileAttributeKey.size] as! Int64
+                                let fileSize = fileDictionary[FileAttributeKey.size] as? Int64 ?? 0
                                 if mediaType == PHAssetMediaType.image {
                                     let data = try Data(contentsOf: URL(fileURLWithPath: fileNamePath!))
                                     if let image = UIImage(data: data) {
@@ -413,7 +413,7 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
             } else if FileManager().fileExists(atPath: filePathNewFile) {
 
                 do {
-                    if metadataNewFile.classFile ==  NKCommon.TypeClassFile.image.rawValue {
+                    if metadataNewFile.classFile == NKCommon.TypeClassFile.image.rawValue {
                         // 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
@@ -421,7 +421,7 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
                     }
 
                     let fileDictionary = try FileManager.default.attributesOfItem(atPath: filePathNewFile)
-                    let fileSize = fileDictionary[FileAttributeKey.size] as! Int64
+                    let fileSize = fileDictionary[FileAttributeKey.size] as? Int64 ?? 0
 
                     cell.labelDetailNewFile.text = CCUtility.dateDiff(metadataNewFile.date as Date) + "\n" + CCUtility.transformedSize(fileSize)
 
@@ -521,7 +521,6 @@ struct UploadConflictView: UIViewControllerRepresentable {
     var metadatasUploadInConflict: [tableMetadata]
     var metadatasNOConflict: [tableMetadata]
 
-
     func makeUIViewController(context: Context) -> UIViewControllerType {
 
         let storyboard = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil)

+ 62 - 72
iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift

@@ -33,7 +33,9 @@ import XLForm
     @IBOutlet weak var collectionView: UICollectionView!
     @IBOutlet weak var collectionViewHeigth: NSLayoutConstraint!
 
+    // swiftlint:disable force_cast
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
 
     var editorId = ""
     var creatorId = ""
@@ -83,8 +85,6 @@ import XLForm
         getTemplate()
     }
 
-
-
     // MARK: - Tableview (XLForm)
 
     func initializeForm() {
@@ -105,7 +105,7 @@ import XLForm
         row.value = fileNameFolder
         row.cellConfig["backgroundColor"] = tableView.backgroundColor
 
-        row.cellConfig["imageView.image"] =  UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 25)
+        row.cellConfig["imageView.image"] = UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 25)
 
         row.cellConfig["textLabel.textAlignment"] = NSTextAlignment.right.rawValue
         row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
@@ -133,15 +133,15 @@ import XLForm
         section.addFormRow(row)
 
         self.form = form
-        //tableView.reloadData()
-        //collectionView.reloadData()
+        // tableView.reloadData()
+        // collectionView.reloadData()
     }
 
     override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
-        let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
-        header.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
-        header.textLabel?.textColor = .gray
-        header.tintColor = tableView.backgroundColor
+        let header = view as? UITableViewHeaderFooterView
+        header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+        header?.textLabel?.textColor = .gray
+        header?.tintColor = tableView.backgroundColor
     }
 
     // MARK: - CollectionView
@@ -167,13 +167,13 @@ import XLForm
         let template = listOfTemplate[indexPath.row]
 
         // image
-        let imagePreview = cell.viewWithTag(100) as! UIImageView
-        if template.preview != "" {
+        let imagePreview = cell.viewWithTag(100) as? UIImageView
+        if !template.preview.isEmpty {
             let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + template.name + ".png"
             if FileManager.default.fileExists(atPath: fileNameLocalPath) {
                 let imageURL = URL(fileURLWithPath: fileNameLocalPath)
                 if let image = UIImage(contentsOfFile: imageURL.path) {
-                    imagePreview.image = image
+                    imagePreview?.image = image
                 }
             } else {
                 getImageFromTemplate(name: template.name, preview: template.preview, indexPath: indexPath)
@@ -181,19 +181,19 @@ import XLForm
         }
 
         // name
-        let name = cell.viewWithTag(200) as! UILabel
-        name.text = template.name
-        name.textColor = .secondarySystemGroupedBackground
+        let name = cell.viewWithTag(200) as? UILabel
+        name?.text = template.name
+        name?.textColor = .secondarySystemGroupedBackground
 
         // select
-        let imageSelect = cell.viewWithTag(300) as! UIImageView
+        let imageSelect = cell.viewWithTag(300) as? UIImageView
         if selectTemplate != nil && selectTemplate?.name == template.name {
             cell.backgroundColor = .label
-            imageSelect.image = UIImage(named: "plus100")
-            imageSelect.isHidden = false
+            imageSelect?.image = UIImage(named: "plus100")
+            imageSelect?.isHidden = false
         } else {
             cell.backgroundColor = .secondarySystemGroupedBackground
-            imageSelect.isHidden = true
+            imageSelect?.isHidden = true
         }
 
         return cell
@@ -211,7 +211,7 @@ import XLForm
 
     // MARK: - Action
 
-    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
+    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], indexPath: [IndexPath], overwrite: Bool, copy: Bool, move: Bool) {
 
         guard let serverUrl = serverUrl else {
             return
@@ -224,7 +224,7 @@ import XLForm
             fileNameFolder = (serverUrl as NSString).lastPathComponent
         }
 
-        let buttonDestinationFolder: XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
+        let buttonDestinationFolder: XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
         buttonDestinationFolder.title = fileNameFolder
 
         self.tableView.reloadData()
@@ -235,13 +235,14 @@ import XLForm
         self.deselectFormRow(sender)
 
         let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
-        let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
-        let viewController = navigationController.topViewController as! NCSelect
+        if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController,
+           let viewController = navigationController.topViewController as? NCSelect {
 
-        viewController.delegate = self
-        viewController.typeOfCommandView = .selectCreateFolder
+            viewController.delegate = self
+            viewController.typeOfCommandView = .selectCreateFolder
 
-        self.present(navigationController, animated: true, completion: nil)
+            self.present(navigationController, animated: true, completion: nil)
+        }
     }
 
     @objc func save() {
@@ -251,59 +252,48 @@ import XLForm
         }
         templateIdentifier = selectTemplate.identifier
 
-        let rowFileName: XLFormRowDescriptor  = self.form.formRow(withTag: "fileName")!
-        guard var fileNameForm = rowFileName.value else {
-            return
-        }
-
-        if fileNameForm as! String == "" {
-            return
-        } else {
+        let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+        guard var fileNameForm: String = rowFileName.value as? String, !fileNameForm.isEmpty else { return }
 
-            //Trim whitespaces after checks above
-            fileNameForm = (fileNameForm as! String).trimmingCharacters(in: .whitespacesAndNewlines)
+        // Trim whitespaces after checks above
+        fileNameForm = fileNameForm.trimmingCharacters(in: .whitespacesAndNewlines)
 
-            let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm as! String, mimeType: "", directory: false)
-            if NCUtility.shared.isDirectEditing(account: appDelegate.account, contentType: result.mimeType).count == 0 {
-                fileNameForm = (fileNameForm as! NSString).deletingPathExtension + "." + fileNameExtension
-            }
+        let result = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameForm, mimeType: "", directory: false)
+        if NCUtility.shared.isDirectEditing(account: appDelegate.account, contentType: result.mimeType).isEmpty {
+            fileNameForm = (fileNameForm as NSString).deletingPathExtension + "." + fileNameExtension
+        }
 
-            if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm)) != nil {
+        if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm)) != nil {
 
-                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
+            let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
 
-                guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+            guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
 
-                conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
-                conflict.alwaysNewFileNameNumber = true
-                conflict.serverUrl = serverUrl
-                conflict.metadatasUploadInConflict = [metadataForUpload]
-                conflict.delegate = self
+            conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+            conflict.alwaysNewFileNameNumber = true
+            conflict.serverUrl = serverUrl
+            conflict.metadatasUploadInConflict = [metadataForUpload]
+            conflict.delegate = self
 
-                self.present(conflict, animated: true, completion: nil)
+            self.present(conflict, animated: true, completion: nil)
 
-            } else {
+        } else {
 
-                let fileNamePath = CCUtility.returnFileNamePath(fromFileName: String(describing: fileNameForm), serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId, account: appDelegate.account)!
-                createDocument(fileNamePath: fileNamePath, fileName: String(describing: fileNameForm))
-            }
+            let fileNamePath = CCUtility.returnFileNamePath(fromFileName: String(describing: fileNameForm), serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId, account: appDelegate.account)!
+            createDocument(fileNamePath: fileNamePath, fileName: String(describing: fileNameForm))
         }
     }
 
     func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
 
-        if metadatas == nil || metadatas?.count == 0 {
-
+        if let metadatas {
+            let fileName = metadatas[0].fileName
+            let fileNamePath = CCUtility.returnFileNamePath(fromFileName: fileName, serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId, account: appDelegate.account)!
+            createDocument(fileNamePath: fileNamePath, fileName: fileName)
+        } else {
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                 self.cancel()
             }
-
-        } else {
-
-            let fileName = metadatas![0].fileName
-            let fileNamePath = CCUtility.returnFileNamePath(fromFileName: fileName, serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId, account: appDelegate.account)!
-
-            createDocument(fileNamePath: fileNamePath, fileName: fileName)
         }
     }
 
@@ -314,7 +304,7 @@ import XLForm
         UUID = "TEMP" + UUID.replacingOccurrences(of: "-", with: "")
 
         if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
-            
+
             var options = NKRequestOptions()
             if self.editorId == NCGlobal.shared.editorOnlyoffice {
                 options = NKRequestOptions(customUserAgent: NCUtility.shared.getCustomUserAgentOnlyOffice())
@@ -322,7 +312,7 @@ import XLForm
                 options = NKRequestOptions(customUserAgent: NCUtility.shared.getCustomUserAgentNCText())
             }
 
-            NextcloudKit.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, options: options) { account, url, data, error in
+            NextcloudKit.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, options: options) { account, url, _, error in
                 guard error == .success, account == self.appDelegate.account, let url = url else {
                     self.navigationItem.rightBarButtonItem?.isEnabled = true
                     NCContentPresenter.shared.showError(error: error)
@@ -330,7 +320,7 @@ import XLForm
                 }
 
                 var results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false)
-                //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+                // FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
                 if results.mimeType.isEmpty {
                     results.mimeType = "text/x-markdown"
                 }
@@ -346,7 +336,7 @@ import XLForm
 
         if self.editorId == NCGlobal.shared.editorCollabora {
 
-            NextcloudKit.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier) { account, url, data, error in
+            NextcloudKit.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier) { account, url, _, error in
                 guard error == .success, account == self.appDelegate.account, let url = url else {
                     self.navigationItem.rightBarButtonItem?.isEnabled = true
                     NCContentPresenter.shared.showError(error: error)
@@ -385,7 +375,7 @@ import XLForm
                 options = NKRequestOptions(customUserAgent: NCUtility.shared.getCustomUserAgentNCText())
             }
 
-            NextcloudKit.shared.NCTextGetListOfTemplates(options: options) { account, templates, data, error in
+            NextcloudKit.shared.NCTextGetListOfTemplates(options: options) { account, templates, _, error in
 
                 self.indicator.stopAnimating()
 
@@ -403,7 +393,7 @@ import XLForm
                         self.listOfTemplate.append(temp)
 
                         // default: template empty
-                        if temp.preview == "" {
+                        if temp.preview.isEmpty {
                             self.selectTemplate = temp
                             self.fileNameExtension = template.ext
                             self.navigationItem.rightBarButtonItem?.isEnabled = true
@@ -411,7 +401,7 @@ import XLForm
                     }
                 }
 
-                if self.listOfTemplate.count == 0 {
+                if self.listOfTemplate.isEmpty {
 
                     let temp = NKEditorTemplates()
 
@@ -442,7 +432,7 @@ import XLForm
 
         if self.editorId == NCGlobal.shared.editorCollabora {
 
-            NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate) { account, templates, data, error in
+            NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate) { account, templates, _, error in
 
                 self.indicator.stopAnimating()
 
@@ -462,7 +452,7 @@ import XLForm
                         self.listOfTemplate.append(temp)
 
                         // default: template empty
-                        if temp.preview == "" {
+                        if temp.preview.isEmpty {
                             self.selectTemplate = temp
                             self.fileNameExtension = temp.ext
                             self.navigationItem.rightBarButtonItem?.isEnabled = true
@@ -470,7 +460,7 @@ import XLForm
                     }
                 }
 
-                if self.listOfTemplate.count == 0 {
+                if self.listOfTemplate.isEmpty {
 
                     let temp = NKEditorTemplates()
 

+ 27 - 27
iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift

@@ -31,7 +31,9 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
     @IBOutlet weak var labelDuration: UILabel!
     @IBOutlet weak var progressView: UIProgressView!
 
+    // swiftlint:disable force_cast
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
 
     private var serverUrl = ""
     private var titleServerUrl = ""
@@ -67,7 +69,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         // Progress view
         progressView.progress = 0
         progressView.progressTintColor = .green
-        progressView.trackTintColor = UIColor(red: 247.0/255.0, green: 247.0/255.0, blue: 247.0/255.0, alpha: 1.0)
+        progressView.trackTintColor = UIColor(red: 247.0 / 255.0, green: 247.0 / 255.0, blue: 247.0 / 255.0, alpha: 1.0)
 
         labelTimer.textColor = .label
         labelDuration.textColor = .label
@@ -131,7 +133,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         row.action.formSelector = #selector(changeDestinationFolder(_:))
         row.cellConfig["backgroundColor"] = cellBackgoundColor
 
-        row.cellConfig["imageView.image"] =  UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 25)
+        row.cellConfig["imageView.image"] = UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 25)
 
         row.cellConfig["textLabel.textAlignment"] = NSTextAlignment.right.rawValue
         row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
@@ -182,15 +184,15 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
     // MARK: TableViewDelegate
 
     override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
-        let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
-        header.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
-        header.textLabel?.textColor = .gray
-        header.tintColor = cellBackgoundColor
+        let header = view as? UITableViewHeaderFooterView
+        header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+        header?.textLabel?.textColor = .gray
+        header?.tintColor = cellBackgoundColor
     }
 
     // MARK: - Action
 
-    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
+    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], indexPath: [IndexPath], overwrite: Bool, copy: Bool, move: Bool) {
 
         if serverUrl != nil {
 
@@ -203,7 +205,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
             }
 
             // Update
-            let row: XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
+            let row: XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
             row.title = self.titleServerUrl
             self.updateFormRow(row)
         }
@@ -211,17 +213,15 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
 
     @objc func save() {
 
-        let rowFileName: XLFormRowDescriptor  = self.form.formRow(withTag: "fileName")!
-        guard let name = rowFileName.value else {
-            return
-        }
-        let ext = (name as! NSString).pathExtension.uppercased()
+        let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+        guard let name = rowFileName.value as? String else { return }
+        let ext = (name as NSString).pathExtension.uppercased()
         var fileNameSave = ""
 
-        if ext == "" {
-            fileNameSave = name as! String + ".m4a"
+        if ext.isEmpty {
+            fileNameSave = name + ".m4a"
         } else {
-            fileNameSave = (name as! NSString).deletingPathExtension + ".m4a"
+            fileNameSave = (name as NSString).deletingPathExtension + ".m4a"
         }
 
         let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "")
@@ -234,7 +234,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileNameSave) != nil {
 
             guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
-            
+
             conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
             conflict.serverUrl = serverUrl
             conflict.metadatasUploadInConflict = [metadataForUpload]
@@ -250,10 +250,9 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
 
     func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
 
-        if metadatas != nil && metadatas!.count > 0 {
-
+        if let metadatas {
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
-                self.dismissAndUpload(metadatas![0])
+                self.dismissAndUpload(metadatas[0])
             }
         }
     }
@@ -278,20 +277,21 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         self.deselectFormRow(sender)
 
         let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
-        let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
-        let viewController = navigationController.topViewController as! NCSelect
+        if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController,
+           let viewController = navigationController.topViewController as? NCSelect {
 
-        viewController.delegate = self
-        viewController.typeOfCommandView = .selectCreateFolder
-        viewController.includeDirectoryE2EEncryption = true
+            viewController.delegate = self
+            viewController.typeOfCommandView = .selectCreateFolder
+            viewController.includeDirectoryE2EEncryption = true
 
-        self.present(navigationController, animated: true, completion: nil)
+            self.present(navigationController, animated: true, completion: nil)
+        }
     }
 
     // MARK: Player - Timer
 
     func updateTimerUI() {
-        labelTimer.text =  String().formatSecondsToString(counterSecondPlayer)
+        labelTimer.text = String().formatSecondsToString(counterSecondPlayer)
         labelDuration.text = String().formatSecondsToString(durationPlayer)
         progressView.progress = Float(counterSecondPlayer / durationPlayer)
     }

+ 8 - 5
iOSClient/Main/Create cloud/NCCreateMenuAdd.swift

@@ -26,7 +26,10 @@ import Sheeeeeeeeet
 
 class NCCreateMenuAdd: NSObject {
 
+    // swiftlint:disable force_cast
     weak var appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
     var isNextcloudTextAvailable = false
 
     @objc init(viewController: UIViewController, view: UIView) {
@@ -68,7 +71,7 @@ class NCCreateMenuAdd: NSObject {
         items.append(MenuItem(title: NSLocalizedString("_create_folder_", comment: ""), value: 60, image: CCGraphics.changeThemingColorImage(UIImage(named: "folder"), width: 50, height: 50, color: NCBrandColor.sharedInstance.brandElement)))
 
         if let richdocumentsMimetypes = NCManageDatabase.sharedInstance.getRichdocumentsMimetypes(account: appDelegate.activeAccount) {
-            if richdocumentsMimetypes.count > 0 {
+            if !richdocumentsMimetypes.isEmpty {
                 items.append(MenuItem(title: NSLocalizedString("_create_new_document_", comment: ""), value: 70, image: UIImage(named: "create_file_document")))
                 items.append(MenuItem(title: NSLocalizedString("_create_new_spreadsheet_", comment: ""), value: 80, image: UIImage(named: "create_file_xls")))
                 items.append(MenuItem(title: NSLocalizedString("_create_new_presentation_", comment: ""), value: 90, image: UIImage(named: "create_file_ppt")))
@@ -96,7 +99,7 @@ class NCCreateMenuAdd: NSObject {
                 }
                 navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
+                let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments
                 viewController.typeTemplate = k_template_document
                 viewController.serverUrl = self.appDelegate.activeMain.serverUrl
                 viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
@@ -119,7 +122,7 @@ class NCCreateMenuAdd: NSObject {
                 }
                 navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
+                let viewController = (navigationController as? UINavigationController).topViewController as? NCCreateFormUploadDocuments
                 viewController.typeTemplate = k_template_document
                 viewController.serverUrl = self.appDelegate.activeMain.serverUrl
                 viewController.titleForm = NSLocalizedString("_create_new_document_", comment: "")
@@ -132,7 +135,7 @@ class NCCreateMenuAdd: NSObject {
                 }
                 navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
+                let viewController = (navigationController as? UINavigationController).topViewController as? NCCreateFormUploadDocuments
                 viewController.typeTemplate = k_template_spreadsheet
                 viewController.serverUrl = self.appDelegate.activeMain.serverUrl
                 viewController.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
@@ -145,7 +148,7 @@ class NCCreateMenuAdd: NSObject {
                 }
                 navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
+                let viewController = (navigationController as? UINavigationController).topViewController as? NCCreateFormUploadDocuments
                 viewController.typeTemplate = k_template_presentation
                 viewController.serverUrl = self.appDelegate.activeMain.serverUrl
                 viewController.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")

+ 70 - 97
iOSClient/Main/NCActionCenter.swift

@@ -121,24 +121,6 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
         case NCGlobal.shared.selectorSaveAlbum:
             saveAlbum(metadata: metadata)
 
-        case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV:
-
-            var metadata = metadata
-            var metadataMOV = metadata
-            guard let metadataTMP = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) else { break }
-
-            if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoIMG {
-                metadataMOV = metadataTMP
-            }
-
-            if selector == NCGlobal.shared.selectorSaveAlbumLivePhotoMOV {
-                metadata = metadataTMP
-            }
-
-            if CCUtility.fileProviderStorageExists(metadata) && CCUtility.fileProviderStorageExists(metadataMOV) {
-                saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
-            }
-
         case NCGlobal.shared.selectorSaveAsScan:
             saveAsScan(metadata: metadata)
 
@@ -162,7 +144,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
             }
         } else if metadata.directory {
             NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, offline: true, account: appDelegate.account)
-            NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorDownloadAllFile)
+            NCNetworking.shared.synchronizationServerUrl(serverUrl, account: metadata.account, selector: NCGlobal.shared.selectorSynchronizationOffline)
         } else {
             NCNetworking.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorLoadOffline) { _, _ in }
             if let metadataLivePhoto = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
@@ -339,10 +321,13 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
         let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadata.count, hudView: appDelegate.window?.rootViewController?.view)
         for (metadata, url) in downloadMetadata {
             processor.execute { completion in
-                NCNetworking.shared.download(metadata: metadata, selector: "", completion: { _, _ in
+                NCNetworking.shared.download(metadata: metadata, selector: "", notificationCenterProgressTask: false) { _ in
+                } progressHandler: { progress in
+                    processor.hud?.progress = Float(progress.fractionCompleted)
+                } completion: { _, _ in
                     if CCUtility.fileProviderStorageExists(metadata) { items.append(url) }
                     completion()
-                })
+                }
             }
         }
 
@@ -457,67 +442,13 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
         }
     }
 
-    func saveLivePhoto(metadata: tableMetadata, metadataMOV: tableMetadata) {
-
-        if !CCUtility.fileProviderStorageExists(metadata) {
-            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbumLivePhotoIMG)
-        }
-
-        if !CCUtility.fileProviderStorageExists(metadataMOV) {
-            NCOperationQueue.shared.download(metadata: metadataMOV, selector: NCGlobal.shared.selectorSaveAlbumLivePhotoMOV)
-        }
-
-        if CCUtility.fileProviderStorageExists(metadata) && CCUtility.fileProviderStorageExists(metadataMOV) {
-            saveLivePhotoToDisk(metadata: metadata, metadataMov: metadataMOV)
-        }
-    }
-
-    func saveLivePhotoToDisk(metadata: tableMetadata, metadataMov: tableMetadata) {
-        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
-
-        let fileNameImage = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!)
-        let fileNameMov = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadataMov.ocId, fileNameView: metadataMov.fileNameView)!)
-        let hud = JGProgressHUD()
-
-        hud.indicatorView = JGProgressHUDRingIndicatorView()
-        if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
-            indicatorView.ringWidth = 1.5
-        }
-        hud.textLabel.text = NSLocalizedString("_saving_", comment: "")
-        hud.show(in: (appDelegate.window?.rootViewController?.view)!)
-
-        NCLivePhoto.generate(from: fileNameImage, videoURL: fileNameMov, progress: { progress in
-            hud.progress = Float(progress)
-        }, completion: { _, resources in
-
-            if resources != nil {
-                NCLivePhoto.saveToLibrary(resources!) { result in
-                    DispatchQueue.main.async {
-                        if !result {
-                            hud.indicatorView = JGProgressHUDErrorIndicatorView()
-                            hud.textLabel.text = NSLocalizedString("_livephoto_save_error_", comment: "")
-                        } else {
-                            hud.indicatorView = JGProgressHUDSuccessIndicatorView()
-                            hud.textLabel.text = NSLocalizedString("_success_", comment: "")
-                        }
-                        hud.dismiss(afterDelay: 1)
-                    }
-                }
-            } else {
-                hud.indicatorView = JGProgressHUDErrorIndicatorView()
-                hud.textLabel.text = NSLocalizedString("_livephoto_save_error_", comment: "")
-                hud.dismiss(afterDelay: 1)
-            }
-        })
-    }
-
     // MARK: - Copy & Paste
 
-    func copyPasteboard(pasteboardOcIds: [String], hudView: UIView) {
+    func copyPasteboard(pasteboardOcIds: [String]) {
         var items = [[String: Any]]()
-        let hud = JGProgressHUD()
-        hud.textLabel.text = NSLocalizedString("_wait_", comment: "")
-        hud.show(in: hudView)
+        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+        let hudView = appDelegate.window?.rootViewController?.view
+        var fractionCompleted: Float = 0
 
         // getting file data can take some time and block the main queue
         DispatchQueue.global(qos: .userInitiated).async {
@@ -531,17 +462,24 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
                 }
             }
 
-            DispatchQueue.main.async(execute: hud.dismiss)
-
             // do 5 downloads in parallel to optimize efficiency
-            let parallelizer = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadatas.count, hudView: hudView)
+            let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadatas.count, hudView: hudView)
 
             for metadata in downloadMetadatas {
-                parallelizer.execute { completion in
-                    NCNetworking.shared.download(metadata: metadata, selector: "") { _, _ in completion() }
+                processor.execute { completion in
+                    NCNetworking.shared.download(metadata: metadata, selector: "", notificationCenterProgressTask: false) { _ in
+                    } progressHandler: { progress in
+                        if Float(progress.fractionCompleted) > fractionCompleted || fractionCompleted == 0 {
+                            processor.hud?.progress = Float(progress.fractionCompleted)
+                            fractionCompleted = Float(progress.fractionCompleted)
+                        }
+                    } completion: { _, _ in
+                        fractionCompleted = 0
+                        completion()
+                    }
                 }
             }
-            parallelizer.completeWork {
+            processor.completeWork {
                 items.append(contentsOf: downloadMetadatas.compactMap({ $0.toPasteBoardItem() }))
                 UIPasteboard.general.setItems(items, options: [:])
             }
@@ -550,13 +488,18 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
 
     func pastePasteboard(serverUrl: String) {
         guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+        var fractionCompleted: Float = 0
 
-        let parallelizer = ParallelWorker(n: 5, titleKey: "_uploading_", totalTasks: nil, hudView: appDelegate.window?.rootViewController?.view)
+        let processor = ParallelWorker(n: 5, titleKey: "_uploading_", totalTasks: nil, hudView: appDelegate.window?.rootViewController?.view)
 
         func uploadPastePasteboard(fileName: String, serverUrlFileName: String, fileNameLocalPath: String, serverUrl: String, completion: @escaping () -> Void) {
             NextcloudKit.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath) { request in
                 NCNetworking.shared.uploadRequest[fileNameLocalPath] = request
-            } progressHandler: { _ in
+            } progressHandler: { progress in
+                if Float(progress.fractionCompleted) > fractionCompleted || fractionCompleted == 0 {
+                    processor.hud?.progress = Float(progress.fractionCompleted)
+                    fractionCompleted = Float(progress.fractionCompleted)
+                }
             } completionHandler: { account, ocId, etag, _, _, _, afError, error in
                 NCNetworking.shared.uploadRequest.removeValue(forKey: fileNameLocalPath)
                 if error == .success && etag != nil && ocId != nil {
@@ -569,6 +512,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
                 } else {
                     NCContentPresenter.shared.showError(error: error)
                 }
+                fractionCompleted = 0
                 completion()
             }
         }
@@ -584,12 +528,12 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
                 let ocIdUpload = UUID().uuidString
                 let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(ocIdUpload, fileNameView: fileName)!
                 do { try data.write(to: URL(fileURLWithPath: fileNameLocalPath)) } catch { continue }
-                parallelizer.execute { completion in
+                processor.execute { completion in
                     uploadPastePasteboard(fileName: fileName, serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, serverUrl: serverUrl, completion: completion)
                 }
             }
         }
-        parallelizer.completeWork()
+        processor.completeWork()
     }
 
     // MARK: -
@@ -652,21 +596,49 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
 
     // MARK: - NCSelect + Delegate
 
-    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
-        if serverUrl != nil && !items.isEmpty {
+    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], indexPath: [IndexPath], overwrite: Bool, copy: Bool, move: Bool) {
+        if let serverUrl, !items.isEmpty {
+            let hud = JGProgressHUD()
+            hud.textLabel.text = copy ? NSLocalizedString("_copying_progess_", comment: "") : NSLocalizedString("_moving_progess_", comment: "")
+            if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
+               let view = appDelegate.window?.rootViewController?.view {
+                hud.show(in: view)
+            }
             if copy {
-                for case let metadata as tableMetadata in items {
-                    NCOperationQueue.shared.copyMove(metadata: metadata, serverUrl: serverUrl!, overwrite: overwrite, move: false)
+                Task {
+                    var error = NKError()
+                    var ocId: [String] = []
+                    for case let metadata as tableMetadata in items where error == .success {
+                        error = await NCNetworking.shared.copyMetadata(metadata, serverUrlTo: serverUrl, overwrite: overwrite)
+                        if error == .success {
+                            ocId.append(metadata.ocId)
+                        }
+                    }
+                    if error != .success {
+                        NCContentPresenter.shared.showError(error: error)
+                    }
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCopyFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "error": error, "hud": hud])
                 }
-            } else if move {
-                for case let metadata as tableMetadata in items {
-                    NCOperationQueue.shared.copyMove(metadata: metadata, serverUrl: serverUrl!, overwrite: overwrite, move: true)
+            } else {
+                Task {
+                    var error = NKError()
+                    var ocId: [String] = []
+                    for case let metadata as tableMetadata in items where error == .success {
+                        error = await NCNetworking.shared.moveMetadata(metadata, serverUrlTo: serverUrl, overwrite: overwrite)
+                        if error == .success {
+                            ocId.append(metadata.ocId)
+                        }
+                    }
+                    if error != .success {
+                        NCContentPresenter.shared.showError(error: error)
+                    }
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMoveFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "error": error, "hud": hud])
                 }
             }
         }
     }
 
-    func openSelectView(items: [tableMetadata]) {
+    func openSelectView(items: [tableMetadata], indexPath: [IndexPath]) {
         guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
 
         let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as? UINavigationController
@@ -701,6 +673,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec
             vc.typeOfCommandView = .copyMove
             vc.items = copyItems
             vc.serverUrl = serverUrl
+            vc.selectIndexPath = indexPath
 
             vc.navigationItem.backButtonTitle = vc.titleCurrentFolder
             listViewController.insert(vc, at: 0)

+ 1 - 0
iOSClient/Main/NCCellProtocol.swift

@@ -39,6 +39,7 @@ protocol NCCellProtocol {
     var fileSharedImage: UIImageView? { get set }
     var fileMoreImage: UIImageView? { get set }
     var cellSeparatorView: UIView? { get set }
+    var indexPath: IndexPath { get set }
 
     func titleInfoTrailingDefault()
     func titleInfoTrailingFull()

+ 27 - 14
iOSClient/Main/NCMainTabBar.swift

@@ -28,16 +28,17 @@ class NCMainTabBar: UITabBar {
 
     private var fillColor: UIColor!
     private var shapeLayer: CALayer?
+
+    // swiftlint:disable force_cast
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
     private let centerButtonY: CGFloat = -28
 
     public var menuRect: CGRect {
-        get {
-            let tabBarItemWidth = Int(self.frame.size.width) / (self.items?.count ?? 0)
-            let rect = CGRect(x: 0, y: -5, width: tabBarItemWidth, height: Int(self.frame.size.height))
-
-            return rect
-        }
+        let tabBarItemWidth = Int(self.frame.size.width) / (self.items?.count ?? 0)
+        let rect = CGRect(x: 0, y: -5, width: tabBarItemWidth, height: Int(self.frame.size.height))
+        return rect
     }
 
     // MARK: - Life Cycle
@@ -48,7 +49,6 @@ class NCMainTabBar: UITabBar {
         appDelegate.mainTabBar = self
 
         NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-
         NotificationCenter.default.addObserver(self, selector: #selector(updateBadgeNumber(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil)
 
         barTintColor = .secondarySystemBackground
@@ -177,7 +177,7 @@ class NCMainTabBar: UITabBar {
         }
 
         let centerButtonHeight: CGFloat = 57
-        let centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2)-(centerButtonHeight/2), y: centerButtonY, width: centerButtonHeight, height: centerButtonHeight))
+        let centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2) - (centerButtonHeight / 2), y: centerButtonY, width: centerButtonHeight, height: centerButtonHeight))
 
         centerButton.setTitle("", for: .normal)
         centerButton.setImage(UIImage(named: "tabBarPlus")?.image(color: .white, size: 100), for: .normal)
@@ -212,15 +212,28 @@ class NCMainTabBar: UITabBar {
     @objc func updateBadgeNumber(_ notification: NSNotification) {
 
         guard let userInfo = notification.userInfo as NSDictionary?,
-              let counter = userInfo["counter"] as? Int
+              let counterDownload = userInfo["counterDownload"] as? Int,
+              let counterUpload = userInfo["counterUpload"] as? Int
         else { return }
 
-        UIApplication.shared.applicationIconBadgeNumber = counter
+        UIApplication.shared.applicationIconBadgeNumber = counterUpload
         if let item = self.items?[0] {
-            if counter > 0 {
-                item.badgeValue = String(counter)
-            } else {
+            if counterDownload == 0, counterUpload == 0 {
                 item.badgeValue = nil
+            } else if counterDownload > 0, counterUpload == 0 {
+                var badgeValue = String("↓ \(counterDownload)")
+                if counterDownload >= NCGlobal.shared.maxConcurrentOperationCountDownload {
+                    badgeValue = String("↓ 10+")
+                }
+                item.badgeValue = badgeValue
+            } else if counterDownload == 0, counterUpload > 0 {
+                item.badgeValue = String("↑ \(counterUpload)")
+            } else {
+                var badgeValue = String("↓ \(counterDownload) ↑ \(counterUpload)")
+                if counterDownload >= NCGlobal.shared.maxConcurrentOperationCountDownload {
+                    badgeValue = String("↓ 10+ ↑ \(counterUpload)")
+                }
+                item.badgeValue = badgeValue
             }
         }
     }
@@ -233,7 +246,7 @@ class NCMainTabBar: UITabBar {
         }
     }
 
-    func getHight() -> CGFloat {
+    func getHeight() -> CGFloat {
         return (frame.size.height - centerButtonY)
     }
 }

+ 8 - 2
iOSClient/Main/NCPickerViewController.swift

@@ -31,7 +31,10 @@ import NextcloudKit
 
 class NCPhotosPickerViewController: NSObject {
 
+    // swiftlint:disable force_cast
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
     var sourceViewController: UIViewController
     var maxSelectedAssets = 1
     var singleSelectedMode = false
@@ -115,7 +118,10 @@ class customPhotoPickerViewController: TLPhotosPickerViewController {
 
 class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate {
 
+    // swiftlint:disable force_cast
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
     var isViewerMedia: Bool
     var viewController: UIViewController?
 
@@ -165,7 +171,7 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate {
                 let toPath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!
                 let urlOut = URL(fileURLWithPath: toPath)
 
-                guard let _ = self.copySecurityScopedResource(url: urlIn, urlOut: urlOut) else { continue }
+                guard self.copySecurityScopedResource(url: urlIn, urlOut: urlOut) != nil else { continue }
 
                 let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
 
@@ -174,7 +180,7 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate {
                 metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath)
                 metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
 
-                if let _ = NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileName) {
+                if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileName) != nil {
                     metadatasInConflict.append(metadataForUpload)
                 } else {
                     metadatas.append(metadataForUpload)

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

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

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

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

+ 1 - 0
iOSClient/Media/Cell/NCGridMediaCell.swift

@@ -32,6 +32,7 @@ class NCGridMediaCell: UICollectionViewCell, NCCellProtocol {
 
     private var objectId: String = ""
     private var user: String = ""
+    var indexPath = IndexPath()
 
     var date: Date?
 

+ 97 - 87
iOSClient/Media/NCMedia.swift

@@ -23,6 +23,7 @@
 
 import UIKit
 import NextcloudKit
+import JGProgressHUD
 
 class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
@@ -33,7 +34,9 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
     private var gridLayout: NCGridMediaLayout!
     internal var documentPickerViewController: NCDocumentPickerViewController?
 
+    // swiftlint:disable force_cast
     internal let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
 
     public var metadatas: [tableMetadata] = []
     private var account: String = ""
@@ -43,6 +46,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
     internal var isEditMode = false
     internal var selectOcId: [String] = []
+    internal var selectIndexPath: [IndexPath] = []
 
     internal var filterClassTypeImage = false
     internal var filterClassTypeVideo = false
@@ -89,9 +93,6 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
         // Empty
         emptyDataSet = NCEmptyDataSet(view: collectionView, offset: 0, delegate: self)
 
-        // Notification
-        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
-
         mediaCommandView = Bundle.main.loadNibNamed("NCMediaCommandView", owner: self, options: nil)?.first as? NCMediaCommandView
         self.view.addSubview(mediaCommandView!)
         mediaCommandView?.mediaView = self
@@ -120,6 +121,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
         NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(moveFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(copyFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
 
@@ -131,15 +133,16 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
     override func viewDidAppear(_ animated: Bool) {
         super.viewDidAppear(animated)
-        
+
         mediaCommandTitle()
     }
-    
+
     override func viewWillDisappear(_ animated: Bool) {
         super.viewWillDisappear(animated)
 
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
     }
@@ -156,53 +159,44 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
     // MARK: - NotificationCenter
 
-    @objc func initialize() {
-
-        self.reloadDataSourceWithCompletion { _ in
-            self.timerSearchNewMedia?.invalidate()
-            self.timerSearchNewMedia = Timer.scheduledTimer(timeInterval: self.timeIntervalSearchNewMedia, target: self, selector: #selector(self.searchNewMediaTimer), userInfo: nil, repeats: false)
-            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                self.mediaCommandTitle()
-            }
-        }
-    }
-
     @objc func deleteFile(_ notification: NSNotification) {
 
         guard let userInfo = notification.userInfo as NSDictionary?,
-              let account = userInfo["account"] as? String,
-              let ocIds = userInfo["ocId"] as? [String],
-              let error = userInfo["error"] as? NKError
-        else { return }
+              let error = userInfo["error"] as? NKError else { return }
+        let onlyLocalCache: Bool = userInfo["onlyLocalCache"] as? Bool ?? false
 
-        if error == .success, account == appDelegate.account {
-            var items: [IndexPath] = []
-            var index: Int = 0
-            for metadata in metadatas {
-                if ocIds.contains(metadata.ocId) {
-                    self.metadatas.remove(at: index)
-                    items.append(IndexPath(row: index, section: 0))
-                }
-                if ocIds.count == items.count { break }
-                index += 1
-            }
-            if ocIds.count == items.count {
-                self.collectionView?.deleteItems(at: items)
-            } else {
-                self.reloadDataSourceWithCompletion { _ in }
+        self.queryDB(isForced: true)
+
+        if error == .success, let indexPath = userInfo["indexPath"] as? [IndexPath], !indexPath.isEmpty, !onlyLocalCache {
+            collectionView?.performBatchUpdates({
+                collectionView?.deleteItems(at: indexPath)
+            }, completion: { _ in
+                self.collectionView?.reloadData()
+            })
+        } else {
+            if error != .success {
+                NCContentPresenter.shared.showError(error: error)
             }
+            self.collectionView?.reloadData()
+        }
+
+        if let hud = userInfo["hud"] as? JGProgressHUD {
+            hud.dismiss()
         }
-        self.updateMediaControlVisibility()
     }
 
     @objc func moveFile(_ notification: NSNotification) {
 
-        guard let userInfo = notification.userInfo as NSDictionary?,
-              let account = userInfo["account"] as? String,
-              account == appDelegate.account
-        else { return }
+        guard let userInfo = notification.userInfo as NSDictionary? else { return }
 
-        self.reloadDataSourceWithCompletion { _ in }
+        if let hud = userInfo["hud"] as? JGProgressHUD {
+            hud.dismiss()
+        }
+    }
+
+    @objc func copyFile(_ notification: NSNotification) {
+
+        moveFile(notification)
     }
 
     @objc func renameFile(_ notification: NSNotification) {
@@ -280,7 +274,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
     // MARK: Select Path
 
-    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
+    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], indexPath: [IndexPath], overwrite: Bool, copy: Bool, move: Bool) {
 
         guard let serverUrl = serverUrl else { return }
         let path = CCUtility.returnPathfromServerUrl(serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId, account: appDelegate.account) ?? ""
@@ -314,10 +308,12 @@ extension NCMedia: UICollectionViewDelegate {
         if isEditMode {
             if let index = selectOcId.firstIndex(of: metadata.ocId) {
                 selectOcId.remove(at: index)
+                selectIndexPath.removeAll(where: { $0 == indexPath })
             } else {
                 selectOcId.append(metadata.ocId)
+                selectIndexPath.append(indexPath)
             }
-            if indexPath.section <  collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
+            if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
                 collectionView.reloadItems(at: [indexPath])
             }
         } else {
@@ -338,7 +334,7 @@ extension NCMedia: UICollectionViewDelegate {
         return UIContextMenuConfiguration(identifier: identifier, previewProvider: {
             return NCViewerProviderContextMenu(metadata: metadata, image: image)
         }, actionProvider: { _ in
-            return NCContextMenu().viewMenu(ocId: metadata.ocId, viewController: self, image: image)
+            return NCContextMenu().viewMenu(ocId: metadata.ocId, indexPath: indexPath, viewController: self, image: image)
         })
     }
 
@@ -391,15 +387,17 @@ extension NCMedia: UICollectionViewDataSource {
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 
+        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridMediaCell else { return UICollectionViewCell() }
+
         if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) && indexPath.row < metadatas.count {
 
-            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridMediaCell
             let metadata = metadatas[indexPath.row]
 
             self.cellHeigth = cell.frame.size.height
 
             cell.date = metadata.date as Date
             cell.fileObjectId = metadata.ocId
+            cell.indexPath = indexPath
             cell.fileUser = metadata.ownerId
 
             if metadata.isAudioOrVideo {
@@ -423,7 +421,7 @@ extension NCMedia: UICollectionViewDataSource {
 
         } else {
 
-            return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridMediaCell
+            return cell
         }
     }
 }
@@ -443,14 +441,7 @@ extension NCMedia {
 
     // MARK: - Datasource
 
-    @objc func reloadDataSourceWithCompletion(_ completion: @escaping (_ metadatas: [tableMetadata]) -> Void) {
-        guard !appDelegate.account.isEmpty else { return }
-
-        if account != appDelegate.account {
-            self.metadatas = []
-            account = appDelegate.account
-            collectionView?.reloadData()
-        }
+    func queryDB(isForced: Bool) {
 
         livePhoto = CCUtility.getLivePhoto()
 
@@ -470,18 +461,32 @@ extension NCMedia {
         }
 
         guard let predicate = predicate else { return }
+
+        self.metadatas = NCManageDatabase.shared.getMetadatasMedia(predicate: predicate, livePhoto: self.livePhoto)
+
+        switch CCUtility.getMediaSortDate() {
+        case "date":
+            self.metadatas = self.metadatas.sorted(by: {($0.date as Date) > ($1.date as Date)})
+        case "creationDate":
+            self.metadatas = self.metadatas.sorted(by: {($0.creationDate as Date) > ($1.creationDate as Date)})
+        case "uploadDate":
+            self.metadatas = self.metadatas.sorted(by: {($0.uploadDate as Date) > ($1.uploadDate as Date)})
+        default:
+            break
+        }
+    }
+
+    @objc func reloadDataSourceWithCompletion(_ completion: @escaping (_ metadatas: [tableMetadata]) -> Void) {
+        guard !appDelegate.account.isEmpty else { return }
+
+        if account != appDelegate.account {
+            self.metadatas = []
+            account = appDelegate.account
+            DispatchQueue.main.async { self.collectionView?.reloadData() }
+        }
+
         DispatchQueue.global().async {
-            self.metadatas = NCManageDatabase.shared.getMetadatasMedia(predicate: predicate, livePhoto: self.livePhoto)
-            switch CCUtility.getMediaSortDate() {
-            case "date":
-                self.metadatas = self.metadatas.sorted(by: {($0.date as Date) > ($1.date as Date)} )
-            case "creationDate":
-                self.metadatas = self.metadatas.sorted(by: {($0.creationDate as Date) > ($1.creationDate as Date)} )
-            case "uploadDate":
-                self.metadatas = self.metadatas.sorted(by: {($0.uploadDate as Date) > ($1.uploadDate as Date)} )
-            default:
-                break
-            }
+            self.queryDB(isForced: true)
             DispatchQueue.main.sync {
                 self.reloadDataThenPerform {
                     self.updateMediaControlVisibility()
@@ -494,7 +499,7 @@ extension NCMedia {
 
     func updateMediaControlVisibility() {
 
-        if self.metadatas.count == 0 {
+        if self.metadatas.isEmpty {
             if !self.filterClassTypeImage && !self.filterClassTypeVideo {
                 self.mediaCommandView?.toggleEmptyView(isEmpty: true)
                 self.mediaCommandView?.isHidden = false
@@ -513,7 +518,14 @@ extension NCMedia {
     private func searchOldMedia(value: Int = -30, limit: Int = 300) {
 
         if oldInProgress { return } else { oldInProgress = true }
-        collectionView.reloadData()
+        DispatchQueue.main.async {
+            self.collectionView.reloadData()
+            var bottom: CGFloat = 0
+            if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
+                bottom = -mainTabBar.getHeight()
+            }
+            NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom - 5, style: .medium)
+        }
 
         var lessDate = Date()
         if predicateDefault != nil {
@@ -529,28 +541,24 @@ extension NCMedia {
             greaterDate = Calendar.current.date(byAdding: .day, value: value, to: lessDate)!
         }
 
-        var bottom: CGFloat = 0
-        if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
-            bottom = -mainTabBar.getHight()
-        }
-        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .medium)
+        let options = NKRequestOptions(timeout: 300, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
 
-        let options = NKRequestOptions(timeout: 300)
-        
-        NextcloudKit.shared.searchMedia(path: mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), options: options) { account, files, data, error in
+        NextcloudKit.shared.searchMedia(path: mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), options: options) { account, files, _, error in
 
             self.oldInProgress = false
-            NCActivityIndicator.shared.stop()
-            self.collectionView.reloadData()
+            DispatchQueue.main.async {
+                NCActivityIndicator.shared.stop()
+                self.collectionView.reloadData()
+            }
 
             if error == .success && account == self.appDelegate.account {
-                if files.count > 0 {
+                if !files.isEmpty {
                     NCManageDatabase.shared.convertFilesToMetadatas(files, useMetadataFolder: false) { _, _, metadatas in
                         let predicateDate = NSPredicate(format: "date > %@ AND date < %@", greaterDate as NSDate, lessDate as NSDate)
                         let predicateResult = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateDate, self.predicateDefault!])
                         let metadatasResult = NCManageDatabase.shared.getMetadatas(predicate: predicateResult)
                         let metadatasChanged = NCManageDatabase.shared.updateMetadatas(metadatas, metadatasResult: metadatasResult, addCompareLivePhoto: false)
-                        if metadatasChanged.metadatasUpdate.count == 0 {
+                        if metadatasChanged.metadatasUpdate.isEmpty {
                             self.researchOldMedia(value: value, limit: limit, withElseReloadDataSource: true)
                         } else {
                             self.reloadDataSourceWithCompletion { _ in }
@@ -616,24 +624,26 @@ extension NCMedia {
 
         reloadDataThenPerform {
 
-            let options = NKRequestOptions(timeout: 300)
-            
-            NextcloudKit.shared.searchMedia(path: self.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), options: options) { account, files, data, error in
+            let options = NKRequestOptions(timeout: 300, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
+
+            NextcloudKit.shared.searchMedia(path: self.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), options: options) { account, files, _, error in
 
                 self.newInProgress = false
-                self.mediaCommandView?.activityIndicator.stopAnimating()
+                DispatchQueue.main.async {
+                    self.mediaCommandView?.activityIndicator.stopAnimating()
+                }
 
-                if error == .success && account == self.appDelegate.account && files.count > 0 {
+                if error == .success, account == self.appDelegate.account, !files.isEmpty {
                     NCManageDatabase.shared.convertFilesToMetadatas(files, useMetadataFolder: false) { _, _, metadatas in
                         let predicate = NSPredicate(format: "date > %@ AND date < %@", greaterDate as NSDate, lessDate as NSDate)
                         let predicateResult = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, self.predicate!])
                         let metadatasResult = NCManageDatabase.shared.getMetadatas(predicate: predicateResult)
                         let updateMetadatas = NCManageDatabase.shared.updateMetadatas(metadatas, metadatasResult: metadatasResult, addCompareLivePhoto: false)
-                        if updateMetadatas.metadatasUpdate.count > 0 || updateMetadatas.metadatasDelete.count > 0 {
+                        if !updateMetadatas.metadatasUpdate.isEmpty || !updateMetadatas.metadatasDelete.isEmpty {
                             self.reloadDataSourceWithCompletion { _ in }
                         }
                     }
-                } else if error == .success && files.count == 0 && self.metadatas.count == 0 {
+                } else if error == .success, files.isEmpty, self.metadatas.isEmpty {
                     self.searchOldMedia()
                 } else if error != .success {
                     NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Media search new media error code \(error.errorCode) " + error.errorDescription)
@@ -649,7 +659,7 @@ extension NCMedia: UIScrollViewDelegate {
 
     func scrollViewDidScroll(_ scrollView: UIScrollView) {
 
-        if lastContentOffsetY == 0 || lastContentOffsetY + cellHeigth/2 <= scrollView.contentOffset.y  || lastContentOffsetY - cellHeigth/2 >= scrollView.contentOffset.y {
+        if lastContentOffsetY == 0 || lastContentOffsetY + cellHeigth / 2 <= scrollView.contentOffset.y || lastContentOffsetY - cellHeigth / 2 >= scrollView.contentOffset.y {
 
             mediaCommandTitle()
             lastContentOffsetY = scrollView.contentOffset.y

+ 4 - 4
iOSClient/Media/NCMediaCommandView.xib

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina6_12" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
         <capability name="Image references" minToolsVersion="12.0"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -29,7 +29,7 @@
                                 </constraints>
                                 <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <inset key="imageEdgeInsets" minX="12" minY="12" maxX="12" maxY="12"/>
-                                <state key="normal" image="plus.slash.minus" catalog="system">
+                                <state key="normal" image="plus.forwardslash.minus" catalog="system">
                                     <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 </state>
                                 <state key="disabled">
@@ -173,6 +173,6 @@
         <image name="ellipsis" catalog="system" width="128" height="37"/>
         <image name="minus" catalog="system" width="128" height="26"/>
         <image name="plus" catalog="system" width="128" height="113"/>
-        <image name="plus.slash.minus" catalog="system" width="128" height="115"/>
+        <image name="plus.forwardslash.minus" catalog="system" width="128" height="115"/>
     </resources>
 </document>

+ 72 - 65
iOSClient/Menu/AppDelegate+Menu.swift

@@ -33,19 +33,19 @@ extension AppDelegate {
 
         var actions: [NCMenuAction] = []
 
+        // swiftlint:disable force_cast
         let appDelegate = UIApplication.shared.delegate as! AppDelegate
+        // swiftlint:enable force_cast
+
         let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: appDelegate.account)
         let isDirectoryE2EE = NCUtility.shared.isDirectoryE2EE(serverUrl: appDelegate.activeServerUrl, userBase: appDelegate)
         let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, appDelegate.activeServerUrl))
-        let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
-
 
         actions.append(
             NCMenuAction(
                 title: NSLocalizedString("_upload_photos_videos_", comment: ""), icon: UIImage(named: "file_photo")!.image(color: UIColor.systemGray, size: 50), action: { _ in
                     NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: viewController) { hasPermission in
-                        if hasPermission {
-                            NCPhotosPickerViewController.init(viewController: viewController, maxSelectedAssets: 0, singleSelectedMode: false)
+                        if hasPermission {NCPhotosPickerViewController(viewController: viewController, maxSelectedAssets: 0, singleSelectedMode: false)
                         }
                     }
                 }
@@ -71,14 +71,15 @@ extension AppDelegate {
                     }
                     navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                    let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                    viewController.editorId = NCGlobal.shared.editorText
-                    viewController.creatorId = directEditingCreator.identifier
-                    viewController.typeTemplate = NCGlobal.shared.templateDocument
-                    viewController.serverUrl = appDelegate.activeServerUrl
-                    viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
+                    if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                        viewController.editorId = NCGlobal.shared.editorText
+                        viewController.creatorId = directEditingCreator.identifier
+                        viewController.typeTemplate = NCGlobal.shared.templateDocument
+                        viewController.serverUrl = appDelegate.activeServerUrl
+                        viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
 
-                    appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                        appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                    }
                 })
             )
         }
@@ -99,14 +100,15 @@ extension AppDelegate {
                     NCAskAuthorization.shared.askAuthorizationAudioRecord(viewController: viewController) { hasPermission in
                         if hasPermission {
                             let fileName = CCUtility.createFileNameDate(NSLocalizedString("_voice_memo_filename_", comment: ""), extension: "m4a")!
-                            let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as! NCAudioRecorderViewController
+                            if let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController {
 
-                            viewController.delegate = self
-                            viewController.createRecorder(fileName: fileName)
-                            viewController.modalTransitionStyle = .crossDissolve
-                            viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
+                                viewController.delegate = self
+                                viewController.createRecorder(fileName: fileName)
+                                viewController.modalTransitionStyle = .crossDissolve
+                                viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
 
-                            appDelegate.window?.rootViewController?.present(viewController, animated: true, completion: nil)
+                                appDelegate.window?.rootViewController?.present(viewController, animated: true, completion: nil)
+                            }
                         }
                     }
                 }
@@ -121,17 +123,16 @@ extension AppDelegate {
         let imageCreateFolder = isDirectoryE2EE ? UIImage(named: "folderEncrypted")! : UIImage(named: "folder")!
         actions.append(
             NCMenuAction(title: titleCreateFolder,
-                icon: imageCreateFolder.image(color: NCBrandColor.shared.brandElement, size: 50), action: { _ in
-                    guard !appDelegate.activeServerUrl.isEmpty else { return }
-                    let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate)
-                    appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil)
-                }
-            )
+                         icon: imageCreateFolder.image(color: NCBrandColor.shared.brandElement, size: 50), action: { _ in
+                             guard !appDelegate.activeServerUrl.isEmpty else { return }
+                             let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate)
+                             appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil)
+                         }
+                        )
         )
 
-        // Folder encrypted (ONLY ROOT)
-        if serverUrlHome == appDelegate.activeServerUrl && CCUtility.isEnd(toEndEnabled: appDelegate.account) {
-        //if !isDirectoryE2EE && CCUtility.isEnd(toEndEnabled: appDelegate.account) {
+        // Folder encrypted
+        if !isDirectoryE2EE && CCUtility.isEnd(toEndEnabled: appDelegate.account) {
             actions.append(
                 NCMenuAction(title: NSLocalizedString("_create_folder_e2ee_", comment: ""),
                              icon: UIImage(named: "folderEncrypted")!.image(color: NCBrandColor.shared.brandElement, size: 50),
@@ -174,14 +175,15 @@ extension AppDelegate {
                         }
                         navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                        let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                        viewController.editorId = NCGlobal.shared.editorOnlyoffice
-                        viewController.creatorId = directEditingCreator.identifier
-                        viewController.typeTemplate = NCGlobal.shared.templateDocument
-                        viewController.serverUrl = appDelegate.activeServerUrl
-                        viewController.titleForm = NSLocalizedString("_create_new_document_", comment: "")
+                        if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                            viewController.editorId = NCGlobal.shared.editorOnlyoffice
+                            viewController.creatorId = directEditingCreator.identifier
+                            viewController.typeTemplate = NCGlobal.shared.templateDocument
+                            viewController.serverUrl = appDelegate.activeServerUrl
+                            viewController.titleForm = NSLocalizedString("_create_new_document_", comment: "")
 
-                        appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                            appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                        }
                     }
                 )
             )
@@ -197,14 +199,15 @@ extension AppDelegate {
                         }
                         navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                        let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                        viewController.editorId = NCGlobal.shared.editorOnlyoffice
-                        viewController.creatorId = directEditingCreator.identifier
-                        viewController.typeTemplate = NCGlobal.shared.templateSpreadsheet
-                        viewController.serverUrl = appDelegate.activeServerUrl
-                        viewController.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
+                        if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                            viewController.editorId = NCGlobal.shared.editorOnlyoffice
+                            viewController.creatorId = directEditingCreator.identifier
+                            viewController.typeTemplate = NCGlobal.shared.templateSpreadsheet
+                            viewController.serverUrl = appDelegate.activeServerUrl
+                            viewController.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
 
-                        appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                            appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                        }
                     }
                 )
             )
@@ -220,14 +223,15 @@ extension AppDelegate {
                         }
                         navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                        let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                        viewController.editorId = NCGlobal.shared.editorOnlyoffice
-                        viewController.creatorId = directEditingCreator.identifier
-                        viewController.typeTemplate = NCGlobal.shared.templatePresentation
-                        viewController.serverUrl = appDelegate.activeServerUrl
-                        viewController.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")
+                        if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                            viewController.editorId = NCGlobal.shared.editorOnlyoffice
+                            viewController.creatorId = directEditingCreator.identifier
+                            viewController.typeTemplate = NCGlobal.shared.templatePresentation
+                            viewController.serverUrl = appDelegate.activeServerUrl
+                            viewController.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")
 
-                        appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                            appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                        }
                     }
                 )
             )
@@ -243,13 +247,14 @@ extension AppDelegate {
                             }
                             navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                            let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                            viewController.editorId = NCGlobal.shared.editorCollabora
-                            viewController.typeTemplate = NCGlobal.shared.templateDocument
-                            viewController.serverUrl = appDelegate.activeServerUrl
-                            viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
+                            if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                                viewController.editorId = NCGlobal.shared.editorCollabora
+                                viewController.typeTemplate = NCGlobal.shared.templateDocument
+                                viewController.serverUrl = appDelegate.activeServerUrl
+                                viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
 
-                            appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                                appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                            }
                         }
                     )
                 )
@@ -262,13 +267,14 @@ extension AppDelegate {
                             }
                             navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                            let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                            viewController.editorId = NCGlobal.shared.editorCollabora
-                            viewController.typeTemplate = NCGlobal.shared.templateSpreadsheet
-                            viewController.serverUrl = appDelegate.activeServerUrl
-                            viewController.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
+                            if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                                viewController.editorId = NCGlobal.shared.editorCollabora
+                                viewController.typeTemplate = NCGlobal.shared.templateSpreadsheet
+                                viewController.serverUrl = appDelegate.activeServerUrl
+                                viewController.titleForm = NSLocalizedString("_create_new_spreadsheet_", comment: "")
 
-                            appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                                appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                            }
                         }
                     )
                 )
@@ -281,13 +287,14 @@ extension AppDelegate {
                             }
                             navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
 
-                            let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
-                            viewController.editorId = NCGlobal.shared.editorCollabora
-                            viewController.typeTemplate = NCGlobal.shared.templatePresentation
-                            viewController.serverUrl = appDelegate.activeServerUrl
-                            viewController.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")
+                            if let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments {
+                                viewController.editorId = NCGlobal.shared.editorCollabora
+                                viewController.typeTemplate = NCGlobal.shared.templatePresentation
+                                viewController.serverUrl = appDelegate.activeServerUrl
+                                viewController.titleForm = NSLocalizedString("_create_new_presentation_", comment: "")
 
-                            appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                                appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                            }
                         }
                     )
                 )

+ 20 - 25
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -32,20 +32,19 @@ import Queuer
 
 extension NCCollectionViewCommon {
 
-    func toggleMenu(metadata: tableMetadata, imageIcon: UIImage?) {
+    func toggleMenu(metadata: tableMetadata, indexPath: IndexPath, imageIcon: UIImage?) {
 
         var actions = [NCMenuAction]()
 
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) else { return }
         let serverUrl = metadata.serverUrl + "/" + metadata.fileName
-        let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
-        let isOffline: Bool
+        var isOffline: Bool = false
 
         if metadata.directory, let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) {
             isOffline = directory.offline
         } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
             isOffline = localFile.offline
-        } else { isOffline = false }
+        }
 
         let editors = NCUtility.shared.isDirectEditing(account: metadata.account, contentType: metadata.contentType)
         let isRichDocument = NCUtility.shared.isRichDocument(metadata)
@@ -130,7 +129,7 @@ extension NCCollectionViewCommon {
                     title: NSLocalizedString("_view_in_folder_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "questionmark.folder"),
                     order: 21,
-                    action: { menuAction in
+                    action: { _ in
                         NCActionCenter.shared.openFileViewInFolder(serverUrl: metadata.serverUrl, fileNameBlink: metadata.fileName, fileNameOpen: nil)
                     }
                 )
@@ -145,24 +144,19 @@ extension NCCollectionViewCommon {
         }
 
         //
-        // SET FOLDER E2EE (ONLY ROOT)
+        // SET FOLDER E2EE
         //
-        if metadata.serverUrl == serverUrlHome, metadata.isDirectoySettableE2EE {
+        if metadata.isDirectoySettableE2EE {
             actions.append(
                 NCMenuAction(
                     title: NSLocalizedString("_e2e_set_folder_encrypted_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "lock"),
                     order: 30,
                     action: { _ in
-                        NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: false) { account, error in
-                            if error == .success {
-                                NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, serverUrl))
-                                NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, serverUrlTo: nil, etag: nil, ocId: nil, fileId: nil, encrypted: true, richWorkspace: nil, account: metadata.account)
-                                NCManageDatabase.shared.setMetadataEncrypted(ocId: metadata.ocId, encrypted: true)
-
-                                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeStatusFolderE2EE, userInfo: ["serverUrl": metadata.serverUrl])
-                            } else {
-                                NCContentPresenter.shared.messageNotification(NSLocalizedString("_e2e_error_mark_folder_", comment: ""), error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .error)
+                        Task {
+                            let error = await NCNetworkingE2EEMarkFolder().markFolderE2ee(account: metadata.account, fileName: metadata.fileName, serverUrl: metadata.serverUrl, userId: metadata.userId)
+                            if error != .success {
+                                NCContentPresenter.shared.showError(error: error)
                             }
                         }
                     }
@@ -180,7 +174,7 @@ extension NCCollectionViewCommon {
                     icon: NCUtility.shared.loadImage(named: "lock"),
                     order: 30,
                     action: { _ in
-                        NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: true) { account, error in
+                        NextcloudKit.shared.markE2EEFolder(fileId: metadata.fileId, delete: true) { _, error in
                             if error == .success {
                                 NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, serverUrl))
                                 NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, serverUrlTo: nil, etag: nil, ocId: nil, fileId: nil, encrypted: false, richWorkspace: nil, account: metadata.account)
@@ -188,7 +182,7 @@ extension NCCollectionViewCommon {
 
                                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeStatusFolderE2EE, userInfo: ["serverUrl": metadata.serverUrl])
                             } else {
-                                NCContentPresenter.shared.messageNotification(NSLocalizedString("_e2e_error_delete_mark_folder_", comment: ""), error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .error)
+                                NCContentPresenter.shared.messageNotification(NSLocalizedString("_e2e_error_", comment: ""), error: error, delay: NCGlobal.shared.dismissAfterSecond, type: .error)
                             }
                         }
                     }
@@ -247,7 +241,7 @@ extension NCCollectionViewCommon {
                 icon = NCUtility.shared.loadImage(named: "collabora")
             }
 
-            if editor != "" {
+            if !editor.isEmpty {
                 actions.append(
                     NCMenuAction(
                         title: title,
@@ -317,6 +311,7 @@ extension NCCollectionViewCommon {
 
                             vcRename.metadata = metadata
                             vcRename.imagePreview = imageIcon
+                            vcRename.indexPath = indexPath
 
                             let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height)
 
@@ -331,16 +326,16 @@ extension NCCollectionViewCommon {
         // COPY - MOVE
         //
         if metadata.isCopyableMovable {
-            actions.append(.moveOrCopyAction(selectedMetadatas: [metadata], order: 130))
+            actions.append(.moveOrCopyAction(selectedMetadatas: [metadata], indexPath: [indexPath], order: 130))
         }
 
         //
         // COPY IN PASTEBOARD
         //
         if metadata.isCopyableInPasteboard {
-            actions.append(.copyAction(selectOcId: [metadata.ocId], hudView: self.view, order: 140))
+            actions.append(.copyAction(selectOcId: [metadata.ocId], order: 140))
         }
-        
+
         //
         // MODIFY WITH QUICK LOOK
         //
@@ -350,7 +345,7 @@ extension NCCollectionViewCommon {
                     title: NSLocalizedString("_modify_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "pencil.tip.crop.circle"),
                     order: 150,
-                    action: { menuAction in
+                    action: { _ in
                         if CCUtility.fileProviderStorageExists(metadata) {
                             NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": NCGlobal.shared.selectorLoadFileQuickLook, "error": NKError(), "account": metadata.account])
                         } else {
@@ -381,12 +376,12 @@ extension NCCollectionViewCommon {
                 )
             )
         }
-        
+
         //
         // DELETE
         //
         if metadata.isDeletable {
-            actions.append(.deleteAction(selectedMetadatas: [metadata], metadataFolder: metadataFolder, viewController: self, order: 170))
+            actions.append(.deleteAction(selectedMetadatas: [metadata], indexPath: [indexPath], metadataFolder: metadataFolder, viewController: self, order: 170))
         }
 
         applicationHandle.addCollectionViewCommonMenu(metadata: metadata, imageIcon: imageIcon, actions: &actions)

+ 11 - 13
iOSClient/Menu/NCContextMenu.swift

@@ -28,7 +28,7 @@ import JGProgressHUD
 
 class NCContextMenu: NSObject {
 
-    func viewMenu(ocId: String, viewController: UIViewController, image: UIImage?) -> UIMenu {
+    func viewMenu(ocId: String, indexPath: IndexPath, viewController: UIViewController, image: UIImage?) -> UIMenu {
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return UIMenu() }
 
         var downloadRequest: DownloadRequest?
@@ -41,6 +41,7 @@ class NCContextMenu: NSObject {
 
         let hud = JGProgressHUD()
         hud.indicatorView = JGProgressHUDRingIndicatorView()
+        hud.textLabel.text = NSLocalizedString("_downloading_", comment: "")
         hud.detailTextLabel.text = NSLocalizedString("_tap_to_cancel_", comment: "")
         if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView { indicatorView.ringWidth = 1.5 }
         hud.tapOnHUDViewBlock = { _ in
@@ -97,7 +98,7 @@ class NCContextMenu: NSObject {
         let save = UIAction(title: titleSave,
                             image: UIImage(systemName: "square.and.arrow.down")) { _ in
             if let metadataMOV = metadataMOV {
-                NCActionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
+                NCOperationQueue.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
             } else {
                 if CCUtility.fileProviderStorageExists(metadata) {
                     NCActionCenter.shared.saveAlbum(metadata: metadata)
@@ -120,13 +121,6 @@ class NCContextMenu: NSObject {
             }
         }
 
-        /*
-        let copy = UIAction(title: NSLocalizedString("_copy_file_", comment: ""),
-                            image: UIImage(systemName: "doc.on.doc")) { _ in
-            NCActionCenter.shared.copyPasteboard(pasteboardOcIds: [metadata.ocId], hudView: viewController.view)
-        }
-        */
-
         let modify = UIAction(title: NSLocalizedString("_modify_", comment: ""),
                               image: UIImage(systemName: "pencil.tip.crop.circle")) { _ in
             if CCUtility.fileProviderStorageExists(metadata) {
@@ -158,16 +152,21 @@ class NCContextMenu: NSObject {
             }
             let alertController = UIAlertController(title: nil, message: nil, preferredStyle: alertStyle)
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_delete_file_", comment: ""), style: .destructive) { _ in
+                let hud = JGProgressHUD()
+                hud.textLabel.text = NSLocalizedString("_deletion_progess_", comment: "")
+                if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
+                   let view = appDelegate.window?.rootViewController?.view {
+                    hud.show(in: view)
+                }
                 Task {
                     var ocId: [String] = []
-                    let account: String = metadata.account
                     let error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
                     if error == .success {
                         ocId.append(metadata.ocId)
                     } else {
                         NCContentPresenter.shared.showError(error: error)
                     }
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["account": account, "ocId": ocId, "error": error])
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": [indexPath], "onlyLocalCache": false, "error": error, "hud": hud])
                 }
             })
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { _ in })
@@ -178,14 +177,13 @@ class NCContextMenu: NSObject {
                                           image: UIImage(systemName: "trash"), attributes: .destructive) { _ in
             Task {
                 var ocId: [String] = []
-                let account: String = metadata.account
                 let error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
                 if error == .success {
                     ocId.append(metadata.ocId)
                 } else {
                     NCContentPresenter.shared.showError(error: error)
                 }
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["account": account, "ocId": ocId, "error": error])
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": [indexPath], "onlyLocalCache": true, "error": error])
             }
         }
 

+ 4 - 5
iOSClient/Menu/NCLoginWeb+Menu.swift

@@ -52,9 +52,8 @@ extension NCLoginWeb {
                     on: account.active == true,
                     action: { _ in
                         if self.appDelegate.account != account.account {
-                            NCManageDatabase.shared.setAccountActive(account.account)
                             self.dismiss(animated: true) {
-                                self.appDelegate.settingAccount(account.account, urlBase: account.urlBase, user: account.user, userId: account.userId, password: CCUtility.getPassword(account.account))
+                                self.appDelegate.changeAccount(account.account, userProfile: nil)
                             }
                         }
                     }
@@ -74,10 +73,10 @@ extension NCLoginWeb {
                     self.appDelegate.deleteAccount(self.appDelegate.account, wipe: false)
                     self.dismiss(animated: true) {
                         let accounts = NCManageDatabase.shared.getAllAccount()
-                        if accounts.count > 0 {
-                            self.appDelegate.changeAccount(accounts.first!.account)
-                        } else {
+                        if accounts.isEmpty {
                             self.appDelegate.openLogin(viewController: nil, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
+                        } else {
+                            self.appDelegate.changeAccount(accounts.first!.account, userProfile: nil)
                         }
                     }
                 }

+ 14 - 11
iOSClient/Menu/NCMedia+Menu.swift

@@ -29,6 +29,7 @@ extension NCMedia {
     func tapSelect() {
         self.isEditMode = false
         self.selectOcId.removeAll()
+        self.selectIndexPath.removeAll()
         self.reloadDataThenPerform { }
     }
 
@@ -39,7 +40,7 @@ extension NCMedia {
         defer { presentMenu(with: actions) }
 
         if !isEditMode {
-            if metadatas.count > 0 {
+            if !metadatas.isEmpty {
                 actions.append(
                     NCMenuAction(
                         title: NSLocalizedString("_select_", comment: ""),
@@ -84,14 +85,16 @@ extension NCMedia {
                     title: NSLocalizedString("_select_media_folder_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "folder"),
                     action: { _ in
-                        let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as! UINavigationController
-                        let viewController = navigationController.topViewController as! NCSelect
+                        if let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as? UINavigationController,
+                           let viewController = navigationController.topViewController as? NCSelect {
 
-                        viewController.delegate = self
-                        viewController.typeOfCommandView = .select
-                        viewController.type = "mediaFolder"
+                            viewController.delegate = self
+                            viewController.typeOfCommandView = .select
+                            viewController.type = "mediaFolder"
+                            viewController.selectIndexPath = self.selectIndexPath
 
-                        self.present(navigationController, animated: true, completion: nil)
+                            self.present(navigationController, animated: true, completion: nil)
+                        }
                     }
                 )
             )
@@ -103,7 +106,7 @@ extension NCMedia {
                     title: NSLocalizedString("_play_from_files_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "play.circle"),
                     action: { _ in
-                        if let tabBarController =  self.appDelegate.window?.rootViewController as? UITabBarController {
+                        if let tabBarController = self.appDelegate.window?.rootViewController as? UITabBarController {
                             self.documentPickerViewController = NCDocumentPickerViewController(tabBarController: tabBarController, isViewerMedia: true, allowsMultipleSelection: false, viewController: self)
                         }
                     }
@@ -207,18 +210,18 @@ extension NCMedia {
             //
             // COPY - MOVE
             //
-            actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, completion: tapSelect))
+            actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, indexPath: selectIndexPath, completion: tapSelect))
 
             //
             // COPY
             //
-            actions.append(.copyAction(selectOcId: selectOcId, hudView: self.view, completion: tapSelect))
+            actions.append(.copyAction(selectOcId: selectOcId, completion: tapSelect))
 
             //
             // DELETE
             // can't delete from cache because is needed for NCMedia view, and if locked can't delete from server either.
             if !selectedMetadatas.contains(where: { $0.lock && $0.lockOwner != appDelegate.userId }) {
-                actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, metadataFolder: nil, viewController: self, completion: tapSelect))
+                actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, indexPath: selectIndexPath, metadataFolder: nil, viewController: self, completion: tapSelect))
             }
         }
     }

+ 1 - 1
iOSClient/Menu/NCMenu+FloatingPanel.swift

@@ -42,7 +42,7 @@ class NCMenuFloatingPanelLayout: FloatingPanelLayout {
         // sometimes UIScreen.main.bounds.size.height is not updated correctly
         // this ensures we use the correct height value
         // can't use `layoutFor size` since menu is dieplayed on top of the whole screen not just the VC
-        let screenHeight = UIApplication.shared.isLandscape
+        let screenHeight = UIDevice.current.orientation.isLandscapeHardCheck
         ? min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
         : max(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
         let window = UIApplication.shared.connectedScenes.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }.first { $0.isKeyWindow }

+ 15 - 13
iOSClient/Menu/NCMenuAction.swift

@@ -25,6 +25,7 @@
 import Foundation
 import UIKit
 import NextcloudKit
+import JGProgressHUD
 
 class NCMenuAction {
     let title: String
@@ -92,20 +93,20 @@ extension NCMenuAction {
     }
 
     /// Copy files to pasteboard
-    static func copyAction(selectOcId: [String], hudView: UIView, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
+    static func copyAction(selectOcId: [String], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
         NCMenuAction(
             title: NSLocalizedString("_copy_file_", comment: ""),
             icon: NCUtility.shared.loadImage(named: "doc.on.doc"),
             order: order,
             action: { _ in
-                NCActionCenter.shared.copyPasteboard(pasteboardOcIds: selectOcId, hudView: hudView)
+                NCActionCenter.shared.copyPasteboard(pasteboardOcIds: selectOcId)
                 completion?()
             }
         )
     }
 
     /// Delete files either from cache or from Nextcloud
-    static func deleteAction(selectedMetadatas: [tableMetadata], metadataFolder: tableMetadata? = nil, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
+    static func deleteAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], metadataFolder: tableMetadata? = nil, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
         var titleDelete = NSLocalizedString("_delete_", comment: "")
         if selectedMetadatas.count > 1 {
             titleDelete = NSLocalizedString("_delete_selected_files_", comment: "")
@@ -145,20 +146,22 @@ extension NCMenuAction {
                     preferredStyle: .alert)
                 if canDeleteServer {
                     alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
+                        let hud = JGProgressHUD()
+                        hud.textLabel.text = NSLocalizedString("_deletion_progess_", comment: "")
+                        if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
+                           let view = appDelegate.window?.rootViewController?.view {
+                            hud.show(in: view)
+                        }
                         Task {
                             var error = NKError()
                             var ocId: [String] = []
-                            let account = selectedMetadatas.first?.account ?? ""
                             for metadata in selectedMetadatas where error == .success {
                                 error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
                                 if error == .success {
                                     ocId.append(metadata.ocId)
                                 }
                             }
-                            if error != .success {
-                                NCContentPresenter.shared.showError(error: error)
-                            }
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["account": account, "ocId": ocId, "error": error])
+                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": false, "error": error, "hud": hud])
                         }
                         completion?()
                     })
@@ -170,7 +173,6 @@ extension NCMenuAction {
                         Task {
                             var error = NKError()
                             var ocId: [String] = []
-                            let account = selectedMetadatas.first?.account ?? ""
                             for metadata in selectedMetadatas where error == .success {
                                 error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
                                 if error == .success {
@@ -180,7 +182,7 @@ extension NCMenuAction {
                             if error != .success {
                                 NCContentPresenter.shared.showError(error: error)
                             }
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["account": account, "ocId": ocId, "error": error])
+                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": true, "error": error])
                         }
                         completion?()
                     })
@@ -220,7 +222,7 @@ extension NCMenuAction {
             action: { _ in
                 for metadata in selectedMediaMetadatas {
                     if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) {
-                        NCActionCenter.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
+                        NCOperationQueue.shared.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV)
                     } else {
                         if CCUtility.fileProviderStorageExists(metadata) {
                             NCActionCenter.shared.saveAlbum(metadata: metadata)
@@ -261,13 +263,13 @@ extension NCMenuAction {
     }
 
     /// Open view that lets the user move or copy the files within Nextcloud
-    static func moveOrCopyAction(selectedMetadatas: [tableMetadata], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
+    static func moveOrCopyAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
         NCMenuAction(
             title: NSLocalizedString("_move_or_copy_selected_files_", comment: ""),
             icon: NCUtility.shared.loadImage(named: "arrow.up.right.square"),
             order: order,
             action: { _ in
-                NCActionCenter.shared.openSelectView(items: selectedMetadatas)
+                NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: indexPath)
                 completion?()
             }
         )

+ 15 - 1
iOSClient/Menu/NCShare+Menu.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import NextcloudKit
 
 extension NCShare {
     func toggleShareMenu(for share: tableShare) {
@@ -62,7 +63,20 @@ extension NCShare {
                 title: NSLocalizedString("_share_unshare_", comment: ""),
                 icon: NCUtility.shared.loadImage(named: "trash"),
                 action: { _ in
-                    self.networking?.unShare(idShare: share.idShare)
+                    Task {
+                        if share.shareType != NCShareCommon.shared.SHARE_TYPE_LINK, let metadata = self.metadata, metadata.e2eEncrypted && NCGlobal.shared.capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
+                            let serverUrl = metadata.serverUrl + "/" + metadata.fileName
+                            if NCNetworkingE2EE.shared.isInUpload(account: metadata.account, serverUrl: serverUrl) {
+                                let error = NKError(errorCode: NCGlobal.shared.errorE2EEUploadInProgress, errorDescription: NSLocalizedString("_e2e_in_upload_", comment: ""))
+                                return NCContentPresenter.shared.showInfo(error: error)
+                            }
+                            let error = await NCNetworkingE2EE().uploadMetadata(account: metadata.account, serverUrl: serverUrl, userId: metadata.userId, addUserId: nil, removeUserId: share.shareWith)
+                            if error != .success {
+                                return NCContentPresenter.shared.showError(error: error)
+                            }
+                        }
+                        self.networking?.unShare(idShare: share.idShare)
+                    }
                 }
             )
         )

+ 1 - 1
iOSClient/Menu/NCSortMenu.swift

@@ -144,6 +144,6 @@ class NCSortMenu: NSObject {
 
         self.sortButton?.setTitle(NSLocalizedString(layoutForView.titleButtonHeader, comment: ""), for: .normal)
         NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl])
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
     }
 }

+ 16 - 0
iOSClient/Menu/NCTrash+Menu.swift

@@ -30,6 +30,22 @@ import NextcloudKit
 extension NCTrash {
     var selectActions: [NCMenuAction] {
         [
+            NCMenuAction(
+                title: NSLocalizedString("_cancel_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "xmark"),
+                action: { _ in
+                    self.tapSelect()
+                }
+            ),
+            NCMenuAction(
+                title: NSLocalizedString("_select_all_", comment: ""),
+                icon: NCUtility.shared.loadImage(named: "checkmark.circle.fill"),
+                action: { _ in
+                    self.selectOcId = self.datasource.map { $0.fileId }
+                    self.collectionView.reloadData()
+                }
+            ),
+            NCMenuAction.seperator(),
             NCMenuAction(
                 title: NSLocalizedString("_trash_restore_selected_", comment: ""),
                 icon: NCUtility.shared.loadImage(named: "restore"),

+ 8 - 7
iOSClient/Menu/NCViewer+Menu.swift

@@ -27,10 +27,10 @@ import NextcloudKit
 
 extension NCViewer {
 
-    func toggleMenu(viewController: UIViewController, metadata: tableMetadata, webView: Bool, imageIcon: UIImage?) {
+    func toggleMenu(viewController: UIViewController, metadata: tableMetadata, webView: Bool, imageIcon: UIImage?, indexPath: IndexPath = IndexPath()) {
 
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) else { return }
-        
+
         var actions = [NCMenuAction]()
         var titleFavorite = NSLocalizedString("_add_favorites_", comment: "")
         if metadata.favorite { titleFavorite = NSLocalizedString("_remove_favorites_", comment: "") }
@@ -60,7 +60,7 @@ extension NCViewer {
                 NCMenuAction(
                     title: NSLocalizedString("_view_in_folder_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "questionmark.folder"),
-                    action: { menuAction in
+                    action: { _ in
                         NCActionCenter.shared.openFileViewInFolder(serverUrl: metadata.serverUrl, fileNameBlink: metadata.fileName, fileNameOpen: nil)
                     }
                 )
@@ -119,7 +119,7 @@ extension NCViewer {
                 )
             )
         }
-        
+
         //
         // CONVERSION VIDEO TO MPEG4 (MFFF Lib)
         //
@@ -182,6 +182,7 @@ extension NCViewer {
                             vcRename.metadata = metadata
                             vcRename.disableChangeExt = true
                             vcRename.imagePreview = imageIcon
+                            vcRename.indexPath = indexPath
 
                             let popup = NCPopupViewController(contentController: vcRename, popupWidth: vcRename.width, popupHeight: vcRename.height)
 
@@ -196,14 +197,14 @@ extension NCViewer {
         // COPY - MOVE
         //
         if !webView, metadata.isCopyableMovable {
-            actions.append(.moveOrCopyAction(selectedMetadatas: [metadata]))
+            actions.append(.moveOrCopyAction(selectedMetadatas: [metadata], indexPath: []))
         }
 
         //
         // COPY IN PASTEBOARD
         //
         if !webView, metadata.isCopyableInPasteboard {
-            actions.append(.copyAction(selectOcId: [metadata.ocId], hudView: viewController.view))
+            actions.append(.copyAction(selectOcId: [metadata.ocId]))
         }
 
         //
@@ -269,7 +270,7 @@ extension NCViewer {
         // DELETE
         //
         if !webView, metadata.isDeletable {
-            actions.append(.deleteAction(selectedMetadatas: [metadata], metadataFolder: nil, viewController: viewController))
+            actions.append(.deleteAction(selectedMetadatas: [metadata], indexPath: [], metadataFolder: nil, viewController: viewController))
         }
 
         viewController.presentMenu(with: actions)

+ 44 - 0
iOSClient/More/Cells/BaseNCMoreCell.swift

@@ -0,0 +1,44 @@
+//
+//  BaseNCMoreCell.swift
+//  Nextcloud
+//
+//  Created by Milen on 15.06.23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+class BaseNCMoreCell: UITableViewCell {
+    let selectionColor: UIView = UIView()
+    let defaultCornerRadius: CGFloat = 10.0
+
+    override var frame: CGRect {
+        get {
+            return super.frame
+        }
+        set (newFrame) {
+            var frame = newFrame
+            let newWidth = frame.width * 0.90
+            let space = (frame.width - newWidth) / 2
+            frame.size.width = newWidth
+            frame.origin.x += space
+            super.frame = frame
+        }
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+
+        selectedBackgroundView = selectionColor
+        backgroundColor = .secondarySystemGroupedBackground
+        applyCornerRadius()
+    }
+
+    func applyCornerRadius() {
+        layer.cornerRadius = defaultCornerRadius
+    }
+
+    func removeCornerRadius() {
+        layer.cornerRadius = 0
+    }
+}

+ 18 - 0
iOSClient/More/Cells/CCCellMore.swift

@@ -0,0 +1,18 @@
+//
+//  CCCellMore.swift
+//  Nextcloud
+//
+//  Created by Milen on 14.06.23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+class CCCellMore: BaseNCMoreCell {
+    @IBOutlet weak var labelText: UILabel!
+    @IBOutlet weak var imageIcon: UIImageView!
+    @IBOutlet weak var separator: UIView!
+    @IBOutlet weak var separatorHeigth: NSLayoutConstraint!
+
+    static let reuseIdentifier = "CCCellMore"
+}

+ 57 - 0
iOSClient/More/Cells/NCMoreAppSuggestionsCell.swift

@@ -0,0 +1,57 @@
+//
+//  NCMoreAppSuggestionsCell.swift
+//  Nextcloud
+//
+//  Created by Milen on 14.06.23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+class NCMoreAppSuggestionsCell: BaseNCMoreCell {
+    @IBOutlet weak var talkView: UIStackView!
+    @IBOutlet weak var notesView: UIStackView!
+    @IBOutlet weak var moreAppsView: UIStackView!
+
+    static let reuseIdentifier = "NCMoreAppSuggestionsCell"
+
+    static func fromNib() -> UINib {
+        return UINib(nibName: "NCMoreAppSuggestionsCell", bundle: nil)
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        backgroundColor = .clear
+
+        talkView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(talkTapped)))
+        notesView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(notesTapped)))
+        moreAppsView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(moreAppsTapped)))
+    }
+
+    @objc func talkTapped() {
+        guard let url = URL(string: NCGlobal.shared.talkSchemeUrl) else { return }
+
+        if UIApplication.shared.canOpenURL(url) {
+            UIApplication.shared.open(url)
+        } else {
+            guard let url = URL(string: NCGlobal.shared.talkAppStoreUrl) else { return }
+            UIApplication.shared.open(url)
+        }
+    }
+
+    @objc func notesTapped() {
+        guard let url = URL(string: NCGlobal.shared.notesSchemeUrl) else { return }
+
+        if UIApplication.shared.canOpenURL(url) {
+            UIApplication.shared.open(url)
+        } else {
+            guard let url = URL(string: NCGlobal.shared.notesAppStoreUrl) else { return }
+            UIApplication.shared.open(url)
+        }
+    }
+
+    @objc func moreAppsTapped() {
+        guard let url = URL(string: NCGlobal.shared.moreAppsUrl) else { return }
+        UIApplication.shared.open(url)
+    }
+}

+ 157 - 0
iOSClient/More/Cells/NCMoreAppSuggestionsCell.xib

@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22113.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="dark"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22089.1"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="0.0" id="dVh-cS-UwU" userLabel="App Suggestion Cell" customClass="NCMoreAppSuggestionsCell" customModule="Nextcloud" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="1076" height="44"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="dVh-cS-UwU" id="9Ma-CX-ckc">
+                <rect key="frame" x="0.0" y="0.0" width="1076" height="44"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="ppx-X2-oTM">
+                        <rect key="frame" x="0.0" y="0.0" width="1076" height="44"/>
+                        <subviews>
+                            <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalCentering" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Tbt-MZ-3jf">
+                                <rect key="frame" x="0.0" y="0.0" width="353.33333333333331" height="44"/>
+                                <subviews>
+                                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="749" image="talk-template" translatesAutoresizingMaskIntoConstraints="NO" id="Uby-L5-yV4" userLabel="Icon">
+                                        <rect key="frame" x="167.66666666666666" y="8" width="18" height="18"/>
+                                        <color key="tintColor" systemColor="linkColor"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="18" id="Vl1-Ip-OUw"/>
+                                            <constraint firstAttribute="width" constant="18" id="m7P-Or-Esh"/>
+                                        </constraints>
+                                    </imageView>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Talk" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9cY-LF-9EI">
+                                        <rect key="frame" x="167" y="26" width="19.333333333333343" height="10"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                        <color key="textColor" systemColor="systemBlueColor"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="bottom" secondItem="9cY-LF-9EI" secondAttribute="bottom" constant="8" id="BIL-hp-tNO"/>
+                                    <constraint firstItem="9cY-LF-9EI" firstAttribute="top" secondItem="Uby-L5-yV4" secondAttribute="bottom" id="twR-46-lPV"/>
+                                    <constraint firstItem="Uby-L5-yV4" firstAttribute="top" secondItem="Tbt-MZ-3jf" secondAttribute="top" constant="8" id="xcP-gx-xol"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+                                        <integer key="value" value="8"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </stackView>
+                            <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalCentering" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="4jA-wm-kCc">
+                                <rect key="frame" x="361.33333333333337" y="0.0" width="353.33333333333337" height="44"/>
+                                <subviews>
+                                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="749" image="notes-template" translatesAutoresizingMaskIntoConstraints="NO" id="BT7-Nt-9RH" userLabel="Icon">
+                                        <rect key="frame" x="167.66666666666669" y="8" width="18" height="18"/>
+                                        <color key="tintColor" systemColor="linkColor"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="18" id="3YI-i4-Ykd"/>
+                                            <constraint firstAttribute="width" constant="18" id="Riu-Cr-O0l"/>
+                                        </constraints>
+                                    </imageView>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Notes" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tQM-o0-e3W">
+                                        <rect key="frame" x="162.33333333333331" y="26" width="28.666666666666657" height="10"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                        <color key="textColor" systemColor="systemBlueColor"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
+                                <constraints>
+                                    <constraint firstItem="tQM-o0-e3W" firstAttribute="top" secondItem="BT7-Nt-9RH" secondAttribute="bottom" id="1ho-7Y-4Ty"/>
+                                    <constraint firstItem="BT7-Nt-9RH" firstAttribute="top" secondItem="4jA-wm-kCc" secondAttribute="top" constant="8" id="4QH-zk-5ph"/>
+                                    <constraint firstAttribute="bottom" secondItem="tQM-o0-e3W" secondAttribute="bottom" constant="8" id="E3e-ra-dwn"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+                                        <integer key="value" value="8"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </stackView>
+                            <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalCentering" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="SA6-fX-Xmx">
+                                <rect key="frame" x="722.66666666666663" y="0.0" width="353.33333333333337" height="44"/>
+                                <subviews>
+                                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="749" image="more-apps-template" translatesAutoresizingMaskIntoConstraints="NO" id="dzE-0b-iBn" userLabel="Icon">
+                                        <rect key="frame" x="167.66666666666674" y="8" width="18" height="18"/>
+                                        <color key="tintColor" systemColor="linkColor"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="18" id="Yo0-sL-LJN"/>
+                                            <constraint firstAttribute="height" constant="18" id="bOc-vN-4Ry"/>
+                                        </constraints>
+                                    </imageView>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="More apps" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GIQ-5h-VeA">
+                                        <rect key="frame" x="151.33333333333337" y="26" width="51" height="12"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                        <color key="textColor" systemColor="systemBlueColor"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="bottom" secondItem="GIQ-5h-VeA" secondAttribute="bottom" constant="8" id="8xI-Ad-3Gc"/>
+                                    <constraint firstItem="dzE-0b-iBn" firstAttribute="top" secondItem="SA6-fX-Xmx" secondAttribute="top" constant="8" id="ixx-ut-Ss2"/>
+                                    <constraint firstItem="GIQ-5h-VeA" firstAttribute="top" secondItem="dzE-0b-iBn" secondAttribute="bottom" id="rWG-kv-J2m"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+                                        <integer key="value" value="8"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </stackView>
+                        </subviews>
+                        <constraints>
+                            <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="400" id="D00-QX-V4P"/>
+                            <constraint firstAttribute="bottom" secondItem="Tbt-MZ-3jf" secondAttribute="bottom" id="hS2-sl-Kz0"/>
+                            <constraint firstItem="Tbt-MZ-3jf" firstAttribute="top" secondItem="ppx-X2-oTM" secondAttribute="top" id="u4O-GE-2yF"/>
+                        </constraints>
+                    </stackView>
+                </subviews>
+                <constraints>
+                    <constraint firstAttribute="bottom" secondItem="ppx-X2-oTM" secondAttribute="bottom" id="Ler-rD-wfw"/>
+                    <constraint firstAttribute="trailing" secondItem="ppx-X2-oTM" secondAttribute="trailing" id="XRL-5j-YM1"/>
+                    <constraint firstItem="ppx-X2-oTM" firstAttribute="top" secondItem="9Ma-CX-ckc" secondAttribute="top" id="Yxe-5b-StO"/>
+                    <constraint firstItem="ppx-X2-oTM" firstAttribute="centerX" secondItem="9Ma-CX-ckc" secondAttribute="centerX" id="mtt-g7-1xb"/>
+                    <constraint firstItem="ppx-X2-oTM" firstAttribute="leading" secondItem="9Ma-CX-ckc" secondAttribute="leading" id="yEM-LQ-UIV"/>
+                </constraints>
+                <variation key="widthClass=regular">
+                    <mask key="constraints">
+                        <exclude reference="XRL-5j-YM1"/>
+                        <exclude reference="yEM-LQ-UIV"/>
+                    </mask>
+                </variation>
+            </tableViewCellContentView>
+            <connections>
+                <outlet property="moreAppsView" destination="SA6-fX-Xmx" id="fiE-FN-en3"/>
+                <outlet property="notesView" destination="4jA-wm-kCc" id="XuQ-8X-RDH"/>
+                <outlet property="talkView" destination="Tbt-MZ-3jf" id="1Tf-ff-0k1"/>
+            </connections>
+            <point key="canvasLocation" x="209.30232558139534" y="-65.665236051502148"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="more-apps-template" width="32" height="32"/>
+        <image name="notes-template" width="24" height="24"/>
+        <image name="talk-template" width="600" height="600"/>
+        <systemColor name="linkColor">
+            <color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+        <systemColor name="secondarySystemGroupedBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemBlueColor">
+            <color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
+</document>

+ 23 - 0
iOSClient/More/Cells/NCMoreUserCell.swift

@@ -0,0 +1,23 @@
+//
+//  NCMoreUserCell.swift
+//  Nextcloud
+//
+//  Created by Milen on 14.06.23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import MarqueeLabel
+
+class NCMoreUserCell: BaseNCMoreCell {
+    @IBOutlet weak var displayName: UILabel!
+    @IBOutlet weak var avatar: UIImageView!
+    @IBOutlet weak var icon: UIImageView!
+    @IBOutlet weak var status: MarqueeLabel!
+
+    static let reuseIdentifier = "NCMoreUserCell"
+
+    static func fromNib() -> UINib {
+        return UINib(nibName: "NCMoreUserCell", bundle: nil)
+    }
+}

+ 3 - 3
iOSClient/More/NCMoreUserCell.xib → iOSClient/More/Cells/NCMoreUserCell.xib

@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomCellFileAndDirectory"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
-        <tableViewCell contentMode="scaleToFill" selectionStyle="blue" indentationWidth="0.0" reuseIdentifier="userCell" rowHeight="107" id="2" customClass="NCMoreUserCell" customModule="Nextcloud" customModuleProvider="target">
+        <tableViewCell contentMode="scaleToFill" selectionStyle="blue" indentationWidth="0.0" reuseIdentifier="NCMoreUserCell" rowHeight="107" id="2" customClass="NCMoreUserCell" customModule="Nextcloud" customModuleProvider="target">
             <rect key="frame" x="0.0" y="0.0" width="600" height="75"/>
             <autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
             <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2" id="sQq-jC-UEV">

+ 3 - 3
iOSClient/More/NCMore.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22113.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22089.1"/>
         <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"/>
@@ -22,7 +22,7 @@
                                 <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                                 <inset key="separatorInset" minX="56" minY="0.0" maxX="0.0" maxY="0.0"/>
                                 <prototypes>
-                                    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="50" id="qwS-lS-XzK" customClass="CCCellMore" customModule="Nextcloud" customModuleProvider="target">
+                                    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="CCCellMore" rowHeight="50" id="qwS-lS-XzK" customClass="CCCellMore" customModule="Nextcloud" customModuleProvider="target">
                                         <rect key="frame" x="0.0" y="55.5" width="414" height="50"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" multipleTouchEnabled="YES" contentMode="center" tableViewCell="qwS-lS-XzK" id="1FG-Yi-cbC">

+ 102 - 190
iOSClient/More/NCMore.swift

@@ -23,7 +23,6 @@
 
 import UIKit
 import NextcloudKit
-import MarqueeLabel
 
 class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
 
@@ -33,16 +32,31 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
     @IBOutlet weak var progressQuota: UIProgressView!
     @IBOutlet weak var viewQuota: UIView!
 
-    var functionMenu: [NKExternalSite] = []
-    var externalSiteMenu: [NKExternalSite] = []
-    var settingsMenu: [NKExternalSite] = []
-    var quotaMenu: [NKExternalSite] = []
+    private var functionMenu: [NKExternalSite] = []
+    private var externalSiteMenu: [NKExternalSite] = []
+    private var settingsMenu: [NKExternalSite] = []
+    private var quotaMenu: [NKExternalSite] = []
 
-    let appDelegate = UIApplication.shared.delegate as! AppDelegate
-    let defaultCornerRadius: CGFloat = 10.0
-    let applicationHandle = NCApplicationHandle()
-    
-    var tabAccount: tableAccount?
+    // swiftlint:disable force_cast
+    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    // swiftlint:enable force_cast
+
+    private let applicationHandle = NCApplicationHandle()
+
+    private var tabAccount: tableAccount?
+
+    private struct Section {
+        var items: [NKExternalSite]
+        var type: SectionType
+
+        enum SectionType {
+            case account
+            case moreApps
+            case regular
+        }
+    }
+
+    private var sections: [Section] = []
 
     // MARK: - View Life Cycle
 
@@ -52,36 +66,29 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
         self.navigationItem.title = NSLocalizedString("_more_", comment: "")
         view.backgroundColor = .systemGroupedBackground
 
+        tableView.insetsContentViewsToSafeArea = false
         tableView.delegate = self
         tableView.dataSource = self
         tableView.backgroundColor = .systemGroupedBackground
-        tableView.register(UINib(nibName: "NCMoreUserCell", bundle: nil), forCellReuseIdentifier: "userCell")
+        tableView.register(NCMoreUserCell.fromNib(), forCellReuseIdentifier: NCMoreUserCell.reuseIdentifier)
+        tableView.register(NCMoreAppSuggestionsCell.fromNib(), forCellReuseIdentifier: NCMoreAppSuggestionsCell.reuseIdentifier)
 
         // create tap gesture recognizer
         let tapQuota = UITapGestureRecognizer(target: self, action: #selector(tapLabelQuotaExternalSite))
         labelQuotaExternalSite.isUserInteractionEnabled = true
         labelQuotaExternalSite.addGestureRecognizer(tapQuota)
-
-        // Notification
-        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
     }
 
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
 
-        navigationController?.setGroupeAppreance()
-        
+        navigationController?.setGroupAppearance()
+
         appDelegate.activeViewController = self
         loadItems()
         tableView.reloadData()
     }
 
-    // MARK: - NotificationCenter
-
-    @objc func initialize() {
-        loadItems()
-    }
-
     // MARK: -
 
     func loadItems() {
@@ -94,6 +101,7 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
         externalSiteMenu.removeAll()
         settingsMenu.removeAll()
         quotaMenu.removeAll()
+        sections.removeAll()
         labelQuotaExternalSite.text = ""
         progressQuota.progressTintColor = NCBrandColor.shared.brandElement
 
@@ -179,7 +187,7 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
         item.url = "segueSettings"
         settingsMenu.append(item)
 
-        if quotaMenu.count > 0 {
+        if !quotaMenu.isEmpty {
             let item = quotaMenu[0]
             labelQuotaExternalSite.text = item.name
         }
@@ -216,7 +224,7 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
         if NCBrandOptions.shared.disable_more_external_site == false {
             if let externalSites = NCManageDatabase.shared.getAllExternalSites(account: appDelegate.account) {
                 for externalSite in externalSites {
-                    if (externalSite.name != "" && externalSite.url != ""), let urlEncoded = externalSite.url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
+                    if !externalSite.name.isEmpty, !externalSite.url.isEmpty, let urlEncoded = externalSite.url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
                         item = NKExternalSite()
                         item.name = externalSite.name
                         item.url = urlEncoded
@@ -229,21 +237,45 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
                 }
             }
         }
+
+        loadSections()
+    }
+
+    private func loadSections() {
+        if tabAccount != nil {
+            sections.append(Section(items: [NKExternalSite()], type: .account))
+        }
+
+        if !NCBrandOptions.shared.disable_show_more_nextcloud_apps_in_settings {
+            sections.append(Section(items: [NKExternalSite()], type: .moreApps))
+        }
+
+        if !functionMenu.isEmpty {
+            sections.append(Section(items: functionMenu, type: .regular))
+        }
+
+        if !externalSiteMenu.isEmpty {
+            sections.append(Section(items: externalSiteMenu, type: .regular))
+        }
+
+        if !settingsMenu.isEmpty {
+            sections.append(Section(items: settingsMenu, type: .regular))
+        }
     }
 
     // MARK: - Action
 
     @objc func tapLabelQuotaExternalSite() {
 
-        if quotaMenu.count > 0 {
-
+        if !quotaMenu.isEmpty {
             let item = quotaMenu[0]
-            let browserWebVC = UIStoryboard(name: "NCBrowserWeb", bundle: nil).instantiateInitialViewController() as! NCBrowserWeb
-            browserWebVC.urlBase = item.url
-            browserWebVC.isHiddenButtonExit = true
+            if let browserWebVC = UIStoryboard(name: "NCBrowserWeb", bundle: nil).instantiateInitialViewController() as? NCBrowserWeb {
+                browserWebVC.urlBase = item.url
+                browserWebVC.isHiddenButtonExit = true
 
-            self.navigationController?.pushViewController(browserWebVC, animated: true)
-            self.navigationController?.navigationBar.isHidden = false
+                self.navigationController?.pushViewController(browserWebVC, animated: true)
+                self.navigationController?.navigationBar.isHidden = false
+            }
         }
     }
 
@@ -257,7 +289,7 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
     // MARK: -
 
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
-        if indexPath.section == 0 {
+        if sections[indexPath.section].type == .account {
             return 75
         } else {
             return NCGlobal.shared.heightCellSettings
@@ -265,65 +297,31 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
     }
 
     func numberOfSections(in tableView: UITableView) -> Int {
-
-        if externalSiteMenu.count == 0 {
-            return 3
-        } else {
-            return 4
-        }
+        return sections.count
     }
-    
-    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
-        
-        if section == 0 {
+
+    func tableView(_ tableView: UITableView, heightForHeaderInSection index: Int) -> CGFloat {
+        let section = sections[index]
+
+        if section.type == .account {
             return 10
+        } else if section.type == .moreApps || sections[index - 1].type == .moreApps {
+            return 1
         } else {
             return 20
         }
     }
 
-    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-
-        var cont = 0
-
-        if section == 0 {
-            cont = tabAccount == nil ? 0 : 1
-        } else if section == 1 {
-            // Menu Normal
-            cont = functionMenu.count
-        } else {
-            switch numberOfSections(in: tableView) {
-            case 3:
-                // Menu Settings
-                if section == 2 {
-                    cont = settingsMenu.count
-                }
-            case 4:
-                // Menu External Site
-                if section == 2 {
-                    cont = externalSiteMenu.count
-                }
-                // Menu Settings
-                if section == 3 {
-                    cont = settingsMenu.count
-                }
-            default:
-                cont = 0
-            }
-        }
-
-        return cont
+    func tableView(_ tableView: UITableView, numberOfRowsInSection index: Int) -> Int {
+        return sections[index].items.count
     }
-    
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
 
-        var item = NKExternalSite()
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let section = sections[indexPath.section]
 
-        // change color selection and disclosure indicator
-        let selectionColor: UIView = UIView()
-        if indexPath.section == 0 {
+        if section.type == .account {
 
-            let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! NCMoreUserCell
+            guard let cell = tableView.dequeueReusableCell(withIdentifier: NCMoreUserCell.reuseIdentifier, for: indexPath) as? NCMoreUserCell else { return UITableViewCell() }
 
             cell.avatar.image = nil
             cell.icon.image = nil
@@ -331,20 +329,15 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
             cell.displayName.text = ""
 
             if let account = tabAccount {
-                cell.avatar.image = NCUtility.shared.loadUserImage(
-                    for: account.user,
-                       displayName: account.displayName,
-                       userBaseUrl: appDelegate)
+                cell.avatar.image = NCUtility.shared.loadUserImage(for: account.user, displayName: account.displayName, userBaseUrl: appDelegate)
 
-                if account.alias == "" {
+                if account.alias.isEmpty {
                     cell.displayName?.text = account.displayName
                 } else {
                     cell.displayName?.text = account.displayName + " (" + account.alias + ")"
                 }
                 cell.displayName.textColor = .label
             }
-            cell.selectedBackgroundView = selectionColor
-            cell.backgroundColor = .secondarySystemGroupedBackground
             cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
 
             if NCGlobal.shared.capabilityUserStatusEnabled, let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", appDelegate.account)) {
@@ -359,46 +352,34 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
                     cell.status.tapToScroll = false
                 }
             }
-            
-            cell.layer.cornerRadius = defaultCornerRadius
+
             cell.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner]
 
             return cell
 
+        } else if section.type == .moreApps {
+            guard let cell = tableView.dequeueReusableCell(withIdentifier: NCMoreAppSuggestionsCell.reuseIdentifier, for: indexPath) as? NCMoreAppSuggestionsCell else { return UITableViewCell() }
+            return cell
         } else {
+            guard let cell = tableView.dequeueReusableCell(withIdentifier: CCCellMore.reuseIdentifier, for: indexPath) as? CCCellMore else { return UITableViewCell() }
 
-            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CCCellMore
-
-            // Menu Normal
-            if indexPath.section == 1 {
-                item = functionMenu[indexPath.row]
-            }
-            // Menu External Site
-            if numberOfSections(in: tableView) == 4 && indexPath.section == 2 {
-                item = externalSiteMenu[indexPath.row]
-            }
-            // Menu Settings
-            if (numberOfSections(in: tableView) == 3 && indexPath.section == 2) || (numberOfSections(in: tableView) == 4 && indexPath.section == 3) {
-                item = settingsMenu[indexPath.row]
-            }
+            let item = sections[indexPath.section].items[indexPath.row]
 
             cell.imageIcon?.image = NCUtility.shared.loadImage(named: item.icon)
             cell.imageIcon?.contentMode = .scaleAspectFit
             cell.labelText?.text = NSLocalizedString(item.name, comment: "")
             cell.labelText.textColor = .label
 
-            cell.selectedBackgroundView = selectionColor
-            cell.backgroundColor = .secondarySystemGroupedBackground
             cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
 
             cell.separator.backgroundColor = .separator
             cell.separatorHeigth.constant = 0.4
-            
-            cell.layer.cornerRadius = 0
+
+            cell.removeCornerRadius()
             let rows = tableView.numberOfRows(inSection: indexPath.section)
-            
+
             if indexPath.row == 0 {
-                cell.layer.cornerRadius = defaultCornerRadius
+                cell.applyCornerRadius()
                 if indexPath.row == rows - 1 {
                     cell.separator.backgroundColor = .clear
                     cell.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner]
@@ -406,70 +387,46 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
                     cell.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
                 }
             } else if indexPath.row == rows - 1 {
-                cell.layer.cornerRadius = defaultCornerRadius
+                cell.applyCornerRadius()
                 cell.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
                 cell.separator.backgroundColor = .clear
             }
-            
+
             return cell
         }
     }
 
-    // method to run when table view cell is tapped
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let item = sections[indexPath.section].items[indexPath.row]
 
-        var item = NKExternalSite()
-
-        if indexPath.section == 0 {
+        // Menu Function
+        if sections[indexPath.section].type == .account {
             tapImageLogoManageAccount()
             return
         }
 
-        // Menu Function
-        if indexPath.section == 1 {
-            item = functionMenu[indexPath.row]
-        }
-
-        // Menu External Site
-        if numberOfSections(in: tableView) == 4 && indexPath.section == 2 {
-            item = externalSiteMenu[indexPath.row]
-        }
-
-        // Menu Settings
-        if (numberOfSections(in: tableView) == 3 && indexPath.section == 2) || (numberOfSections(in: tableView) == 4 && indexPath.section == 3) {
-            item = settingsMenu[indexPath.row]
-        }
-
         // Action
         if item.url.contains("segue") && !item.url.contains("//") {
-
             self.navigationController?.performSegue(withIdentifier: item.url, sender: self)
-
         } else if item.url.contains("openStoryboard") && !item.url.contains("//") {
-
             let nameStoryboard = item.url.replacingOccurrences(of: "openStoryboard", with: "")
             let storyboard = UIStoryboard(name: nameStoryboard, bundle: nil)
             if let controller = storyboard.instantiateInitialViewController() {
                 controller.modalPresentationStyle = UIModalPresentationStyle.pageSheet
                 present(controller, animated: true, completion: nil)
             }
-
         } else if item.url.contains("//") {
-
-            let browserWebVC = UIStoryboard(name: "NCBrowserWeb", bundle: nil).instantiateInitialViewController() as! NCBrowserWeb
-            browserWebVC.urlBase = item.url
-            browserWebVC.isHiddenButtonExit = true
-            browserWebVC.titleBrowser = item.name
-
-            self.navigationController?.pushViewController(browserWebVC, animated: true)
-            self.navigationController?.navigationBar.isHidden = false
-
+            if let browserWebVC = UIStoryboard(name: "NCBrowserWeb", bundle: nil).instantiateInitialViewController() as? NCBrowserWeb {
+                browserWebVC.urlBase = item.url
+                browserWebVC.isHiddenButtonExit = true
+                browserWebVC.titleBrowser = item.name
+                self.navigationController?.pushViewController(browserWebVC, animated: true)
+                self.navigationController?.navigationBar.isHidden = false
+            }
         } else if item.url == "logout" {
-
             let alertController = UIAlertController(title: "", message: NSLocalizedString("_want_delete_", comment: ""), preferredStyle: .alert)
 
             let actionYes = UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
-
                 let manageAccount = CCManageAccount()
                 manageAccount.delete(self.appDelegate.account)
 
@@ -483,53 +440,8 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
             alertController.addAction(actionYes)
             alertController.addAction(actionNo)
             self.present(alertController, animated: true, completion: nil)
-
         } else {
             applicationHandle.didSelectItem(item, viewController: self)
         }
     }
 }
-
-class CCCellMore: UITableViewCell {
-
-    @IBOutlet weak var labelText: UILabel!
-    @IBOutlet weak var imageIcon: UIImageView!
-    @IBOutlet weak var separator: UIView!
-    @IBOutlet weak var separatorHeigth: NSLayoutConstraint!
-
-    override var frame: CGRect {
-        get {
-            return super.frame
-        }
-        set (newFrame) {
-            var frame = newFrame
-            let newWidth = frame.width * 0.90
-            let space = (frame.width - newWidth) / 2
-            frame.size.width = newWidth
-            frame.origin.x += space
-            super.frame = frame
-        }
-    }
-}
-
-class NCMoreUserCell: UITableViewCell {
-
-    @IBOutlet weak var displayName: UILabel!
-    @IBOutlet weak var avatar: UIImageView!
-    @IBOutlet weak var icon: UIImageView!
-    @IBOutlet weak var status: MarqueeLabel!
-    
-    override var frame: CGRect {
-        get {
-            return super.frame
-        }
-        set (newFrame) {
-            var frame = newFrame
-            let newWidth = frame.width * 0.90
-            let space = (frame.width - newWidth) / 2
-            frame.size.width = newWidth
-            frame.origin.x += space
-            super.frame = frame
-        }
-    }
-}

+ 69 - 51
iOSClient/NCGlobal.swift

@@ -61,14 +61,6 @@ class NCGlobal: NSObject {
         return result.reduce(0, { $0 + $1 }) % maximum
     }
 
-    // Struct for Progress
-    //
-    struct progressType {
-        var progress: Float
-        var totalBytes: Int64
-        var totalBytesExpected: Int64
-    }
-
     // Directory on Group
     //
     @objc let directoryProviderStorage              = "File Provider Storage"
@@ -108,7 +100,7 @@ class NCGlobal: NSObject {
 
     // Nextcloud unsupported
     //
-    let nextcloud_unsupported_version: Int          = 16
+    let nextcloud_unsupported_version: Int = 16
 
     // Intro selector
     //
@@ -117,7 +109,7 @@ class NCGlobal: NSObject {
 
     // Varie size GUI
     //
-    @objc let heightCellSettings: CGFloat           = 50
+    @objc let heightCellSettings: CGFloat = 50
 
     // Avatar & Preview size
     //
@@ -129,7 +121,14 @@ class NCGlobal: NSObject {
     // E2EE
     //
     let e2eePassphraseTest                          = "more over television factory tendency independence international intellectual impress interest sentence pony"
-    @objc let e2eeReadVersions                      = ["1.1", "1.2"]
+    @objc let e2eeVersions                          = ["1.1", "1.2"] // ["1.1", "1.2", "2.0"]
+    let e2eeVersionV11                              = "1.1"
+    let e2eeVersionV12                              = "1.2"
+    let e2eeVersionV20                              = "2.0"
+
+    // CHUNK
+    let chunkSizeMBCellular                         = 10000000
+    let chunkSizeMBEthernetOrWiFi                   = 100000000
 
     // Video
     //
@@ -146,12 +145,10 @@ class NCGlobal: NSObject {
     let layoutList                                  = "typeLayoutList"
     let layoutGrid                                  = "typeLayoutGrid"
 
-    let layoutViewMove                              = "LayoutMove"
     let layoutViewTrash                             = "LayoutTrash"
     let layoutViewOffline                           = "LayoutOffline"
     let layoutViewFavorite                          = "LayoutFavorite"
     let layoutViewFiles                             = "LayoutFiles"
-    let layoutViewViewInFolder                      = "LayoutViewInFolder"
     let layoutViewTransfers                         = "LayoutTransfers"
     let layoutViewRecent                            = "LayoutRecent"
     let layoutViewShares                            = "LayoutShares"
@@ -167,6 +164,7 @@ class NCGlobal: NSObject {
     // Standard height sections header/footer
     //
     let heightButtonsView: CGFloat                  = 50
+    let heightHeaderTransfer: CGFloat               = 50
     let heightSection: CGFloat                      = 30
     let heightFooter: CGFloat                       = 1
     let heightFooterButton: CGFloat                 = 30
@@ -191,11 +189,11 @@ class NCGlobal: NSObject {
 
     // Rich Workspace
     //
-    let fileNameRichWorkspace                       = "Readme.md"
+    let fileNameRichWorkspace = "Readme.md"
 
     // Extension
     //
-    @objc let extensionPreview                      = "ico"
+    @objc let extensionPreview = "ico"
 
     // ContentPresenter
     //
@@ -210,24 +208,42 @@ class NCGlobal: NSObject {
     @objc let errorUnauthorized401: Int             = 401
     @objc let errorForbidden: Int                   = 403
     @objc let errorResourceNotFound: Int            = 404
-    @objc let errordMethodNotSupported: Int         = 405
+    @objc let errorMethodNotSupported: Int          = 405
     @objc let errorConflict: Int                    = 409
     @objc let errorPreconditionFailed: Int          = 412
+    @objc let errorQuota: Int                       = 507
     @objc let errorUnauthorized997: Int             = 997
     @objc let errorConnectionLost: Int              = -1005
     @objc let errorNetworkNotAvailable: Int         = -1009
     @objc let errorBadServerResponse: Int           = -1011
     @objc let errorInternalError: Int               = -99999
     @objc let errorFileNotSaved: Int                = -99998
-    @objc let errorDecodeMetadata: Int              = -99997
-    @objc let errorE2EENotEnabled: Int              = -99996
-    @objc let errorE2EE: Int                        = -99995
-    @objc let errorOffline: Int                     = -99994
-    @objc let errorCharactersForbidden: Int         = -99993
-    @objc let errorCreationFile: Int                = -99992
-    @objc let errorReadFile: Int                    = -99991
-    @objc let errorUnauthorizedFilesPasscode: Int   = -99990
-    @objc let errorDisableFilesApp: Int             = -99989
+    @objc let errorOffline: Int                     = -99997
+    @objc let errorCharactersForbidden: Int         = -99996
+    @objc let errorCreationFile: Int                = -99995
+    @objc let errorReadFile: Int                    = -99994
+    @objc let errorUnauthorizedFilesPasscode: Int   = -99993
+    @objc let errorDisableFilesApp: Int             = -99992
+    @objc let errorUnexpectedResponseFromDB: Int    = -99991
+    // E2EE
+    @objc let errorE2EENotEnabled: Int              = -98000
+    @objc let errorE2EEVersion: Int                 = -98001
+    @objc let errorE2EEKeyChecksums: Int            = -98002
+    @objc let errorE2EEKeyEncodeMetadata: Int       = -98003
+    @objc let errorE2EEKeyDecodeMetadata: Int       = -98004
+    @objc let errorE2EEKeyVerifySignature: Int      = -98005
+    @objc let errorE2EEKeyCiphertext: Int           = -98006
+    @objc let errorE2EEKeyFiledropCiphertext: Int   = -98007
+    @objc let errorE2EEJSon: Int                    = -98008
+    @objc let errorE2EELock: Int                    = -98009
+    @objc let errorE2EEEncryptFile: Int             = -98010
+    @objc let errorE2EEEncryptPayloadFile: Int      = -98011
+    @objc let errorE2EECounter: Int                 = -98012
+    @objc let errorE2EEGenerateKey: Int             = -98013
+    @objc let errorE2EEEncodedKey: Int              = -98014
+    @objc let errorE2EENoUserFound: Int             = -98015
+    @objc let errorE2EEUploadInProgress: Int        = -98016
+
 
     // Constants to identify the different permissions of a file
     //
@@ -257,7 +273,7 @@ class NCGlobal: NSObject {
     @objc let permissionDefaultFileRemoteShareNoSupportShareOption: Int     = 3
     @objc let permissionDefaultFolderRemoteShareNoSupportShareOption: Int   = 15
     // ATTRIBUTES
-    @objc let permissionDownloadShare: Int          = 0
+    @objc let permissionDownloadShare: Int = 0
 
     // Filename Mask and Type
     //
@@ -271,7 +287,6 @@ class NCGlobal: NSObject {
     // Selector
     //
     let selectorDownloadFile                        = "downloadFile"
-    let selectorDownloadAllFile                     = "downloadAllFile"
     let selectorReadFile                            = "readFile"
     let selectorListingFavorite                     = "listingFavorite"
     let selectorLoadFileView                        = "loadFileView"
@@ -285,10 +300,10 @@ class NCGlobal: NSObject {
     let selectorUploadFileNODelete                  = "UploadFileNODelete"
     let selectorUploadFileShareExtension            = "uploadFileShareExtension"
     let selectorSaveAlbum                           = "saveAlbum"
-    let selectorSaveAlbumLivePhotoIMG               = "saveAlbumLivePhotoIMG"
-    let selectorSaveAlbumLivePhotoMOV               = "saveAlbumLivePhotoMOV"
     let selectorSaveAsScan                          = "saveAsScan"
     let selectorOpenDetail                          = "openDetail"
+    let selectorSynchronizationOffline              = "synchronizationOffline"
+    let selectorSynchronizationFavorite             = "synchronizationFavorite"
 
     // Metadata : Status
     //
@@ -309,9 +324,12 @@ class NCGlobal: NSObject {
     let metadataStatusUploading: Int                = 3
     let metadataStatusUploadError: Int              = 4
 
+    // Queue Concurrent Operation Download
+    let maxConcurrentOperationCountDownload: Int    = 10
+
     //  Hidden files included in the read
     //
-    let includeHiddenFiles: [String]                = [".LivePhoto"]
+    let includeHiddenFiles: [String] = [".LivePhoto"]
 
     // Auto upload subfolder granularity
     //
@@ -322,19 +340,19 @@ class NCGlobal: NSObject {
     // Notification Center
     //
     @objc let notificationCenterApplicationDidEnterBackground   = "applicationDidEnterBackground"
-    let notificationCenterApplicationDidBecomeActive            = "applicationDidBecomeActive"
-    let notificationCenterApplicationWillResignActive           = "applicationWillResignActive"
+    @objc let notificationCenterApplicationDidBecomeActive      = "applicationDidBecomeActive"
+    @objc let notificationCenterApplicationWillResignActive     = "applicationWillResignActive"
 
-    @objc let notificationCenterInitialize                      = "initialize"
+    @objc let notificationCenterChangeUser                      = "changeUser"
     @objc let notificationCenterChangeTheming                   = "changeTheming"
     let notificationCenterRichdocumentGrabFocus                 = "richdocumentGrabFocus"
     let notificationCenterReloadDataNCShare                     = "reloadDataNCShare"
     let notificationCenterCloseRichWorkspaceWebView             = "closeRichWorkspaceWebView"
-    let notificationCenterUpdateBadgeNumber                     = "updateBadgeNumber"               // userInfo: counter
+    let notificationCenterUpdateBadgeNumber                     = "updateBadgeNumber"               // userInfo: counterDownload, counterUpload
     let notificationCenterReloadAvatar                          = "reloadAvatar"
 
-    @objc let notificationCenterReloadDataSource                = "reloadDataSource"                // userInfo: serverUrl?
-    let notificationCenterReloadDataSourceNetwork               = "reloadDataSourceNetwork"         // userInfo: serverUrl?
+    @objc let notificationCenterReloadDataSource                = "reloadDataSource"
+    let notificationCenterReloadDataSourceNetwork               = "reloadDataSourceNetwork"
     let notificationCenterReloadDataSourceNetworkForced         = "reloadDataSourceNetworkForced"
 
     let notificationCenterChangeStatusFolderE2EE                = "changeStatusFolderE2EE"          // userInfo: serverUrl
@@ -347,25 +365,24 @@ class NCGlobal: NSObject {
     @objc let notificationCenterUploadedFile                    = "uploadedFile"                    // userInfo: ocId, serverUrl, account, fileName, ocIdTemp, error
     let notificationCenterUploadCancelFile                      = "uploadCancelFile"                // userInfo: ocId, serverUrl, account
 
-    let notificationCenterProgressTask                          = "progressTask"                    // userInfo: account, ocId, serverUrl, status, progress, totalBytes, totalBytesExpected
+    let notificationCenterProgressTask                          = "progressTask"                    // userInfo: account, ocId, serverUrl, status, chunk, e2eEncrypted, progress, totalBytes, totalBytesExpected
 
-    let notificationCenterCreateFolder                          = "createFolder"                    // userInfo: ocId, serverUrl, account, e2ee, withPush
-    let notificationCenterDeleteFile                            = "deleteFile"                      // userInfo: account, ocIds, error
-    let notificationCenterRenameFile                            = "renameFile"                      // userInfo: ocId, account
-    let notificationCenterMoveFile                              = "moveFile"                        // userInfo: ocId, account, serverUrlFrom
-    let notificationCenterCopyFile                              = "copyFile"                        // userInfo: ocId, serverUrlTo
+    let notificationCenterCreateFolder                          = "createFolder"                    // userInfo: ocId, serverUrl, account, withPush
+    let notificationCenterDeleteFile                            = "deleteFile"                      // userInfo: [ocId], [indexPath], onlyLocalCache, error, hud?
+    let notificationCenterMoveFile                              = "moveFile"                        // userInfo: [ocId], [indexPath], error, hud?
+    let notificationCenterCopyFile                              = "copyFile"                        // userInfo: [ocId], [indexPath], error, hud?
+    let notificationCenterRenameFile                            = "renameFile"                      // userInfo: ocId, account, indexPath
     let notificationCenterFavoriteFile                          = "favoriteFile"                    // userInfo: ocId, serverUrl
 
     let notificationCenterOperationReadFile                     = "operationReadFile"               // userInfo: ocId
 
     let notificationCenterMenuSearchTextPDF                     = "menuSearchTextPDF"
     let notificationCenterMenuGotToPageInPDF                    = "menuGotToPageInPDF"
-    let notificationCenterMenuDetailClose                       = "menuDetailClose"
 
     let notificationCenterDownloadedThumbnail                   = "DownloadedThumbnail"             // userInfo: ocId
 
     let notificationCenterOpenMediaDetail                       = "openMediaDetail"                 // userInfo: ocId
-    
+
     let notificationCenterDismissScanDocument                   = "dismissScanDocument"
     let notificationCenterDismissUploadAssets                   = "dismissUploadAssets"
 
@@ -378,7 +395,7 @@ class NCGlobal: NSObject {
     let tipNCCollectionViewCommonAccountRequest                 = "tipnccollectionviewcommonaccountrequest"
     let tipNCScanAddImage                                       = "tipncscanaddimage"
     let tipNCViewerMediaDetailView                              = "tipncviewermediadetailview"
-    
+
     // ACTION
     //
     let actionNoAction                                          = "no-action"
@@ -386,7 +403,7 @@ class NCGlobal: NSObject {
     let actionScanDocument                                      = "add-scan-document"
     let actionTextDocument                                      = "create-text-document"
     let actionVoiceMemo                                         = "create-voice-memo"
-    
+
     // WIDGET ACTION
     //
     let widgetActionNoAction                                    = "nextcloud://open-action?action=no-action"
@@ -394,7 +411,7 @@ class NCGlobal: NSObject {
     let widgetActionScanDocument                                = "nextcloud://open-action?action=add-scan-document"
     let widgetActionTextDocument                                = "nextcloud://open-action?action=create-text-document"
     let widgetActionVoiceMemo                                   = "nextcloud://open-action?action=create-voice-memo"
-    
+
     // APPCONFIG
     //
     let configuration_brand                                     = "brand"
@@ -403,7 +420,7 @@ class NCGlobal: NSObject {
     let configuration_username                                  = "username"
     let configuration_password                                  = "password"
     let configuration_apppassword                               = "apppassword"
-    
+
     let configuration_disable_intro                             = "disable_intro"
     let configuration_disable_multiaccount                      = "disable_multiaccount"
     let configuration_disable_crash_service                     = "disable_crash_service"
@@ -416,7 +433,7 @@ class NCGlobal: NSObject {
     //
     var capabilityServerVersionMajor: Int                       = 0
     @objc var capabilityServerVersion: String                   = ""
-    
+
     var capabilityFileSharingApiEnabled: Bool                   = false
     var capabilityFileSharingPubPasswdEnforced: Bool            = false
     var capabilityFileSharingPubExpireDateEnforced: Bool        = false
@@ -443,12 +460,13 @@ class NCGlobal: NSObject {
     var capabilityFilesUndelete: Bool                           = false
     var capabilityFilesLockVersion: String                      = ""    // NC 24
     var capabilityFilesComments: Bool                           = false // NC 20
+    var capabilityFilesBigfilechunking: Bool                    = false
 
     @objc var capabilityUserStatusEnabled: Bool                 = false
     var capabilityExternalSites: Bool                           = false
     var capabilityGroupfoldersEnabled: Bool                     = false // NC27
 
-    // MORE APPS
+    // MORE NEXTCLOUD APPS
     let talkSchemeUrl                                           = "nextcloudtalk://"
     let notesSchemeUrl                                          = "nextcloudnotes://"
     let talkAppStoreUrl                                         = "https://apps.apple.com/de/app/nextcloud-talk/id1296825574"
@@ -456,5 +474,5 @@ class NCGlobal: NSObject {
     let moreAppsUrl                                             = "https://www.apple.com/us/search/nextcloud?src=globalnav"
 
     // SNAPSHOT PREVIEW
-    let defaultSnapshotConfiguration                            = "DefaultPreviewConfiguration"
+    let defaultSnapshotConfiguration = "DefaultPreviewConfiguration"
 }

部分文件因文件數量過多而無法顯示