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

Version 6.0.0 (#3122)

Signed-off-by: Nextcloud bot <bot@nextcloud.com>
Signed-off-by: Marino Faggiana <marino@marinofaggiana.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
Marino Faggiana 5 сар өмнө
parent
commit
574ab7e3a2
100 өөрчлөгдсөн 2541 нэмэгдсэн , 2018 устгасан
  1. 1 1
      Brand/Database.swift
  2. 2 0
      Brand/File_Provider_Extension.plist
  3. 19 15
      Brand/Intro/NCIntroViewController.swift
  4. 121 85
      Brand/NCBrand.swift
  5. 2 0
      Brand/iOSClient.plist
  6. 0 2
      Cartfile
  7. 0 2
      Cartfile.resolved
  8. 1 1
      ExternalResources/NCApplicationHandle.swift
  9. 3 3
      File Provider Extension UI/DocumentActionViewController.swift
  10. 44 48
      File Provider Extension/FileProviderData.swift
  11. 7 7
      File Provider Extension/FileProviderDomain.swift
  12. 18 17
      File Provider Extension/FileProviderEnumerator.swift
  13. 48 53
      File Provider Extension/FileProviderExtension+Actions.swift
  14. 14 11
      File Provider Extension/FileProviderExtension+NetworkingDelegate.swift
  15. 8 7
      File Provider Extension/FileProviderExtension+Thumbnail.swift
  16. 76 53
      File Provider Extension/FileProviderExtension.swift
  17. 1 1
      File Provider Extension/FileProviderItem.swift
  18. 7 6
      File Provider Extension/FileProviderUtility.swift
  19. 320 66
      Nextcloud.xcodeproj/project.pbxproj
  20. 7 3
      Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme
  21. 2 2
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme
  22. 1 2
      Notification Service Extension/NotificationService.swift
  23. 1 9
      README.md
  24. 9 7
      Share/NCShareCell.swift
  25. 27 31
      Share/NCShareExtension+DataSource.swift
  26. 11 6
      Share/NCShareExtension+Files.swift
  27. 35 47
      Share/NCShareExtension+NCAccountRequestDelegate.swift
  28. 56 67
      Share/NCShareExtension.swift
  29. 1 0
      Share/Share-Bridging-Header.h
  30. 1 0
      Tests/Common/BaseXCTestCase.swift
  31. 1 0
      Tests/Common/TestConstants.swift
  32. 7 5
      Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift
  33. 31 30
      Widget/Dashboard/DashboardData.swift
  34. 1 1
      Widget/Dashboard/DashboardWidgetProvider.swift
  35. 5 10
      Widget/Dashboard/DashboardWidgetView.swift
  36. 38 36
      Widget/Files/FilesData.swift
  37. 2 2
      Widget/Files/FilesWidgetProvider.swift
  38. 11 11
      Widget/Files/FilesWidgetView.swift
  39. 19 20
      Widget/Lockscreen/LockscreenData.swift
  40. 10 7
      Widget/Toolbar/ToolbarData.swift
  41. 1 1
      Widget/Toolbar/ToolbarWidgetProvider.swift
  42. 11 11
      Widget/Toolbar/ToolbarWidgetView.swift
  43. 10 10
      WidgetDashboardIntentHandler/IntentHandler.swift
  44. 6 8
      iOSClient/Account Request/NCAccountRequest.swift
  45. 34 46
      iOSClient/Account Settings/NCAccountSettingsModel.swift
  46. 16 14
      iOSClient/Account Settings/NCAccountSettingsView.swift
  47. 17 16
      iOSClient/Activity/NCActivity.storyboard
  48. 78 40
      iOSClient/Activity/NCActivity.swift
  49. 3 2
      iOSClient/Activity/NCActivityCommentView.swift
  50. 25 31
      iOSClient/Activity/NCActivityTableViewCell.swift
  51. 58 253
      iOSClient/AppDelegate.swift
  52. 1 1
      iOSClient/Assistant/Create Task/NCAssistantCreateNewTask.swift
  53. 12 7
      iOSClient/Assistant/Models/NCAssistantTask.swift
  54. 2 2
      iOSClient/Assistant/NCAssistant.swift
  55. 2 1
      iOSClient/Assistant/NCAssistantEmptyView.swift
  56. 1 1
      iOSClient/Assistant/Task Detail/NCAssistantTaskDetail.swift
  57. 19 9
      iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
  58. 1 1
      iOSClient/BrowserWeb/NCBrowserWeb.swift
  59. 2 2
      iOSClient/Color/NCColorPicker.swift
  60. 29 44
      iOSClient/Data/NCManageDatabase+Account.swift
  61. 1 0
      iOSClient/Data/NCManageDatabase+Activity.swift
  62. 4 6
      iOSClient/Data/NCManageDatabase+Avatar.swift
  63. 63 50
      iOSClient/Data/NCManageDatabase+Capabilities.swift
  64. 1 0
      iOSClient/Data/NCManageDatabase+Chunk.swift
  65. 1 0
      iOSClient/Data/NCManageDatabase+Comments.swift
  66. 1 0
      iOSClient/Data/NCManageDatabase+DashboardWidget.swift
  67. 1 0
      iOSClient/Data/NCManageDatabase+DirectEditing.swift
  68. 2 14
      iOSClient/Data/NCManageDatabase+Directory.swift
  69. 1 0
      iOSClient/Data/NCManageDatabase+E2EE.swift
  70. 1 0
      iOSClient/Data/NCManageDatabase+ExternalSites.swift
  71. 1 0
      iOSClient/Data/NCManageDatabase+GPS.swift
  72. 1 0
      iOSClient/Data/NCManageDatabase+Groupfolders.swift
  73. 1 0
      iOSClient/Data/NCManageDatabase+LayoutForView.swift
  74. 5 2
      iOSClient/Data/NCManageDatabase+LocalFile.swift
  75. 46 33
      iOSClient/Data/NCManageDatabase+Metadata+Session.swift
  76. 393 326
      iOSClient/Data/NCManageDatabase+Metadata.swift
  77. 1 0
      iOSClient/Data/NCManageDatabase+SecurityGuard.swift
  78. 1 0
      iOSClient/Data/NCManageDatabase+Share.swift
  79. 1 0
      iOSClient/Data/NCManageDatabase+Tag.swift
  80. 1 0
      iOSClient/Data/NCManageDatabase+Tip.swift
  81. 6 7
      iOSClient/Data/NCManageDatabase+Trash.swift
  82. 1 0
      iOSClient/Data/NCManageDatabase+UserStatus.swift
  83. 1 0
      iOSClient/Data/NCManageDatabase+Video.swift
  84. 45 16
      iOSClient/Data/NCManageDatabase.swift
  85. 4 2
      iOSClient/DeepLink/NCDeepLinkHandler.swift
  86. 0 1
      iOSClient/Extensions/NotificationCenter+MainThread.swift
  87. 45 44
      iOSClient/Extensions/UIAlertController+Extension.swift
  88. 4 10
      iOSClient/Extensions/UIImage+Extension.swift
  89. 26 24
      iOSClient/Favorites/NCFavorite.swift
  90. 153 120
      iOSClient/Files/NCFiles.swift
  91. 3 4
      iOSClient/GUI/ComponentView.swift
  92. 7 11
      iOSClient/GUI/NCHUDView.swift
  93. 131 0
      iOSClient/GUI/NCHud.swift
  94. 31 29
      iOSClient/Groupfolders/NCGroupfolders.swift
  95. 60 25
      iOSClient/Login/NCLogin.storyboard
  96. 61 42
      iOSClient/Login/NCLogin.swift
  97. 41 24
      iOSClient/Login/NCLoginPoll.swift
  98. 18 16
      iOSClient/Login/NCLoginProvider.swift
  99. 26 14
      iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift
  100. 55 24
      iOSClient/Main/Collection Common/Cell/NCGridCell.swift

+ 1 - 1
Brand/Database.swift

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

+ 2 - 0
Brand/File_Provider_Extension.plist

@@ -31,6 +31,8 @@
 		<string>group.it.twsweb.Crypto-Cloud</string>
 		<key>NSExtensionFileProviderSupportsEnumeration</key>
 		<true/>
+		<key>NSExtensionFileProviderSupportsPickingFolders</key>
+		<true/>
 		<key>NSExtensionPointIdentifier</key>
 		<string>com.apple.fileprovider-nonui</string>
 		<key>NSExtensionPrincipalClass</key>

+ 19 - 15
Brand/Intro/NCIntroViewController.swift

@@ -26,7 +26,6 @@
 import UIKit
 
 class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
-
     @IBOutlet weak var buttonLogin: UIButton!
     @IBOutlet weak var buttonSignUp: UIButton!
     @IBOutlet weak var buttonHost: UIButton!
@@ -99,18 +98,23 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
         view.backgroundColor = NCBrandColor.shared.customer
         timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true)
 
-        NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { _ in
-            let window = UIApplication.shared.firstWindow
-            if window?.rootViewController is NCMainTabBarController {
-                self.dismiss(animated: true)
-            } else {
-                if let mainTabBarController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
-                    mainTabBarController.modalPresentationStyle = .fullScreen
-                    mainTabBarController.view.alpha = 0
-                    window?.rootViewController = mainTabBarController
-                    window?.makeKeyAndVisible()
-                    UIView.animate(withDuration: 0.5) {
-                        mainTabBarController.view.alpha = 1
+        NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { notification in
+            if let userInfo = notification.userInfo,
+               let account = userInfo["account"] as? String {
+                let window = UIApplication.shared.firstWindow
+                if let controller = window?.rootViewController as? NCMainTabBarController {
+                    controller.account = account
+                    self.dismiss(animated: true)
+                } else {
+                    if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+                        controller.account = account
+                        controller.modalPresentationStyle = .fullScreen
+                        controller.view.alpha = 0
+                        window?.rootViewController = controller
+                        window?.makeKeyAndVisible()
+                        UIView.animate(withDuration: 0.5) {
+                            controller.view.alpha = 1
+                        }
                     }
                 }
             }
@@ -179,11 +183,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
     }
 
     @IBAction func login(_ sender: Any) {
-        appDelegate.openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
+        appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
     }
 
     @IBAction func signup(_ sender: Any) {
-        appDelegate.openLogin(selector: NCGlobal.shared.introSignup, openLoginWeb: false)
+        appDelegate.openLogin(selector: NCGlobal.shared.introSignup)
     }
 
     @IBAction func host(_ sender: Any) {

+ 121 - 85
Brand/NCBrand.swift

@@ -58,7 +58,7 @@ let userAgent: String = {
     // BRAND ONLY
     @objc public var use_AppConfig: Bool = false                                                // Don't touch me !!
 
-    // Options
+    // Use server theming color
     @objc public var use_themingColor: Bool = true
 
     var disable_intro: Bool = false
@@ -71,6 +71,10 @@ let userAgent: String = {
     var disable_mobileconfig: Bool = false
     var disable_show_more_nextcloud_apps_in_settings: Bool = false
     var doNotAskPasscodeAtStartup: Bool = false
+    var disable_source_code_in_settings: Bool = false
+
+    // (name: "Name 1", url: "https://cloud.nextcloud.com"),(name: "Name 2", url: "https://cloud.nextcloud.com")
+    var enforce_servers: [(name: String, url: String)] = []
 
     // Internal option behaviour
     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
@@ -127,26 +131,22 @@ class NCBrandColor: NSObject {
         return instance
     }()
 
-    // Color
+    /// This is rewrited from customet theme, default is Nextcloud color
+    ///
     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
     var customerText: UIColor = .white
 
-    var brand: UIColor                                                                                         // don't touch me
-    var brandElement: UIColor                                                                                  // don't touch me
-    var brandText: UIColor                                                                                     // don't touch me
+    // INTERNAL DEFINE COLORS
+    private var themingColor = ThreadSafeDictionary<String, UIColor>()
+    private var themingColorElement = ThreadSafeDictionary<String, UIColor>()
+    private var themingColorText = ThreadSafeDictionary<String, UIColor>()
 
+    var userColors: [CGColor] = []
     let nextcloud: UIColor = UIColor(red: 0.0 / 255.0, green: 130.0 / 255.0, blue: 201.0 / 255.0, alpha: 1.0)
     let yellowFavorite: UIColor = UIColor(red: 248.0 / 255.0, green: 205.0 / 255.0, blue: 70.0 / 255.0, alpha: 1.0)
-
-    var userColors: [CGColor] = []
-    var themingColor: String = ""
-    var themingColorElement: String = ""
-    var themingColorText: String = ""
-
     let iconImageColor: UIColor = .label
     let iconImageColor2: UIColor = .secondaryLabel
     let iconImageMultiColors: [UIColor] = [.secondaryLabel, .label]
-
     let textColor: UIColor = .label
     let textColor2: UIColor = .secondaryLabel
 
@@ -174,126 +174,162 @@ class NCBrandColor: NSObject {
         }
     }
 
-    override init() {
-        brand = customer
-        brandElement = customer
-        brandText = customerText
-    }
+    override init() { }
 
+    /**
+     Generate colors from the official nextcloud color.
+     You can provide how many colors you want (multiplied by 3).
+     if `step` = 6,
+     3 colors \* 6 will result in 18 generated colors
+     */
     func createUserColors() {
+        func generateColors(steps: Int = 6) -> [CGColor] {
+            func stepCalc(steps: Int, color1: CGColor, color2: CGColor) -> [CGFloat] {
+                var step = [CGFloat](repeating: 0, count: 3)
+
+                step[0] = (color2.components![0] - color1.components![0]) / CGFloat(steps)
+                step[1] = (color2.components![1] - color1.components![1]) / CGFloat(steps)
+                step[2] = (color2.components![2] - color1.components![2]) / CGFloat(steps)
+                return step
+            }
+
+            func mixPalette(steps: Int, color1: CGColor, color2: CGColor) -> [CGColor] {
+                var palette = [color1]
+                let step = stepCalc(steps: steps, color1: color1, color2: color2)
+                let c1Components = color1.components!
+
+                for i in 1 ..< steps {
+                    let r = c1Components[0] + step[0] * CGFloat(i)
+                    let g = c1Components[1] + step[1] * CGFloat(i)
+                    let b = c1Components[2] + step[2] * CGFloat(i)
+
+                    palette.append(UIColor(red: r, green: g, blue: b, alpha: 1).cgColor)
+                }
+                return palette
+            }
+
+            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)
+            let palette3 = mixPalette(steps: steps, color1: blue, color2: red)
+
+            return palette1 + palette2 + palette3
+        }
+
         userColors = generateColors()
     }
 
-    func settingThemingColor(account: String) {
+    @discardableResult
+    func settingThemingColor(account: String) -> Bool {
         let darker: CGFloat = 30    // %
         let lighter: CGFloat = 30   // %
+        var colorThemingColor: UIColor?
+        var colorThemingColorElement: UIColor?
+        var colorThemingColorText: UIColor?
 
         if NCBrandOptions.shared.use_themingColor {
-            self.themingColor = NCGlobal.shared.capabilityThemingColor
-            self.themingColorElement = NCGlobal.shared.capabilityThemingColorElement
-            self.themingColorText = NCGlobal.shared.capabilityThemingColorText
+            let themingColor = NCCapabilities.shared.getCapabilities(account: account).capabilityThemingColor
+            let themingColorElement = NCCapabilities.shared.getCapabilities(account: account).capabilityThemingColorElement
+            let themingColorText = NCCapabilities.shared.getCapabilities(account: account).capabilityThemingColorText
 
-            // COLOR
+            // THEMING COLOR
             if themingColor.first == "#" {
                 if let color = UIColor(hex: themingColor) {
-                    brand = color
-                } else {
-                    brand = customer
-                }
-            } else {
-                brand = customer
-            }
-
-            // COLOR TEXT
-            if themingColorText.first == "#" {
-                if let color = UIColor(hex: themingColorText) {
-                    brandText = color
+                    colorThemingColor = color
                 } else {
-                    brandText = customerText
+                    colorThemingColor = customer
                 }
             } else {
-                brandText = customerText
+                colorThemingColor = customer
             }
 
-            // COLOR ELEMENT
+            // THEMING COLOR ELEMENT (control isTooLight / isTooDark)
             if themingColorElement.first == "#" {
                 if let color = UIColor(hex: themingColorElement) {
-                    brandElement = color
+                    if color.isTooLight() {
+                        if let color = color.darker(by: darker) {
+                            colorThemingColorElement = color
+                        }
+                    } else if color.isTooDark() {
+                        if let color = color.lighter(by: lighter) {
+                            colorThemingColorElement = color
+                        }
+                    } else {
+                        colorThemingColorElement = color
+                    }
                 } else {
-                    brandElement = brand
+                    colorThemingColorElement = customer
                 }
             } else {
-                brandElement = brand
+                colorThemingColorElement = customer
             }
 
-            if brandElement.isTooLight() {
-                if let color = brandElement.darker(by: darker) {
-                    brandElement = color
-                }
-            } else if brandElement.isTooDark() {
-                if let color = brandElement.lighter(by: lighter) {
-                    brandElement = color
+            // THEMING COLOR TEXT
+            if themingColorText.first == "#" {
+                if let color = UIColor(hex: themingColorText) {
+                    colorThemingColorText = color
+                } else {
+                    colorThemingColorText = .white
                 }
+            } else {
+                colorThemingColorText = .white
             }
 
         } else {
 
+            // THEMING COLOR
+            colorThemingColor = customer
+
+            // THEMING COLOR ELEMENT (control isTooLight / isTooDark)
             if self.customer.isTooLight() {
                 if let color = customer.darker(by: darker) {
-                    brandElement = color
+                    colorThemingColorElement = color
                 }
             } else if customer.isTooDark() {
                 if let color = customer.lighter(by: lighter) {
-                    brandElement = color
+                    colorThemingColorElement = color
                 }
             } else {
-                brandElement = customer
+                colorThemingColorElement = customer
             }
 
-            brand = customer
-            brandText = customerText
+            // THEMING COLOR TEXT
+            colorThemingColorText = customerText
         }
-    }
 
-    private func stepCalc(steps: Int, color1: CGColor, color2: CGColor) -> [CGFloat] {
-        var step = [CGFloat](repeating: 0, count: 3)
+        if self.themingColor[account] != colorThemingColor || self.themingColorElement[account] != colorThemingColorElement || self.themingColorText[account] != colorThemingColorText {
 
-        step[0] = (color2.components![0] - color1.components![0]) / CGFloat(steps)
-        step[1] = (color2.components![1] - color1.components![1]) / CGFloat(steps)
-        step[2] = (color2.components![2] - color1.components![2]) / CGFloat(steps)
-        return step
-    }
+            self.themingColor[account] = colorThemingColor
+            self.themingColorElement[account] = colorThemingColorElement
+            self.themingColorText[account] = colorThemingColorText
 
-    private func mixPalette(steps: Int, color1: CGColor, color2: CGColor) -> [CGColor] {
-        var palette = [color1]
-        let step = stepCalc(steps: steps, color1: color1, color2: color2)
-        let c1Components = color1.components!
+            return true
+        }
 
-        for i in 1 ..< steps {
-            let r = c1Components[0] + step[0] * CGFloat(i)
-            let g = c1Components[1] + step[1] * CGFloat(i)
-            let b = c1Components[2] + step[2] * CGFloat(i)
+        return false
+    }
 
-            palette.append(UIColor(red: r, green: g, blue: b, alpha: 1).cgColor)
+    public func getTheming(account: String?) -> UIColor {
+        if let account, let color = self.themingColor[account] {
+            return color
         }
-        return palette
+        return customer
     }
 
-    /**
-     Generate colors from the official nextcloud color.
-     You can provide how many colors you want (multiplied by 3).
-     if `step` = 6,
-     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 palette1 = mixPalette(steps: steps, color1: red, color2: yellow)
-        let palette2 = mixPalette(steps: steps, color1: yellow, color2: blue)
-        let palette3 = mixPalette(steps: steps, color1: blue, color2: red)
+    public func getElement(account: String?) -> UIColor {
+        if let account, let color = self.themingColorElement[account] {
+            return color
+        }
+        return customer
+    }
 
-        return palette1 + palette2 + palette3
+    public func getText(account: String?) -> UIColor {
+        if let account, let color = self.themingColorText[account] {
+            return color
+        }
+        return .white
     }
 }

+ 2 - 0
Brand/iOSClient.plist

@@ -9,6 +9,8 @@
 	</array>
 	<key>CFBundleAllowMixedLocalizations</key>
 	<true/>
+	<key>LSMinimumSystemVersion</key>
+	<string>12.3</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>en</string>
 	<key>CFBundleDisplayName</key>

+ 0 - 2
Cartfile

@@ -1,2 +0,0 @@
-github "https://github.com/marinofaggiana/TOPasscodeViewController" "master"
-binary "https://code.videolan.org/videolan/VLCKit/raw/master/Packaging/MobileVLCKit.json" ~> 3.5.1

+ 0 - 2
Cartfile.resolved

@@ -1,2 +0,0 @@
-binary "https://code.videolan.org/videolan/VLCKit/raw/master/Packaging/MobileVLCKit.json" "3.6.0"
-github "marinofaggiana/TOPasscodeViewController" "ed795637acd2b1ef154e011a04ebab4686d0523c"

+ 1 - 1
ExternalResources/NCApplicationHandle.swift

@@ -53,7 +53,7 @@ class NCApplicationHandle: NSObject {
 
     // class: NCCollectionViewCommon (+Menu)
     // func: toggleMenu(metadata: tableMetadata, imageIcon: UIImage?)
-    func addCollectionViewCommonMenu(metadata: tableMetadata, imageIcon: UIImage?, actions: inout [NCMenuAction]) {
+    func addCollectionViewCommonMenu(metadata: tableMetadata, image: UIImage?, actions: inout [NCMenuAction]) {
     }
 
     // class: NCMore

+ 3 - 3
File Provider Extension UI/DocumentActionViewController.swift

@@ -32,9 +32,9 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
     override func loadView() {
         super.loadView()
 
-        view.backgroundColor = NCBrandColor.shared.brandElement
-        titleError.textColor = NCBrandColor.shared.brandText
-        cancelButton.setTitleColor(NCBrandColor.shared.brandText, for: .normal)
+        view.backgroundColor = NCBrandColor.shared.customer
+        titleError.textColor = NCBrandColor.shared.customerText
+        cancelButton.setTitleColor(NCBrandColor.shared.customerText, for: .normal)
 
         titleError.text = ""
     }

+ 44 - 48
File Provider Extension/FileProviderData.swift

@@ -33,19 +33,24 @@ class fileProviderData: NSObject {
     var domain: NSFileProviderDomain?
     var fileProviderManager: NSFileProviderManager = NSFileProviderManager.default
     let utilityFileSystem = NCUtilityFileSystem()
-
-    var account = ""
-    var user = ""
-    var userId = ""
-    var accountUrlBase = ""
-    var homeServerUrl = ""
-
+    let database = NCManageDatabase.shared
     var listFavoriteIdentifierRank: [String: NSNumber] = [:]
-
     var fileProviderSignalDeleteContainerItemIdentifier: [NSFileProviderItemIdentifier: NSFileProviderItemIdentifier] = [:]
     var fileProviderSignalUpdateContainerItem: [NSFileProviderItemIdentifier: FileProviderItem] = [:]
     var fileProviderSignalDeleteWorkingSetItemIdentifier: [NSFileProviderItemIdentifier: NSFileProviderItemIdentifier] = [:]
     var fileProviderSignalUpdateWorkingSetItem: [NSFileProviderItemIdentifier: FileProviderItem] = [:]
+    private var account: String = ""
+    var session: NCSession.Session {
+        if !account.isEmpty,
+           let tableAccount = self.database.getTableAccount(account: account) {
+            return NCSession.Session(account: tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId)
+        } else if let activeTableAccount = self.database.getActiveTableAccount() {
+            self.account = activeTableAccount.account
+            return NCSession.Session(account: activeTableAccount.account, urlBase: activeTableAccount.urlBase, user: activeTableAccount.user, userId: activeTableAccount.userId)
+        } else {
+            return NCSession.Session(account: "", urlBase: "", user: "", userId: "")
+        }
+    }
 
     enum FileProviderError: Error {
         case downloadError
@@ -81,54 +86,45 @@ class fileProviderData: NSObject {
         let version = NSString(format: NCBrandOptions.shared.textCopyrightNextcloudiOS as NSString, NCUtility().getVersionApp()) as String
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start File Provider session with level \(levelLog) " + version + " (File Provider Extension)")
 
-        // NO DOMAIN -> Set default account
-        if domain == nil {
-            guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return nil }
-            account = activeAccount.account
-            user = activeAccount.user
-            userId = activeAccount.userId
-            accountUrlBase = activeAccount.urlBase
-            homeServerUrl = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId)
-
-            NCManageDatabase.shared.setCapabilities(account: account)
-            NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: 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)
-        }
-
-        // DOMAIN
-        let accounts = NCManageDatabase.shared.getAllAccount()
-        if accounts.isEmpty { return nil }
-
-        for activeAccount in accounts {
-            guard let url = NSURL(string: activeAccount.urlBase) else { continue }
-            guard let host = url.host else { continue }
-            let accountDomain = activeAccount.userId + " (" + host + ")"
-            if accountDomain == domain!.identifier.rawValue {
-                account = activeAccount.account
-                user = activeAccount.user
-                userId = activeAccount.userId
-                accountUrlBase = activeAccount.urlBase
-                homeServerUrl = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId)
-
-                NCManageDatabase.shared.setCapabilities(account: account)
-
-                NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: 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)
+        var tblAccount = self.database.getActiveTableAccount()
+        if let domain {
+            for tableAccount in self.database.getAllTableAccount() {
+                guard let urlBase = NSURL(string: tableAccount.urlBase) else { continue }
+                guard let host = urlBase.host else { continue }
+                let accountDomain = tableAccount.userId + " (" + host + ")"
+                if accountDomain == domain.identifier.rawValue {
+                    let account = "\(tableAccount.user) \(tableAccount.urlBase)"
+                    tblAccount = self.database.getTableAccount(account: account)
+                    break
+                }
             }
         }
-        return nil
+        guard let tblAccount else { return nil }
+
+        self.account = tblAccount.account
+        /// NextcloudKit Session
+        NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+        NextcloudKit.shared.appendSession(account: tblAccount.account,
+                                          urlBase: tblAccount.urlBase,
+                                          user: tblAccount.user,
+                                          userId: tblAccount.userId,
+                                          password: NCKeychain().getPassword(account: tblAccount.account),
+                                          userAgent: userAgent,
+                                          nextcloudVersion: NCCapabilities.shared.getCapabilities(account: tblAccount.account).capabilityServerVersionMajor,
+                                          groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
+        NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
+
+        return tableAccount(value: tblAccount)
     }
 
     // MARK: -
 
     @discardableResult
     func signalEnumerator(ocId: String, type: TypeSignal) -> FileProviderItem? {
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
-              let parentItemIdentifier = fileProviderUtility().getParentItemIdentifier(metadata: metadata) else { return nil }
+        guard let metadata = self.database.getMetadataFromOcId(ocId),
+              let parentItemIdentifier = fileProviderUtility().getParentItemIdentifier(metadata: metadata) else {
+            return nil
+        }
         let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
 
         if type == .delete {

+ 7 - 7
File Provider Extension/FileProviderDomain.swift

@@ -28,7 +28,7 @@ class FileProviderDomain: NSObject {
         NSFileProviderManager.getDomainsWithCompletionHandler { fileProviderDomain, error in
             var domains: [String] = []
             let pathRelativeToDocumentStorage = NSFileProviderManager.default.documentStorageURL.absoluteString
-            let accounts = NCManageDatabase.shared.getAllAccount()
+            let tableAccounts = NCManageDatabase.shared.getAllTableAccount()
 
             for domain in fileProviderDomain {
                 domains.append(domain.identifier.rawValue)
@@ -37,10 +37,10 @@ class FileProviderDomain: NSObject {
             // Delete
             for domain in domains {
                 var domainFound = false
-                for account in accounts {
-                    guard let urlBase = NSURL(string: account.urlBase) else { continue }
+                for tableAccount in tableAccounts {
+                    guard let urlBase = NSURL(string: tableAccount.urlBase) else { continue }
                     guard let host = urlBase.host else { continue }
-                    let accountDomain = account.userId + " (" + host + ")"
+                    let accountDomain = tableAccount.userId + " (" + host + ")"
                     if domain == accountDomain {
                         domainFound = true
                         break
@@ -57,11 +57,11 @@ class FileProviderDomain: NSObject {
             }
 
             // Add
-            for account in accounts {
+            for tableAccount in tableAccounts {
                 var domainFound = false
-                guard let urlBase = NSURL(string: account.urlBase) else { continue }
+                guard let urlBase = NSURL(string: tableAccount.urlBase) else { continue }
                 guard let host = urlBase.host else { continue }
-                let accountDomain = account.userId + " (" + host + ")"
+                let accountDomain = tableAccount.userId + " (" + host + ")"
                 for domain in domains {
                     if domain == accountDomain {
                         domainFound = true

+ 18 - 17
File Provider Extension/FileProviderEnumerator.swift

@@ -30,16 +30,17 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
     var enumeratedItemIdentifier: NSFileProviderItemIdentifier
     var serverUrl: String?
     let providerUtility = fileProviderUtility()
+    let database = NCManageDatabase.shared
     var recordsPerPage: Int = 20
     var anchor: UInt64 = 0
 
     init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) {
         self.enumeratedItemIdentifier = enumeratedItemIdentifier
         if enumeratedItemIdentifier == .rootContainer {
-            serverUrl = fileProviderData.shared.homeServerUrl
+            serverUrl = NCUtilityFileSystem().getHomeServer(session: fileProviderData.shared.session)
         } else {
             if let metadata = providerUtility.getTableMetadataFromItemIdentifier(enumeratedItemIdentifier),
-               let directorySource = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+               let directorySource = self.database.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
                 serverUrl = directorySource.serverUrl + "/" + metadata.fileName
 
             }
@@ -55,15 +56,15 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
         if enumeratedItemIdentifier == .workingSet {
             var itemIdentifierMetadata: [NSFileProviderItemIdentifier: tableMetadata] = [:]
             /// Tags
-            let tags = NCManageDatabase.shared.getTags(predicate: NSPredicate(format: "account == %@", fileProviderData.shared.account))
+            let tags = self.database.getTags(predicate: NSPredicate(format: "account == %@", fileProviderData.shared.session.account))
             for tag in tags {
-                guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(tag.ocId)  else { continue }
+                guard let metadata = self.database.getMetadataFromOcId(tag.ocId)  else { continue }
                 itemIdentifierMetadata[providerUtility.getItemIdentifier(metadata: metadata)] = metadata
             }
             /// Favorite
-            fileProviderData.shared.listFavoriteIdentifierRank = NCManageDatabase.shared.getTableMetadatasDirectoryFavoriteIdentifierRank(account: fileProviderData.shared.account)
+            fileProviderData.shared.listFavoriteIdentifierRank = self.database.getTableMetadatasDirectoryFavoriteIdentifierRank(account: fileProviderData.shared.session.account)
             for (identifier, _) in fileProviderData.shared.listFavoriteIdentifierRank {
-                guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(identifier) else { continue }
+                guard let metadata = self.database.getMetadataFromOcId(identifier) else { continue }
                 itemIdentifierMetadata[providerUtility.getItemIdentifier(metadata: metadata)] = metadata
             }
             /// Create items
@@ -90,7 +91,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
             self.fetchItemsForPage(serverUrl: serverUrl, pageNumber: pageNumber) { metadatas in
                 if let metadatas {
                     for metadata in metadatas {
-                        if metadata.e2eEncrypted || (!metadata.session.isEmpty && metadata.session != NCNetworking.shared.sessionUploadBackgroundExtension) {
+                        if metadata.e2eEncrypted || (!metadata.session.isEmpty && metadata.session != NCNetworking.shared.sessionUploadBackgroundExt) {
                             continue
                         }
                         if let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadata) {
@@ -157,25 +158,25 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
     }
 
     func fetchItemsForPage(serverUrl: String, pageNumber: Int, completionHandler: @escaping (_ metadatas: Results<tableMetadata>?) -> Void) {
-        let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl)
+        let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.session.account, serverUrl)
 
         if pageNumber == 1 {
-            NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: NCKeychain().showHiddenFiles, account: fileProviderData.shared.account) { _, files, _, error in
-                if error == .success {
-                    NCManageDatabase.shared.convertFilesToMetadatas(files, useFirstAsMetadataFolder: true) { metadataFolder, metadatas in
+            NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: NCKeychain().showHiddenFiles, account: fileProviderData.shared.session.account) { _, files, _, error in
+                if error == .success, let files {
+                    self.database.convertFilesToMetadatas(files, useFirstAsMetadataFolder: true) { metadataFolder, metadatas in
                         /// FOLDER
-                        NCManageDatabase.shared.addMetadata(metadataFolder)
-                        NCManageDatabase.shared.addDirectory(e2eEncrypted: metadataFolder.e2eEncrypted, favorite: metadataFolder.favorite, ocId: metadataFolder.ocId, fileId: metadataFolder.fileId, etag: metadataFolder.etag, permissions: metadataFolder.permissions, richWorkspace: metadataFolder.richWorkspace, serverUrl: serverUrl, account: metadataFolder.account)
+                        self.database.addMetadata(metadataFolder)
+                        self.database.addDirectory(e2eEncrypted: metadataFolder.e2eEncrypted, favorite: metadataFolder.favorite, ocId: metadataFolder.ocId, fileId: metadataFolder.fileId, etag: metadataFolder.etag, permissions: metadataFolder.permissions, richWorkspace: metadataFolder.richWorkspace, serverUrl: serverUrl, account: metadataFolder.account)
                         /// FILES
-                        NCManageDatabase.shared.deleteMetadata(predicate: predicate)
-                        NCManageDatabase.shared.addMetadatas(metadatas)
+                        self.database.deleteMetadata(predicate: predicate)
+                        self.database.addMetadatas(metadatas)
                     }
                 }
-                let resultsMetadata = NCManageDatabase.shared.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: self.recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
+                let resultsMetadata = self.database.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: self.recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
                 completionHandler(resultsMetadata)
             }
         } else {
-            let resultsMetadata = NCManageDatabase.shared.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
+            let resultsMetadata = self.database.fetchPagedResults(ofType: tableMetadata.self, primaryKey: "ocId", recordsPerPage: recordsPerPage, pageNumber: pageNumber, filter: predicate, sortedByKeyPath: "fileName")
             completionHandler(resultsMetadata)
         }
     }

+ 48 - 53
File Provider Extension/FileProviderExtension+Actions.swift

@@ -27,23 +27,23 @@ import NextcloudKit
 
 extension FileProviderExtension {
     override func createDirectory(withName directoryName: String, inParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-        guard let tableDirectory = providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
+        guard let tableDirectory = providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.session.account, homeServerUrl: utilityFileSystem.getHomeServer(session: fileProviderData.shared.session)) else {
             return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
-        let directoryName = utilityFileSystem.createFileName(directoryName, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.account)
+        let directoryName = utilityFileSystem.createFileName(directoryName, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.session.account)
         let serverUrlFileName = tableDirectory.serverUrl + "/" + directoryName
 
-        NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: fileProviderData.shared.account) { _, ocId, _, error in
+        NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: fileProviderData.shared.session.account) { _, ocId, _, _, error in
             if error == .success {
-                NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: fileProviderData.shared.account) { _, files, _, error in
-                    if error == .success, let file = files.first {
+                NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: fileProviderData.shared.session.account) { _, files, _, error in
+                    if error == .success, let file = files?.first {
                         let isDirectoryEncrypted = self.utilityFileSystem.isDirectoryE2EE(file: file)
-                        let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryEncrypted)
+                        let metadata = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryEncrypted)
 
-                        NCManageDatabase.shared.addDirectory(e2eEncrypted: false, favorite: false, ocId: ocId!, fileId: metadata.fileId, etag: metadata.etag, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
-                        NCManageDatabase.shared.addMetadata(metadata)
+                        self.database.addDirectory(e2eEncrypted: false, favorite: false, ocId: ocId!, fileId: metadata.fileId, etag: metadata.etag, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
+                        self.database.addMetadata(metadata)
 
-                        guard let metadataInsert = NCManageDatabase.shared.getMetadataFromOcId(ocId!),
+                        guard let metadataInsert = self.database.getMetadataFromOcId(ocId!),
                               let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadataInsert) else {
                             return completionHandler(nil, NSFileProviderError(.noSuchItem))
                         }
@@ -69,7 +69,7 @@ extension FileProviderExtension {
         let serverUrl = metadata.serverUrl
         let fileName = metadata.fileName
 
-        NextcloudKit.shared.deleteFileOrFolder(serverUrlFileName: serverUrlFileName, account: metadata.account) { account, error in
+        NextcloudKit.shared.deleteFileOrFolder(serverUrlFileName: serverUrlFileName, account: metadata.account) { account, _, error in
             if error == .success {
                 let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue)
 
@@ -81,11 +81,11 @@ extension FileProviderExtension {
 
                 if isDirectory {
                     let dirForDelete = self.utilityFileSystem.stringAppendServerUrl(serverUrl, addFileName: fileName)
-                    NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: dirForDelete, account: account)
+                    self.database.deleteDirectoryAndSubDirectory(serverUrl: dirForDelete, account: account)
                 }
 
-                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocId))
-                NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", ocId))
+                self.database.deleteMetadataOcId(ocId)
+                self.database.deleteLocalFileOcId(ocId)
                 completionHandler(nil)
             } else {
                 completionHandler(NSFileProviderError(.serverUnreachable))
@@ -101,38 +101,41 @@ extension FileProviderExtension {
         let ocIdFrom = metadataFrom.ocId
         let serverUrlFrom = metadataFrom.serverUrl
         let fileNameFrom = serverUrlFrom + "/" + itemFrom.filename
-        guard let tableDirectoryTo = providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
+
+        guard let tableDirectoryTo = providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.session.account, homeServerUrl: utilityFileSystem.getHomeServer(session: fileProviderData.shared.session)) else {
             return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
         let serverUrlTo = tableDirectoryTo.serverUrl
-        let fileNameTo = serverUrlTo + "/" + itemFrom.filename
+        var fileNameTo = serverUrlTo + "/" + itemFrom.filename
+        if let newName {
+            fileNameTo = serverUrlTo + "/" + newName
+        }
 
-        NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNameFrom, serverUrlFileNameDestination: fileNameTo, overwrite: false, account: metadataFrom.account) { account, error in
-            if error == .success {
-                if metadataFrom.directory {
-                    NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: serverUrlFrom, account: account)
-                    NCManageDatabase.shared.renameDirectory(ocId: ocIdFrom, serverUrl: serverUrlTo)
-                }
-                NCManageDatabase.shared.moveMetadata(ocId: ocIdFrom, serverUrlTo: serverUrlTo)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+            NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNameFrom, serverUrlFileNameDestination: fileNameTo, overwrite: true, account: metadataFrom.account) { account, _, error in
+                if error == .success {
+                    if metadataFrom.directory {
+                        self.database.deleteDirectoryAndSubDirectory(serverUrl: serverUrlFrom, account: account)
+                        self.database.renameDirectory(ocId: ocIdFrom, serverUrl: serverUrlTo)
+                    }
+                    self.database.moveMetadata(ocId: ocIdFrom, serverUrlTo: serverUrlTo)
 
-                guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocIdFrom) else {
-                    return completionHandler(nil, NSFileProviderError(.noSuchItem))
+                    guard let metadata = self.database.getMetadataFromOcId(ocIdFrom) else {
+                        return completionHandler(nil, NSFileProviderError(.noSuchItem))
 
-                }
-                let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
+                    }
+                    let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
 
-                completionHandler(item, nil)
-            } else if error.errorCode == NCGlobal.shared.errorBadRequest {
-                completionHandler(nil, NSFileProviderError(.noSuchItem, userInfo: [NSLocalizedDescriptionKey: error.errorDescription, NSLocalizedFailureReasonErrorKey: ""]))
-            } else {
-                completionHandler(nil, NSFileProviderError(.serverUnreachable))
+                    completionHandler(item, nil)
+                } else {
+                    completionHandler(nil, NSFileProviderError(.noSuchItem, userInfo: [NSLocalizedDescriptionKey: error.errorDescription, NSLocalizedFailureReasonErrorKey: ""]))
+                }
             }
         }
     }
 
     override func renameItem(withIdentifier itemIdentifier: NSFileProviderItemIdentifier, toName itemName: String, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
-        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier),
-              let directoryTable = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) else {
+        guard let metadata = providerUtility.getTableMetadataFromItemIdentifier(itemIdentifier) else {
             return completionHandler(nil, NSFileProviderError(.noSuchItem))
         }
         let fileNameFrom = metadata.fileNameView
@@ -140,30 +143,22 @@ extension FileProviderExtension {
         let fileNamePathTo = metadata.serverUrl + "/" + itemName
         let ocId = metadata.ocId
 
-        NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNamePathFrom, serverUrlFileNameDestination: fileNamePathTo, overwrite: false, account: metadata.account) { account, error in
+        NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: fileNamePathFrom, serverUrlFileNameDestination: fileNamePathTo, overwrite: false, account: metadata.account) { _, _, error in
             if error == .success {
-                // Rename metadata
-                NCManageDatabase.shared.renameMetadata(fileNameTo: itemName, ocId: ocId)
 
-                guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
+                self.database.renameMetadata(fileNameNew: itemName, ocId: ocId)
+                self.database.setMetadataServeUrlFileNameStatusNormal(ocId: ocId)
+
+                guard let metadata = self.database.getMetadataFromOcId(ocId) else {
                     return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
-                if metadata.directory {
-                    NCManageDatabase.shared.setDirectory(serverUrl: fileNamePathFrom, serverUrlTo: fileNamePathTo, encrypted: directoryTable.e2eEncrypted, account: account)
-                } else {
-                    let itemIdentifier = self.providerUtility.getItemIdentifier(metadata: metadata)
-                    self.providerUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: fileNameFrom), toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(itemIdentifier.rawValue, fileNameView: itemName))
-                    self.providerUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(itemIdentifier.rawValue, etag: metadata.etag), toPath: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(itemIdentifier.rawValue, etag: metadata.etag))
-                    self.providerUtility.moveFile(self.utilityFileSystem.getDirectoryProviderStorageIconOcId(itemIdentifier.rawValue, etag: metadata.etag), toPath: self.utilityFileSystem.getDirectoryProviderStorageIconOcId(itemIdentifier.rawValue, etag: metadata.etag))
-                    NCManageDatabase.shared.setLocalFile(ocId: ocId, fileName: itemName)
-                }
 
                 guard let parentItemIdentifier = self.providerUtility.getParentItemIdentifier(metadata: metadata) else {
                     return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
                 let item = FileProviderItem(metadata: tableMetadata.init(value: metadata), parentItemIdentifier: parentItemIdentifier)
                 completionHandler(item, nil)
-            } else if error.errorCode == NCGlobal.shared.errorBadRequest  {
+            } else if error.errorCode == NCGlobal.shared.errorBadRequest {
                 completionHandler(nil, NSFileProviderError(.noSuchItem, userInfo: [NSLocalizedDescriptionKey: error.errorDescription, NSLocalizedFailureReasonErrorKey: ""]))
             } else {
                 completionHandler(nil, NSFileProviderError(.serverUnreachable))
@@ -188,20 +183,20 @@ extension FileProviderExtension {
         }
 
         if (favorite == true && metadata.favorite == false) || (favorite == false && metadata.favorite == true) {
-            let fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
-            NextcloudKit.shared.setFavorite(fileName: fileNamePath, favorite: favorite, account: metadata.account) { _, error in
+            let fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, session: fileProviderData.shared.session)
+            NextcloudKit.shared.setFavorite(fileName: fileNamePath, favorite: favorite, account: metadata.account) { _, _, error in
                 if error == .success {
-                    guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
+                    guard let metadata = self.database.getMetadataFromOcId(ocId) else {
                         return completionHandler(nil, NSFileProviderError(.noSuchItem))
                     }
                     // Change DB
                     metadata.favorite = favorite
-                    NCManageDatabase.shared.addMetadata(metadata)
+                    self.database.addMetadata(metadata)
                     /// SIGNAL
                     let item = fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .workingSet)
                     completionHandler(item, nil)
                 } else {
-                    guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
+                    guard let metadata = self.database.getMetadataFromOcId(ocId) else {
                         return completionHandler(nil, NSFileProviderError(.noSuchItem))
                     }
                     // Errore, remove from listFavoriteIdentifierRank
@@ -221,7 +216,7 @@ extension FileProviderExtension {
         let ocId = metadataForTag.ocId
         let account = metadataForTag.account
 
-        NCManageDatabase.shared.addTag(ocId, tagIOS: tagData, account: account)
+        self.database.addTag(ocId, tagIOS: tagData, account: account)
         /// SIGNAL WORKINGSET
         let item = fileProviderData.shared.signalEnumerator(ocId: ocId, type: .workingSet)
         completionHandler(item, nil)

+ 14 - 11
File Provider Extension/FileProviderExtension+NetworkingDelegate.swift

@@ -37,10 +37,19 @@ extension FileProviderExtension: NCNetworkingDelegate {
         guard let url = task.currentRequest?.url,
               let metadata = NCManageDatabase.shared.getMetadata(from: url, sessionTaskIdentifier: task.taskIdentifier) else { return }
 
-        DispatchQueue.global(qos: .userInteractive).async {
+        if let ocId, !metadata.ocIdTransfer.isEmpty {
+            let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocIdTransfer)
+            let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(ocId)
+            self.utilityFileSystem.copyFile(atPath: atPath, toPath: toPath)
+        }
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
             if error == .success, let ocId {
                 /// SIGNAL
-                fileProviderData.shared.signalEnumerator(ocId: metadata.ocIdTemp, type: .delete)
+                fileProviderData.shared.signalEnumerator(ocId: metadata.ocIdTransfer, type: .delete)
+                if !metadata.ocIdTransfer.isEmpty, ocId != metadata.ocIdTransfer {
+                    NCManageDatabase.shared.deleteMetadataOcId(metadata.ocIdTransfer)
+                }
                 metadata.fileName = fileName
                 metadata.serverUrl = serverUrl
                 metadata.uploadDate = (date as? NSDate) ?? NSDate()
@@ -61,19 +70,13 @@ extension FileProviderExtension: NCNetworkingDelegate {
 
                 NCManageDatabase.shared.addMetadata(metadata)
                 NCManageDatabase.shared.addLocalFile(metadata: metadata)
-                /// NEW File
-                if !metadata.ocIdTemp.isEmpty, ocId != metadata.ocIdTemp {
-                    let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocIdTemp)
-                    let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(ocId)
-                    self.utilityFileSystem.copyFile(atPath: atPath, toPath: toPath)
-                    NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocIdTemp))
-                }
+
                 /// SIGNAL
                 fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .update)
             } else {
-                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocIdTemp))
+                NCManageDatabase.shared.deleteMetadataOcId(metadata.ocIdTransfer)
                 /// SIGNAL
-                fileProviderData.shared.signalEnumerator(ocId: metadata.ocIdTemp, type: .delete)
+                fileProviderData.shared.signalEnumerator(ocId: metadata.ocIdTransfer, type: .delete)
             }
         }
     }

+ 8 - 7
File Provider Extension/FileProviderExtension+Thumbnail.swift

@@ -24,6 +24,7 @@
 import UIKit
 import FileProvider
 import NextcloudKit
+import Alamofire
 
 extension FileProviderExtension {
     override func fetchThumbnails(for itemIdentifiers: [NSFileProviderItemIdentifier], requestedSize size: CGSize, perThumbnailCompletionHandler: @escaping (NSFileProviderItemIdentifier, Data?, Error?) -> Void, completionHandler: @escaping (Error?) -> Void) -> Progress {
@@ -36,14 +37,14 @@ extension FileProviderExtension {
                 if counterProgress == progress.totalUnitCount { completionHandler(nil) }
                 continue
             }
-            let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)
 
-            NextcloudKit.shared.downloadPreview(fileId: metadata.fileId, widthPreview: Int(size.width), heightPreview: Int(size.height), etag: metadata.etag, account: metadata.account) { _ in
-            } completion: { _, data, error in
-                if error == .success, let data {
-                    do {
-                        try data.write(to: URL(fileURLWithPath: fileNameIconLocalPath), options: .atomic)
-                    } catch { }
+            NextcloudKit.shared.downloadPreview(fileId: metadata.fileId,
+                                                width: Int(size.width),
+                                                height: Int(size.height),
+                                                etag: metadata.etag,
+                                                account: metadata.account) { _ in
+            } completion: { _, _, _, _, responseData, error in
+                if error == .success, let data = responseData?.data {
                     perThumbnailCompletionHandler(itemIdentifier, data, nil)
                 } else {
                     perThumbnailCompletionHandler(itemIdentifier, nil, NSFileProviderError(.serverUnreachable))

+ 76 - 53
File Provider Extension/FileProviderExtension.swift

@@ -55,16 +55,13 @@ import Alamofire
 class FileProviderExtension: NSFileProviderExtension {
     let providerUtility = fileProviderUtility()
     let utilityFileSystem = NCUtilityFileSystem()
+    let database = NCManageDatabase.shared
 
     override init() {
         super.init()
 
-        // Create directory File Provider Storage
         _ = utilityFileSystem.directoryProviderStorage
-        // Configure URLSession
-        _ = NCNetworking.shared.sessionManagerUploadBackgroundExtension
-        // Domains
-        // FileProviderDomain().registerDomains()
+        _ = fileProviderData.shared.setupAccount(domain: domain, providerExtension: self)
     }
 
     deinit {
@@ -114,12 +111,12 @@ class FileProviderExtension: NSFileProviderExtension {
     override func item(for identifier: NSFileProviderItemIdentifier) throws -> NSFileProviderItem {
         if identifier == .rootContainer {
             let metadata = tableMetadata()
-            metadata.account = fileProviderData.shared.account
+            metadata.account = fileProviderData.shared.session.account
             metadata.directory = true
             metadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
             metadata.fileName = "root"
             metadata.fileNameView = "root"
-            metadata.serverUrl = fileProviderData.shared.homeServerUrl
+            metadata.serverUrl = utilityFileSystem.getHomeServer(session: fileProviderData.shared.session)
             metadata.classFile = NKCommon.TypeClassFile.directory.rawValue
             return FileProviderItem(metadata: metadata, parentItemIdentifier: NSFileProviderItemIdentifier(NSFileProviderItemIdentifier.rootContainer.rawValue))
         } else {
@@ -170,35 +167,36 @@ class FileProviderExtension: NSFileProviderExtension {
         if let result = fileProviderData.shared.getUploadMetadata(id: itemIdentifier.rawValue) {
             metadata = result.metadata
         } else {
-            metadata = NCManageDatabase.shared.getMetadataFromOcIdAndOcIdTemp(itemIdentifier.rawValue)
+            metadata = self.database.getMetadataFromOcIdAndocIdTransfer(itemIdentifier.rawValue)
         }
         guard let metadata else {
             return completionHandler(NSFileProviderError(.noSuchItem))
         }
-        if metadata.session == NCNetworking.shared.sessionUploadBackgroundExtension {
+        if metadata.session == NCNetworking.shared.sessionUploadBackgroundExt {
             return completionHandler(nil)
         }
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
         let fileNameLocalPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)
         // Exists ? return
-        if let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)),
+        if let tableLocalFile = self.database.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)),
            utilityFileSystem.fileProviderStorageExists(metadata),
            tableLocalFile.etag == metadata.etag {
             return completionHandler(nil)
         } else {
-            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                       session: NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload,
-                                                       sessionError: "",
-                                                       selector: "",
-                                                       status: NCGlobal.shared.metadataStatusDownloading)
+            self.database.setMetadataSession(ocId: metadata.ocId,
+                                             session: NCNetworking.shared.sessionDownload,
+                                             sessionTaskIdentifier: 0,
+                                             sessionError: "",
+                                             selector: "",
+                                             status: NCGlobal.shared.metadataStatusDownloading)
         }
         /// SIGNAL
         fileProviderData.shared.signalEnumerator(ocId: metadata.ocId, type: .update)
 
         NextcloudKit.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: metadata.account, requestHandler: { _ in
         }, taskHandler: { task in
-            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                       taskIdentifier: task.taskIdentifier)
+            self.database.setMetadataSession(ocId: metadata.ocId,
+                                             sessionTaskIdentifier: task.taskIdentifier)
             fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(itemIdentifier.rawValue)) { _ in }
         }, progressHandler: { _ in
         }) { _, etag, date, _, _, _, error in
@@ -215,16 +213,16 @@ class FileProviderExtension: NSFileProviderExtension {
                 metadata.status = NCGlobal.shared.metadataStatusNormal
                 metadata.date = (date as? NSDate) ?? NSDate()
                 metadata.etag = etag ?? ""
-                NCManageDatabase.shared.addLocalFile(metadata: metadata)
-                NCManageDatabase.shared.addMetadata(metadata)
+                self.database.addLocalFile(metadata: metadata)
+                self.database.addMetadata(metadata)
                 completionHandler(nil)
             } else if error.errorCode == 200 {
-                NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusNormal)
+                self.database.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusNormal)
                 completionHandler(nil)
             } else {
                 metadata.status = NCGlobal.shared.metadataStatusDownloadError
                 metadata.sessionError = error.errorDescription
-                NCManageDatabase.shared.addMetadata(metadata)
+                self.database.addMetadata(metadata)
                 completionHandler(NSFileProviderError(.noSuchItem))
             }
             /// SIGNAL
@@ -242,23 +240,33 @@ class FileProviderExtension: NSFileProviderExtension {
         if let result = fileProviderData.shared.getUploadMetadata(id: itemIdentifier.rawValue) {
             metadata = result.metadata
         } else {
-            metadata = NCManageDatabase.shared.getMetadataFromOcIdAndOcIdTemp(itemIdentifier.rawValue)
+            metadata = self.database.getMetadataFromOcIdAndocIdTransfer(itemIdentifier.rawValue)
         }
         guard let metadata else {
             return
         }
         let serverUrlFileName = metadata.serverUrl + "/" + fileName
 
-        NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                   session: NCNetworking.shared.sessionUploadBackgroundExtension,
-                                                   sessionError: "",
-                                                   selector: "",
-                                                   status: NCGlobal.shared.metadataStatusUploading)
-        if let task = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance).upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: url.path, dateCreationFile: nil, dateModificationFile: nil, account: metadata.account, session: NCNetworking.shared.sessionManagerUploadBackgroundExtension) {
-            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                       status: NCGlobal.shared.metadataStatusUploading,
-                                                       taskIdentifier: task.taskIdentifier)
-            fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(metadata.fileId)) { _ in }
+        self.database.setMetadataSession(ocId: metadata.ocId,
+                                         session: NCNetworking.shared.sessionUploadBackgroundExt,
+                                         sessionTaskIdentifier: 0,
+                                         sessionError: "",
+                                         selector: "",
+                                         status: NCGlobal.shared.metadataStatusUploading)
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+            if let task = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance).upload(serverUrlFileName: serverUrlFileName,
+                                                                                                      fileNameLocalPath: url.path,
+                                                                                                      dateCreationFile: nil,
+                                                                                                      dateModificationFile: nil,
+                                                                                                      overwrite: true,
+                                                                                                      account: metadata.account,
+                                                                                                      sessionIdentifier: metadata.session) {
+                self.database.setMetadataSession(ocId: metadata.ocId,
+                                                 sessionTaskIdentifier: task.taskIdentifier,
+                                                 status: NCGlobal.shared.metadataStatusUploading)
+                fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(metadata.fileId)) { _ in }
+            }
         }
     }
 
@@ -266,9 +274,10 @@ class FileProviderExtension: NSFileProviderExtension {
         let pathComponents = url.pathComponents
         assert(pathComponents.count > 2)
         let itemIdentifier = NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
-        guard let metadata = NCManageDatabase.shared.getMetadataFromOcIdAndOcIdTemp(itemIdentifier.rawValue) else { return }
-        if metadata.session == NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload {
-            NextcloudKit.shared.sessionManager.session.getTasksWithCompletionHandler { _, _, downloadTasks in
+        guard let metadata = self.database.getMetadataFromOcIdAndocIdTransfer(itemIdentifier.rawValue) else { return }
+        if metadata.session == NCNetworking.shared.sessionDownload {
+            let session = NextcloudKit.shared.nkCommonInstance.getSession(account: metadata.session)?.sessionData.session
+            session?.getTasksWithCompletionHandler { _, _, downloadTasks in
                 downloadTasks.forEach { task in
                     if metadata.sessionTaskIdentifier == task.taskIdentifier {
                         task.cancel()
@@ -281,7 +290,7 @@ class FileProviderExtension: NSFileProviderExtension {
     override func importDocument(at fileURL: URL, toParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) {
         DispatchQueue.main.async {
             autoreleasepool {
-                guard let tableDirectory = self.providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.account, homeServerUrl: fileProviderData.shared.homeServerUrl) else {
+                guard let tableDirectory = self.providerUtility.getTableDirectoryFromParentItemIdentifier(parentItemIdentifier, account: fileProviderData.shared.session.account, homeServerUrl: self.utilityFileSystem.getHomeServer(session: fileProviderData.shared.session)) else {
                     return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
                 var size = 0 as Int64
@@ -299,34 +308,48 @@ class FileProviderExtension: NSFileProviderExtension {
                     return completionHandler(nil, NSFileProviderError(.noSuchItem))
                 }
 
-                let fileName = self.utilityFileSystem.createFileName(fileURL.lastPathComponent, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.account)
-                let ocIdTemp = NSUUID().uuidString.lowercased()
+                let fileName = self.utilityFileSystem.createFileName(fileURL.lastPathComponent, serverUrl: tableDirectory.serverUrl, account: fileProviderData.shared.session.account)
+                let ocIdTransfer = NSUUID().uuidString.lowercased()
 
                 NSFileCoordinator().coordinate(readingItemAt: fileURL, options: .withoutChanges, error: &error) { url in
-                    self.providerUtility.copyFile(url.path, toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName))
+                    self.providerUtility.copyFile(url.path, toPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTransfer, fileNameView: fileName))
                 }
 
                 fileURL.stopAccessingSecurityScopedResource()
 
-                let metadata = NCManageDatabase.shared.createMetadata(account: fileProviderData.shared.account, user: fileProviderData.shared.user, userId: fileProviderData.shared.userId, fileName: fileName, fileNameView: fileName, ocId: ocIdTemp, serverUrl: tableDirectory.serverUrl, urlBase: fileProviderData.shared.accountUrlBase, url: "", contentType: "")
-                metadata.session = NCNetworking.shared.sessionUploadBackgroundExtension
-                metadata.size = size
-                metadata.status = NCGlobal.shared.metadataStatusUploading
+                let metadataForUpload = self.database.createMetadata(fileName: fileName,
+                                                                     fileNameView: fileName,
+                                                                     ocId: ocIdTransfer,
+                                                                     serverUrl: tableDirectory.serverUrl,
+                                                                     url: "",
+                                                                     contentType: "",
+                                                                     session: fileProviderData.shared.session,
+                                                                     sceneIdentifier: nil)
+
+                metadataForUpload.session = NCNetworking.shared.sessionUploadBackgroundExt
+                metadataForUpload.size = size
+                metadataForUpload.status = NCGlobal.shared.metadataStatusUploading
 
-                NCManageDatabase.shared.addMetadata(metadata)
+                self.database.addMetadata(metadataForUpload)
 
                 let serverUrlFileName = tableDirectory.serverUrl + "/" + fileName
-                let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName)
-
-                if let task = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance).upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: nil, dateModificationFile: nil, account: metadata.account, session: NCNetworking.shared.sessionManagerUploadBackgroundExtension) {
-                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                               status: NCGlobal.shared.metadataStatusUploading,
-                                                               taskIdentifier: task.taskIdentifier)
-                    fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(ocIdTemp)) { _ in }
-                    fileProviderData.shared.appendUploadMetadata(id: ocIdTemp, metadata: metadata, task: task)
+                let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(ocIdTransfer, fileNameView: fileName)
+
+                if let task = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance).upload(serverUrlFileName: serverUrlFileName,
+                                                                                                          fileNameLocalPath: fileNameLocalPath,
+                                                                                                          dateCreationFile: nil,
+                                                                                                          dateModificationFile: nil,
+                                                                                                          overwrite: true,
+                                                                                                          account: metadataForUpload.account,
+                                                                                                          sessionIdentifier: metadataForUpload.session) {
+                    self.database.setMetadataSession(ocId: metadataForUpload.ocId,
+                                                     sessionTaskIdentifier: task.taskIdentifier,
+                                                     status: NCGlobal.shared.metadataStatusUploading)
+                    fileProviderData.shared.fileProviderManager.register(task, forItemWithIdentifier: NSFileProviderItemIdentifier(ocIdTransfer)) { _ in }
+                    fileProviderData.shared.appendUploadMetadata(id: ocIdTransfer, metadata: metadataForUpload, task: task)
                 }
 
-                let item = FileProviderItem(metadata: tableMetadata.init(value: metadata), parentItemIdentifier: parentItemIdentifier)
+                let item = FileProviderItem(metadata: tableMetadata.init(value: metadataForUpload), parentItemIdentifier: parentItemIdentifier)
                 completionHandler(item, nil)
             }
         }

+ 1 - 1
File Provider Extension/FileProviderItem.swift

@@ -36,7 +36,7 @@ class FileProviderItem: NSObject, NSFileProviderItem {
         return metadata.fileNameView
     }
     var typeIdentifier: String {
-        let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: "", directory: metadata.directory)
+        let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: "", directory: metadata.directory, account: metadata.account)
         return results.typeIdentifier
     }
     var capabilities: NSFileProviderItemCapabilities {

+ 7 - 6
File Provider Extension/FileProviderUtility.swift

@@ -26,15 +26,16 @@ import UIKit
 class fileProviderUtility: NSObject {
     let fileManager = FileManager()
     let utilityFileSystem = NCUtilityFileSystem()
+    let database = NCManageDatabase.shared
 
     func getAccountFromItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) -> String? {
         let ocId = itemIdentifier.rawValue
-        return NCManageDatabase.shared.getMetadataFromOcId(ocId)?.account
+        return self.database.getMetadataFromOcId(ocId)?.account
     }
 
     func getTableMetadataFromItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) -> tableMetadata? {
         let ocId = itemIdentifier.rawValue
-        return NCManageDatabase.shared.getMetadataFromOcId(ocId)
+        return self.database.getMetadataFromOcId(ocId)
     }
 
     func getItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier {
@@ -42,13 +43,13 @@ class fileProviderUtility: NSObject {
     }
 
     func getParentItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier? {
-        let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: metadata.urlBase, userId: metadata.userId)
-        if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+        let homeServerUrl = utilityFileSystem.getHomeServer(session: fileProviderData.shared.session)
+        if let directory = self.database.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
             if directory.serverUrl == homeServerUrl {
                 return NSFileProviderItemIdentifier(NSFileProviderItemIdentifier.rootContainer.rawValue)
             } else {
                 // get the metadata.ocId of parent Directory
-                if let metadata = NCManageDatabase.shared.getMetadataFromOcId(directory.ocId) {
+                if let metadata = self.database.getMetadataFromOcId(directory.ocId) {
                     let identifier = getItemIdentifier(metadata: metadata)
                     return identifier
                 }
@@ -65,7 +66,7 @@ class fileProviderUtility: NSObject {
             guard let metadata = getTableMetadataFromItemIdentifier(parentItemIdentifier) else { return nil }
             predicate = NSPredicate(format: "ocId == %@", metadata.ocId)
         }
-        guard let directory = NCManageDatabase.shared.getTableDirectory(predicate: predicate) else { return nil }
+        guard let directory = self.database.getTableDirectory(predicate: predicate) else { return nil }
         return directory
     }
 

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 320 - 66
Nextcloud.xcodeproj/project.pbxproj


+ 7 - 3
Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme

@@ -120,8 +120,12 @@
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES"
       launchAutomaticallySubstyle = "2">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
+      <RemoteRunnable
+         runnableDebuggingMode = "0"
+         BundleIdentifier = "it.twsweb.Nextcloud"
+         RemotePath = "/var/containers/Bundle/Application/92F13FE8-7056-4509-8468-5856675AB1CA/Nextcloud.app">
+      </RemoteRunnable>
+      <MacroExpansion>
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "F77B0DEB1D118A16002130FE"
@@ -129,7 +133,7 @@
             BlueprintName = "Nextcloud"
             ReferencedContainer = "container:Nextcloud.xcodeproj">
          </BuildableReference>
-      </BuildableProductRunnable>
+      </MacroExpansion>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"

+ 2 - 2
Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme

@@ -121,9 +121,9 @@
       allowLocationSimulation = "YES"
       launchAutomaticallySubstyle = "2">
       <RemoteRunnable
-         runnableDebuggingMode = "1"
+         runnableDebuggingMode = "0"
          BundleIdentifier = "it.twsweb.Nextcloud"
-         RemotePath = "/Users/marinofaggiana/Library/Developer/CoreSimulator/Devices/4DBB1715-4E24-4CE7-B67C-CA327B95130C/data/Containers/Bundle/Application/F427D7FF-1632-4BB9-ADEA-A094898BB6C7/Nextcloud.app">
+         RemotePath = "/var/containers/Bundle/Application/92F13FE8-7056-4509-8468-5856675AB1CA/Nextcloud.app">
       </RemoteRunnable>
       <MacroExpansion>
          <BuildableReference

+ 1 - 2
Notification Service Extension/NotificationService.swift

@@ -36,8 +36,7 @@ class NotificationService: UNNotificationServiceExtension {
             bestAttemptContent.body = "Nextcloud notification"
             do {
                 if let message = bestAttemptContent.userInfo["subject"] as? String {
-                    let tableAccounts = NCManageDatabase.shared.getAllAccount()
-                    for tableAccount in tableAccounts {
+                    for tableAccount in NCManageDatabase.shared.getAllTableAccount() {
                         guard let privateKey = NCKeychain().getPushNotificationPrivateKey(account: tableAccount.account),
                               let decryptedMessage = NCPushNotificationEncryption.shared().decryptPushNotification(message, withDevicePrivateKey: privateKey),
                               let data = decryptedMessage.data(using: .utf8) else {

+ 1 - 9
README.md

@@ -38,18 +38,10 @@ 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 15 Project Setup
+### Xcode 16 Project Setup
 
 #### Dependencies
 
-After forking a repository you have to build the dependencies. Dependencies are managed with Carthage version 0.38.0 or later. 
-Run
-
-```
-carthage update --use-xcframeworks --platform iOS
-```
-to fetch and compile the dependencies.
-
 In order to build the project in Xcode you will also need a file `GoogleService-Info.plist` at the root of the repository which contains the Firebase configuration. For development work you can use a mock version found [here](https://github.com/firebase/quickstart-ios/blob/master/mock-GoogleService-Info.plist).
 
 ### Creating Pull requests

+ 9 - 7
Share/NCShareCell.swift

@@ -27,7 +27,7 @@ import NextcloudKit
 protocol NCShareCellDelegate: AnyObject {
     var uploadStarted: Bool { get }
     func removeFile(named fileName: String)
-    func renameFile(named fileName: String)
+    func renameFile(named fileName: String, account: String)
 }
 
 class NCShareCell: UITableViewCell {
@@ -36,13 +36,15 @@ class NCShareCell: UITableViewCell {
     @IBOutlet weak var moreButton: UIButton!
     @IBOutlet weak var sizeCell: UILabel!
     weak var delegate: (NCShareCellDelegate & UIViewController)?
-    var fileName = ""
+    var fileName: String = ""
+    var account: String = ""
     let utilityFileSystem = NCUtilityFileSystem()
     let utility = NCUtility()
 
-    func setup(fileName: String) {
+    func setup(fileName: String, account: String) {
         self.fileName = fileName
-        let resultInternalType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false)
+        self.account = account
+        let resultInternalType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: "", directory: false, account: account)
 
         backgroundColor = .systemBackground
         imageCell?.layer.cornerRadius = 6
@@ -52,7 +54,7 @@ class NCShareCell: UITableViewCell {
             imageCell.image = image
             imageCell.contentMode = .scaleAspectFill
         } else {
-            imageCell.image = utility.loadImage(named: resultInternalType.iconName, useTypeIconFile: true)
+            imageCell.image = utility.loadImage(named: resultInternalType.iconName, useTypeIconFile: true, account: account)
             imageCell.contentMode = .scaleAspectFit
         }
 
@@ -61,7 +63,7 @@ class NCShareCell: UITableViewCell {
         let fileSize = utilityFileSystem.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
         sizeCell?.text = utilityFileSystem.transformedSize(fileSize)
 
-        moreButton?.setImage(NCImageCache.images.buttonMore, for: .normal)
+        moreButton?.setImage(NCImageCache.shared.getImageButtonMore(), for: .normal)
     }
 
     @IBAction func buttonTapped(_ sender: Any) {
@@ -69,7 +71,7 @@ class NCShareCell: UITableViewCell {
         let alertController = UIAlertController(title: "", message: fileName, preferredStyle: .alert)
 
         alertController.addAction(UIAlertAction(title: NSLocalizedString("_rename_file_", comment: ""), style: .default) { _ in
-            self.delegate?.renameFile(named: self.fileName)
+            self.delegate?.renameFile(named: self.fileName, account: self.account)
         })
 
         alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_file_", comment: ""), style: .default) { _ in

+ 27 - 31
Share/NCShareExtension+DataSource.swift

@@ -27,15 +27,14 @@ import NextcloudKit
 // MARK: - Collection View (target folder)
 
 extension NCShareExtension: UICollectionViewDelegate {
-
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return showAlert(description: "_invalid_url_") }
+        guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else { return showAlert(description: "_invalid_url_") }
         let serverUrl = utilityFileSystem.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)
-        if metadata.e2eEncrypted && !NCKeychain().isEndToEndEnabled(account: activeAccount.account) {
+        if metadata.e2eEncrypted && !NCKeychain().isEndToEndEnabled(account: session.account) {
             showAlert(title: "_info_", description: "_e2e_goto_settings_for_enable_")
         }
 
-        if let fileNameError = FileNameValidator.shared.checkFileName(metadata.fileNameView) {
+        if let fileNameError = FileNameValidator.shared.checkFileName(metadata.fileNameView, account: session.account) {
             present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true)
             return
         }
@@ -49,11 +48,11 @@ extension NCShareExtension: UICollectionViewDelegate {
         if kind == UICollectionView.elementKindSectionHeader {
             guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() }
             if self.dataSourceTask?.state == .running {
-                header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.brandElement])
+                header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.getElement(account: session.account)])
                 header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
                 header.emptyDescription.text = ""
             } else {
-                header.emptyImage.image = NCImageCache.images.folder
+                header.emptyImage.image = NCImageCache.shared.getFolder(account: self.session.account)
                 header.emptyTitle.text = NSLocalizedString("_files_no_folders_", comment: "")
                 header.emptyDescription.text = ""
             }
@@ -67,30 +66,30 @@ extension NCShareExtension: UICollectionViewDelegate {
 extension NCShareExtension: UICollectionViewDelegateFlowLayout {
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
         var height: CGFloat = 0
-        if dataSource.getMetadataSourceForAllSections().isEmpty {
-            height = NCGlobal.shared.getHeightHeaderEmptyData(view: view, portraitOffset: 0, landscapeOffset: -50)
+        if self.dataSource.isEmpty() {
+            height = NCUtility().getHeightHeaderEmptyData(view: view, portraitOffset: 0, landscapeOffset: -50)
         }
         return CGSize(width: collectionView.frame.width, height: height)
     }
 }
 
 extension NCShareExtension: UICollectionViewDataSource {
-
     func numberOfSections(in collectionView: UICollectionView) -> Int {
         return dataSource.numberOfSections()
     }
 
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        return dataSource.numberOfItemsInSection(section)
+        return self.dataSource.numberOfItemsInSection(section)
     }
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath), let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else {
-            return UICollectionViewCell()
+        let cell = (collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell)!
+        guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else {
+            return cell
         }
 
-        cell.fileObjectId = metadata.ocId
-        cell.indexPath = indexPath
+        cell.fileOcId = metadata.ocId
+        cell.fileOcIdTransfer = metadata.ocIdTransfer
         cell.fileUser = metadata.ownerId
         cell.labelTitle.text = metadata.fileNameView
         cell.labelTitle.textColor = NCBrandColor.shared.textColor
@@ -102,14 +101,13 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.imageMore.image = nil
         cell.imageItem.image = nil
         cell.imageItem.backgroundColor = nil
-        cell.progressView.progress = 0.0
 
         if metadata.directory {
             setupDirectoryCell(cell, indexPath: indexPath, with: metadata)
         }
 
         if metadata.favorite {
-            cell.imageFavorite.image = NCImageCache.images.favorite
+            cell.imageFavorite.image = NCImageCache.shared.getImageFavorite()
         }
 
         cell.imageSelect.isHidden = true
@@ -119,7 +117,7 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.selected(false, isEditMode: false)
 
         if metadata.isLivePhoto {
-            cell.imageStatus.image = NCImageCache.images.livePhoto
+            cell.imageStatus.image = utility.loadImage(named: "livephoto", colors: [NCBrandColor.shared.iconImageColor2])
         }
 
         cell.setTags(tags: Array(metadata.tags))
@@ -139,31 +137,31 @@ extension NCShareExtension: UICollectionViewDataSource {
         }
 
         if metadata.e2eEncrypted {
-            cell.imageItem.image = NCImageCache.images.folderEncrypted
+            cell.imageItem.image = NCImageCache.shared.getFolderEncrypted(account: metadata.account)
         } else if isShare {
-            cell.imageItem.image = NCImageCache.images.folderSharedWithMe
+            cell.imageItem.image = NCImageCache.shared.getFolderSharedWithMe(account: metadata.account)
         } else if !metadata.shareType.isEmpty {
             metadata.shareType.contains(3) ?
-            (cell.imageItem.image = NCImageCache.images.folderPublic) :
-            (cell.imageItem.image = NCImageCache.images.folderSharedWithMe)
+            (cell.imageItem.image = NCImageCache.shared.getFolderPublic(account: metadata.account)) :
+            (cell.imageItem.image = NCImageCache.shared.getFolderSharedWithMe(account: metadata.account))
         } else if metadata.mountType == "group" {
-            cell.imageItem.image = NCImageCache.images.folderGroup
+            cell.imageItem.image = NCImageCache.shared.getFolderGroup(account: metadata.account)
         } else if isMounted {
-            cell.imageItem.image = NCImageCache.images.folderExternal
+            cell.imageItem.image = NCImageCache.shared.getFolderExternal(account: metadata.account)
         } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
-            cell.imageItem.image = NCImageCache.images.folderAutomaticUpload
+            cell.imageItem.image = NCImageCache.shared.getFolderAutomaticUpload(account: metadata.account)
         } else {
-            cell.imageItem.image = NCImageCache.images.folder
+            cell.imageItem.image = NCImageCache.shared.getFolder(account: metadata.account)
         }
 
         cell.labelInfo.text = utility.dateDiff(metadata.date as Date)
 
         let lockServerUrl = utilityFileSystem.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)
-        let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", activeAccount.account, lockServerUrl))
+        let tableDirectory = self.database.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, lockServerUrl))
 
         // Local image: offline
         if tableDirectory != nil && tableDirectory!.offline {
-            cell.imageLocal.image = NCImageCache.images.offlineFlag
+            cell.imageLocal.image = NCImageCache.shared.getImageOfflineFlag()
         }
     }
 }
@@ -171,7 +169,6 @@ extension NCShareExtension: UICollectionViewDataSource {
 // MARK: - Table View (uploading files)
 
 extension NCShareExtension: UITableViewDelegate {
-
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
         return heightRowTableView
     }
@@ -179,12 +176,11 @@ extension NCShareExtension: UITableViewDelegate {
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         guard !uploadStarted else { return }
         let fileName = filesName[indexPath.row]
-        renameFile(named: fileName)
+        renameFile(named: fileName, account: session.account)
     }
 }
 
 extension NCShareExtension: UITableViewDataSource {
-
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         filesName.count
     }
@@ -192,7 +188,7 @@ extension NCShareExtension: UITableViewDataSource {
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NCShareCell else { return UITableViewCell() }
         let fileName = filesName[indexPath.row]
-        cell.setup(fileName: fileName)
+        cell.setup(fileName: fileName, account: session.account)
         cell.delegate = self
         return cell
     }

+ 11 - 6
Share/NCShareExtension+Files.swift

@@ -22,14 +22,16 @@
 //
 
 import Foundation
+import UIKit
 import UniformTypeIdentifiers
 import NextcloudKit
 
 extension NCShareExtension {
     @objc func reloadDatasource(withLoadFolder: Bool) {
-        layoutForView = NCManageDatabase.shared.setLayoutForView(account: activeAccount.account, key: keyLayout, serverUrl: serverUrl)
-        let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
-        self.dataSource = NCDataSource(metadatas: metadatas, account: activeAccount.account, layoutForView: layoutForView)
+        let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", session.account, serverUrl)
+        let results = self.database.getResultsMetadatasPredicate(predicate, layoutForView: NCDBLayoutForView())
+
+        self.dataSource = NCCollectionViewDataSource(results: results)
 
         if withLoadFolder {
             loadFolder()
@@ -42,7 +44,7 @@ extension NCShareExtension {
     @objc func didCreateFolder(_ notification: NSNotification) {
         guard let userInfo = notification.userInfo as NSDictionary?,
               let ocId = userInfo["ocId"] as? String,
-              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId)
+              let metadata = self.database.getMetadataFromOcId(ocId)
         else { return }
 
         self.serverUrl += "/" + metadata.fileName
@@ -51,10 +53,13 @@ extension NCShareExtension {
     }
 
     func loadFolder() {
-        NCNetworking.shared.readFolder(serverUrl: serverUrl, account: activeAccount.account) { task in
+        NCNetworking.shared.readFolder(serverUrl: serverUrl,
+                                       account: session.account,
+                                       checkResponseDataChanged: false,
+                                       queue: .main) { task in
             self.dataSourceTask = task
             self.collectionView.reloadData()
-        } completion: { _, metadataFolder, _, _, _, error in
+        } completion: { _, metadataFolder, _, _, error in
             DispatchQueue.main.async {
                 if error != .success {
                     self.showAlert(description: error.errorDescription)

+ 35 - 47
Share/NCShareExtension+NCDelegate.swift → Share/NCShareExtension+NCAccountRequestDelegate.swift

@@ -29,16 +29,16 @@ extension NCShareExtension: NCAccountRequestDelegate {
     // MARK: - Account
 
     func showAccountPicker() {
-        let accounts = NCManageDatabase.shared.getAllAccountOrderAlias()
+        let accounts = self.database.getAllAccountOrderAlias()
         guard accounts.count > 1,
               let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest else { return }
 
         // Only here change the active account
         for account in accounts {
-            account.active = account.account == self.activeAccount.account
+            account.active = account.account == session.account
         }
 
-        vcAccountRequest.activeAccount = self.activeAccount
+        vcAccountRequest.activeAccount = self.session.account
         vcAccountRequest.accounts = accounts.sorted { sorg, dest -> Bool in
             return sorg.active && !dest.active
         }
@@ -57,49 +57,37 @@ extension NCShareExtension: NCAccountRequestDelegate {
 
     func accountRequestAddAccount() { }
 
-    func accountRequestChangeAccount(account: String) {
-        guard let activeAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) else {
+    func accountRequestChangeAccount(account: String, controller: UIViewController?) {
+        guard let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", account)),
+              let capabilities = self.database.setCapabilities(account: account) else {
             cancel(with: NCShareExtensionError.noAccount)
             return
         }
-        self.activeAccount = activeAccount
-
-        // CAPABILITIES
-        NCManageDatabase.shared.setCapabilities(account: account)
+        self.account = account
 
         // COLORS
-        NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
-        NCBrandColor.shared.createUserColors()
-        NCImageCache.shared.createImagesBrandCache()
+        NCBrandColor.shared.settingThemingColor(account: account)
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeTheming, userInfo: ["account": account])
 
         // NETWORKING
-        NextcloudKit.shared.setup(
-            account: activeAccount.account,
-            user: activeAccount.user,
-            userId: activeAccount.userId,
-            password: NCKeychain().getPassword(account: activeAccount.account),
-            urlBase: activeAccount.urlBase,
-            userAgent: userAgent,
-            nextcloudVersion: 0,
-            delegate: NCNetworking.shared)
+        NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+        NextcloudKit.shared.appendSession(account: tableAccount.account,
+                                          urlBase: tableAccount.urlBase,
+                                          user: tableAccount.user,
+                                          userId: tableAccount.userId,
+                                          password: NCKeychain().getPassword(account: tableAccount.account),
+                                          userAgent: userAgent,
+                                          nextcloudVersion: capabilities.capabilityServerVersionMajor,
+                                          groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
 
         // get auto upload folder
-        autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
-        autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: activeAccount.urlBase, userId: activeAccount.userId, account: activeAccount.account)
-
-        serverUrl = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId)
+        autoUploadFileName = self.database.getAccountAutoUploadFileName()
+        autoUploadDirectory = self.database.getAccountAutoUploadDirectory(session: session)
 
-        layoutForView = NCManageDatabase.shared.getLayoutForView(account: activeAccount.account, key: keyLayout, serverUrl: serverUrl)
+        serverUrl = utilityFileSystem.getHomeServer(session: session)
 
         reloadDatasource(withLoadFolder: true)
         setNavigationBar(navigationTitle: NCBrandOptions.shared.brand)
-
-        FileNameValidator.shared.setup(
-            forbiddenFileNames: NCGlobal.shared.capabilityForbiddenFileNames,
-            forbiddenFileNameBasenames: NCGlobal.shared.capabilityForbiddenFileNameBasenames,
-            forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters,
-            forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions
-        )
     }
 }
 
@@ -117,20 +105,8 @@ extension NCShareExtension: NCCreateFormUploadConflictDelegate {
 }
 
 extension NCShareExtension: NCShareCellDelegate {
-    func removeFile(named fileName: String) {
-        guard let index = self.filesName.firstIndex(of: fileName) else {
-            return showAlert(title: "_file_not_found_", description: fileName)
-        }
-        self.filesName.remove(at: index)
-        if self.filesName.isEmpty {
-            cancel(with: NCShareExtensionError.noFiles)
-        } else {
-            self.setCommandView()
-        }
-    }
-
-    func renameFile(named fileName: String) {
-        let alert = UIAlertController.renameFile(fileName: fileName) { [self] newFileName in
+    func renameFile(named fileName: String, account: String) {
+        let alert = UIAlertController.renameFile(fileName: fileName, account: account) { [self] newFileName in
             guard let fileIx = self.filesName.firstIndex(of: fileName),
                   !self.filesName.contains(newFileName),
                   utilityFileSystem.moveFile(atPath: (NSTemporaryDirectory() + fileName), toPath: (NSTemporaryDirectory() + newFileName)) else {
@@ -143,4 +119,16 @@ extension NCShareExtension: NCShareCellDelegate {
 
         present(alert, animated: true)
     }
+
+    func removeFile(named fileName: String) {
+        guard let index = self.filesName.firstIndex(of: fileName) else {
+            return showAlert(title: "_file_not_found_", description: fileName)
+        }
+        self.filesName.remove(at: index)
+        if self.filesName.isEmpty {
+            cancel(with: NCShareExtensionError.noFiles)
+        } else {
+            self.setCommandView()
+        }
+    }
 }

+ 56 - 67
Share/NCShareExtension.swift

@@ -25,15 +25,12 @@
 
 import UIKit
 import NextcloudKit
-import JGProgressHUD
-import TOPasscodeViewController
 
 enum NCShareExtensionError: Error {
     case cancel, fileUpload, noAccount, noFiles
 }
 
 class NCShareExtension: UIViewController {
-
     @IBOutlet weak var collectionView: UICollectionView!
     @IBOutlet weak var tableView: UITableView!
     @IBOutlet weak var cancelButton: UIBarButtonItem!
@@ -57,22 +54,33 @@ class NCShareExtension: UIViewController {
     let keyLayout = NCGlobal.shared.layoutViewShareExtension
     var metadataFolder: tableMetadata?
     var dataSourceTask: URLSessionTask?
-    var dataSource = NCDataSource()
-    var layoutForView: NCDBLayoutForView?
+    var dataSource = NCCollectionViewDataSource()
     let heightRowTableView: CGFloat = 50
     let heightCommandView: CGFloat = 170
     var autoUploadFileName = ""
     var autoUploadDirectory = ""
     let refreshControl = UIRefreshControl()
-    var activeAccount: tableAccount!
     var progress: CGFloat = 0
     var counterUploaded: Int = 0
     var uploadErrors: [tableMetadata] = []
     var uploadMetadata: [tableMetadata] = []
     var uploadStarted = false
-    let hud = JGProgressHUD()
+    let hud = NCHud()
     let utilityFileSystem = NCUtilityFileSystem()
     let utility = NCUtility()
+    let database = NCManageDatabase.shared
+    var account: String = ""
+    var session: NCSession.Session {
+        if !account.isEmpty,
+           let tableAccount = self.database.getTableAccount(account: account) {
+            return NCSession.Session(account: tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId)
+        } else if let activeTableAccount = self.database.getActiveTableAccount() {
+            self.account = activeTableAccount.account
+            return NCSession.Session(account: activeTableAccount.account, urlBase: activeTableAccount.urlBase, user: activeTableAccount.user, userId: activeTableAccount.userId)
+        } else {
+            return NCSession.Session(account: "", urlBase: "", user: "", userId: "")
+        }
+    }
 
     // MARK: - View Life Cycle
 
@@ -86,7 +94,7 @@ class NCShareExtension: UIViewController {
         collectionView.collectionViewLayout = NCListLayout()
 
         collectionView.refreshControl = refreshControl
-        refreshControl.tintColor = NCBrandColor.shared.brandText
+        refreshControl.tintColor = NCBrandColor.shared.iconImageColor
         refreshControl.backgroundColor = .systemBackground
         refreshControl.addTarget(self, action: #selector(reloadDatasource), for: .valueChanged)
 
@@ -125,33 +133,20 @@ class NCShareExtension: UIViewController {
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start Share session with level \(levelLog) " + versionNextcloudiOS)
         }
 
-        // Colors
-        if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
-            NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
-        }
         NCBrandColor.shared.createUserColors()
-        NCImageCache.shared.createImagesCache()
-        NCImageCache.shared.createImagesBrandCache()
-
-        hud.indicatorView = JGProgressHUDRingIndicatorView()
-        if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
-            indicatorView.ringWidth = 1.5
-            indicatorView.ringColor = NCBrandColor.shared.brandElement
-        }
 
         NotificationCenter.default.addObserver(self, selector: #selector(didCreateFolder(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil)
     }
 
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
-        guard serverUrl.isEmpty else { return }
-        guard let activeAccount = NCManageDatabase.shared.getActiveAccount(),
+        guard !session.account.isEmpty,
               !NCPasscode.shared.isPasscodeReset else {
             return showAlert(description: "_no_active_account_") {
                 self.cancel(with: .noAccount)
             }
         }
-        accountRequestChangeAccount(account: activeAccount.account)
+        accountRequestChangeAccount(account: account, controller: nil)
         guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else {
             cancel(with: .noFiles)
             return
@@ -197,7 +192,6 @@ class NCShareExtension: UIViewController {
     }
 
     func setNavigationBar(navigationTitle: String) {
-
         navigationItem.title = navigationTitle
         cancelButton.title = NSLocalizedString("_cancel_", comment: "")
 
@@ -212,28 +206,26 @@ class NCShareExtension: UIViewController {
             if !self.uploadStarted {
                 while self.serverUrl.last != "/" { self.serverUrl.removeLast() }
                 self.serverUrl.removeLast()
-
                 self.reloadDatasource(withLoadFolder: true)
-
                 var navigationTitle = (self.serverUrl as NSString).lastPathComponent
-                if self.utilityFileSystem.getHomeServer(urlBase: self.activeAccount.urlBase, userId: self.activeAccount.userId) == self.serverUrl {
+                if self.utilityFileSystem.getHomeServer(session: self.session) == self.serverUrl {
                     navigationTitle = NCBrandOptions.shared.brand
                 }
                 self.setNavigationBar(navigationTitle: navigationTitle)
             }
         }
 
-        let image = utility.loadUserImage(for: activeAccount.user, displayName: activeAccount.displayName, userBaseUrl: activeAccount)
+        let tableAccount = self.database.getTableAccount(account: session.account)
+        let image = utility.loadUserImage(for: session.user, displayName: tableAccount?.displayName, urlBase: session.urlBase)
         let profileButton = UIButton(type: .custom)
         profileButton.setImage(image, for: .normal)
 
-        if serverUrl == utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) {
-
+        if serverUrl == utilityFileSystem.getHomeServer(session: self.session) {
             var title = "  "
-            if let userAlias = activeAccount?.alias, !userAlias.isEmpty {
+            if let userAlias = tableAccount?.alias, !userAlias.isEmpty {
                 title += userAlias
             } else {
-                title += activeAccount?.displayName ?? ""
+                title += tableAccount?.displayName ?? ""
             }
 
             profileButton.setTitle(title, for: .normal)
@@ -248,7 +240,7 @@ class NCShareExtension: UIViewController {
             }
         }
         var navItems = [UIBarButtonItem(customView: profileButton)]
-        if serverUrl != utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) {
+        if serverUrl != utilityFileSystem.getHomeServer(session: self.session) {
             let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
             space.width = 20
             navItems.append(contentsOf: [UIBarButtonItem(customView: backButton), space])
@@ -278,7 +270,7 @@ class NCShareExtension: UIViewController {
     }
 
     @objc func actionCreateFolder() {
-        let alertController = UIAlertController.createFolder(serverUrl: serverUrl, userBaseUrl: activeAccount) { error in
+        let alertController = UIAlertController.createFolder(serverUrl: serverUrl, account: session.account) { error in
             guard error != .success else { return }
             self.showAlert(title: "_error_createsubfolders_upload_", description: error.errorDescription)
         }
@@ -298,7 +290,7 @@ extension NCShareExtension {
 
         var conflicts: [tableMetadata] = []
         for fileName in filesName {
-            if let fileNameError = FileNameValidator.shared.checkFileName(fileName) {
+            if let fileNameError = FileNameValidator.shared.checkFileName(fileName, account: session.account) {
                 present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true)
 
                 continue
@@ -307,27 +299,32 @@ extension NCShareExtension {
             let ocId = NSUUID().uuidString
             let toPath = utilityFileSystem.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)
             guard utilityFileSystem.copyFile(atPath: (NSTemporaryDirectory() + fileName), toPath: toPath) else { continue }
-            let metadata = NCManageDatabase.shared.createMetadata(
-                account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId,
-                fileName: fileName, fileNameView: fileName,
-                ocId: ocId,
-                serverUrl: serverUrl, urlBase: activeAccount.urlBase, url: "",
-                contentType: "")
-            metadata.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
-            metadata.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension
-            metadata.size = utilityFileSystem.getFileSize(filePath: toPath)
-            metadata.status = NCGlobal.shared.metadataStatusWaitUpload
-            metadata.sessionDate = Date()
-            if NCManageDatabase.shared.getMetadataConflict(account: activeAccount.account, serverUrl: serverUrl, fileNameView: fileName) != nil {
-                conflicts.append(metadata)
+            let metadataForUpload = self.database.createMetadata(fileName: fileName,
+                                                                 fileNameView: fileName,
+                                                                 ocId: ocId,
+                                                                 serverUrl: serverUrl,
+                                                                 url: "",
+                                                                 contentType: "",
+                                                                 session: session,
+                                                                 sceneIdentifier: nil)
+
+            metadataForUpload.session = NCNetworking.shared.sessionUpload
+            metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension
+            metadataForUpload.size = utilityFileSystem.getFileSize(filePath: toPath)
+            metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
+            metadataForUpload.sessionDate = Date()
+            if self.database.getMetadataConflict(account: session.account, serverUrl: serverUrl, fileNameView: fileName) != nil {
+                conflicts.append(metadataForUpload)
             } else {
-                uploadMetadata.append(metadata)
+                uploadMetadata.append(metadataForUpload)
             }
         }
 
         if !conflicts.isEmpty {
             guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict
             else { return }
+
+            conflict.account = session.account
             conflict.serverUrl = self.serverUrl
             conflict.metadatasUploadInConflict = conflicts
             conflict.delegate = self
@@ -341,8 +338,8 @@ extension NCShareExtension {
         guard uploadStarted else { return }
         guard uploadMetadata.count > counterUploaded else { return DispatchQueue.main.async { self.finishedUploading() } }
         let metadata = uploadMetadata[counterUploaded]
+        let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false, account: session.account)
 
-        let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false)
         metadata.contentType = results.mimeType
         metadata.iconName = results.iconName
         metadata.classFile = results.classFile
@@ -359,22 +356,16 @@ extension NCShareExtension {
         // E2EE
         metadata.e2eEncrypted = metadata.isDirectoryE2EE
 
-        DispatchQueue.main.async {
-            self.hud.show(in: self.view)
-            self.hud.textLabel.text = NSLocalizedString("_upload_file_", comment: "") + " \(self.counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(self.filesName.count)"
-        }
+        hud.initHudRing(view: self.view,
+                        text: NSLocalizedString("_upload_file_", comment: "") + " \(self.counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(self.filesName.count)")
 
-        NCNetworking.shared.upload(metadata: metadata, uploadE2EEDelegate: self, hudView: self.view, hud: JGProgressHUD()) {
-            DispatchQueue.main.async {
-                self.hud.progress = 0
-            }
+        NCNetworking.shared.upload(metadata: metadata, uploadE2EEDelegate: self, controller: self) {
+            self.hud.progress(0)
         } progressHandler: { _, _, fractionCompleted in
-            DispatchQueue.main.async {
-                self.hud.progress = Float(fractionCompleted)
-            }
+            self.hud.progress(fractionCompleted)
         } completion: { _, error in
             if error != .success {
-                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                self.database.deleteMetadataOcId(metadata.ocId)
                 self.utilityFileSystem.removeFile(atPath: self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId))
                 self.uploadErrors.append(metadata)
             }
@@ -391,9 +382,7 @@ extension NCShareExtension {
                 self.extensionContext?.cancelRequest(withError: NCShareExtensionError.fileUpload)
             }
         } else {
-            hud.indicatorView = JGProgressHUDSuccessIndicatorView()
-            hud.indicatorView?.tintColor = NCBrandColor.shared.brandElement
-            hud.textLabel.text = NSLocalizedString("_success_", comment: "")
+            hud.success()
             DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                 self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
             }
@@ -403,11 +392,11 @@ extension NCShareExtension {
 
 extension NCShareExtension: uploadE2EEDelegate {
     func start() {
-        self.hud.progress = 0
+        self.hud.progress(0)
     }
 
     func uploadE2EEProgress(_ totalBytesExpected: Int64, _ totalBytes: Int64, _ fractionCompleted: Double) {
-        self.hud.progress = Float(fractionCompleted)
+        self.hud.progress(fractionCompleted)
     }
 }
 

+ 1 - 0
Share/Share-Bridging-Header.h

@@ -4,3 +4,4 @@
 
 #import "NCEndToEndEncryption.h"
 #import "UIImage+animatedGIF.h"
+#import "TOPasscodeViewController.h"

+ 1 - 0
Tests/Common/BaseXCTestCase.swift

@@ -8,6 +8,7 @@
 
 import XCTest
 import Foundation
+import UIKit
 import Alamofire
 import NextcloudKit
 @testable import Nextcloud

+ 1 - 0
Tests/Common/TestConstants.swift

@@ -20,6 +20,7 @@
 //
 
 import Foundation
+import UIKit
 
 public class TestConstants {
     static let timeoutLong: Double = 600

+ 7 - 5
Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift

@@ -24,10 +24,9 @@ import NextcloudKit
 @testable import Nextcloud
 
 final class FilesIntegrationTests: BaseIntegrationXCTestCase {
-    private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
 
     override func setUp() {
-        appDelegate.deleteAllAccounts()
+        NCAccount().deleteAllAccounts()
     }
 
     func test_createReadDeleteFolder_withProperParams_shouldCreateReadDeleteFolder() throws {
@@ -36,11 +35,14 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
         let folderName = "TestFolder\(randomInt)"
         let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)"
         let serverUrlFileName = "\(serverUrl)/\(folderName)"
+        let domain = NCDomain.Domain(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, sceneIdentifier: "")
 
-        NextcloudKit.shared.setup(account: TestConstants.account, user: TestConstants.username, userId: TestConstants.username, password: appToken, urlBase: TestConstants.server, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
+        NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+        NextcloudKit.shared.appendAccount(TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, password: appToken, userAgent: userAgent, nextcloudVersion: 0, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
 
         // Test creating folder
-        NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, account: TestConstants.account, urlBase: TestConstants.server, userId: TestConstants.username, withPush: true, sceneIdentifier: nil) { error in
+        NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, overwrite: true, withPush: true, sceneIdentifier: nil, domain: domain) { error in
+
             XCTAssertEqual(NKError.success.errorCode, error.errorCode)
             XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
 
@@ -61,7 +63,7 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
 
                 Task {
                     // Test deleting folder
-                    await _ = NCNetworking.shared.deleteMetadata(metadataFolder!, onlyLocalCache: false)
+                    await _ = NCNetworking.shared.deleteMetadata(metadataFolder!)
 
                     XCTAssertEqual(NKError.success.errorCode, error.errorCode)
                     XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)

+ 31 - 30
Widget/Dashboard/DashboardData.swift

@@ -39,6 +39,7 @@ struct DashboardDataEntry: TimelineEntry {
     let title: String
     let footerImage: String
     let footerText: String
+    let account: String
 }
 
 struct DashboardData: Identifiable, Hashable {
@@ -101,45 +102,44 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
     let utility = NCUtility()
     let dashboardItems = getDashboardItems(displaySize: displaySize, withButton: false)
     let datasPlaceholder = Array(dashboardDatasTest[0...dashboardItems - 1])
-    var account: tableAccount?
+    var activeTableAccount: tableAccount?
 
     if isPreview {
-        return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, isEmpty: false, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " dashboard"))
+        return completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, isEmpty: false, titleImage: UIImage(named: "widget")!, title: "Dashboard", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " dashboard", account: ""))
     }
 
     let accountIdentifier: String = configuration?.accounts?.identifier ?? "active"
     if accountIdentifier == "active" {
-        account = NCManageDatabase.shared.getActiveAccount()
+        activeTableAccount = NCManageDatabase.shared.getActiveTableAccount()
     } else {
-        account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
+        activeTableAccount = NCManageDatabase.shared.getTableAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
     }
 
-    guard let account = account 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("_no_active_account_", comment: "")))
+    guard let activeTableAccount,
+          let capabilities = NCManageDatabase.shared.setCapabilities(account: activeTableAccount.account) 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("_no_active_account_", comment: ""), account: ""))
     }
 
     // Default widget
-    let result = NCManageDatabase.shared.getDashboardWidgetApplications(account: account.account).first
+    let result = NCManageDatabase.shared.getDashboardWidgetApplications(account: activeTableAccount.account).first
     let id: String = configuration?.applications?.identifier ?? (result?.id ?? "recommendations")
 
-    // Capabilities
-    NCManageDatabase.shared.setCapabilities(account: account.account)
-
-    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: "")))
+    guard capabilities.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: ""), account: activeTableAccount.account))
     }
 
     // NETWORKING
-    let password = NCKeychain().getPassword(account: account.account)
-    NextcloudKit.shared.setup(
-        account: account.account,
-        user: account.user,
-        userId: account.userId,
-        password: password,
-        urlBase: account.urlBase,
-        userAgent: userAgent,
-        nextcloudVersion: 0,
-        delegate: NCNetworking.shared)
+    let password = NCKeychain().getPassword(account: activeTableAccount.account)
+
+    NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+    NextcloudKit.shared.appendSession(account: activeTableAccount.account,
+                                      urlBase: activeTableAccount.urlBase,
+                                      user: activeTableAccount.user,
+                                      userId: activeTableAccount.userId,
+                                      password: password,
+                                      userAgent: userAgent,
+                                      nextcloudVersion: capabilities.capabilityServerVersionMajor,
+                                      groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
 
     // LOG
     let levelLog = NCKeychain().logLevel
@@ -154,7 +154,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
         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 (tableDashboard, tableButton) = NCManageDatabase.shared.getDashboardWidget(account: activeTableAccount.account, id: id)
     let existsButton = (tableButton?.isEmpty ?? true) ? false : true
     let title = tableDashboard?.title ?? id
 
@@ -168,7 +168,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
     let titleImage = imagetmp
 
     let options = NKRequestOptions(timeout: 90, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
-    NextcloudKit.shared.getDashboardWidgetsApplication(id, account: account.account, options: options) { _, results, data, error in
+    NextcloudKit.shared.getDashboardWidgetsApplication(id, account: activeTableAccount.account, options: options) { account, results, responseData, error in
         Task {
             var datas = [DashboardData]()
             var numberItems = 0
@@ -225,9 +225,10 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
                                     if FileManager().fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
                                         icon = image
                                     } else {
-                                        let (_, data, error) = await NCNetworking.shared.downloadPreview(url: url, account: account.account)
+                                        let (_, data, error) = await NCNetworking.shared.downloadPreview(url: url, account: activeTableAccount.account)
                                         if error == .success,
-                                           let image = convertDataToImage(data: data, size: CGSize(width: 256, height: 256), fileNameToWrite: fileName) {
+                                           let data = responseData?.data,
+                                           let image = convertDataToImage(data: data, size: NCGlobal.shared.size256, fileNameToWrite: fileName) {
                                             icon = image
                                         }
                                     }
@@ -248,13 +249,13 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
                 buttons = tableButton.filter(({ $0.type != "more" }))
             }
 
-            let alias = (account.alias.isEmpty) ? "" : (" (" + account.alias + ")")
-            let footerText = "Dashboard " + NSLocalizedString("_of_", comment: "") + " " + account.displayName + alias
+            let alias = (activeTableAccount.alias.isEmpty) ? "" : (" (" + activeTableAccount.alias + ")")
+            let footerText = "Dashboard " + NSLocalizedString("_of_", comment: "") + " " + activeTableAccount.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))
+                completion(DashboardDataEntry(date: Date(), datas: datasPlaceholder, dashboard: tableDashboard, buttons: buttons, isPlaceholder: true, isEmpty: false, titleImage: titleImage, title: title, footerImage: "xmark.icloud", footerText: error.errorDescription, account: account))
             } else {
-                completion(DashboardDataEntry(date: Date(), datas: datas, dashboard: tableDashboard, buttons: buttons, isPlaceholder: false, isEmpty: datas.isEmpty, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: footerText))
+                completion(DashboardDataEntry(date: Date(), datas: datas, dashboard: tableDashboard, buttons: buttons, isPlaceholder: false, isEmpty: datas.isEmpty, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: footerText, account: account))
             }
         }
     }

+ 1 - 1
Widget/Dashboard/DashboardWidgetProvider.swift

@@ -35,7 +35,7 @@ struct DashboardWidgetProvider: IntentTimelineProvider {
         let datasPlaceholder = Array(dashboardDatasTest[0...dashboardItems])
         let title = "Dashboard"
         let titleImage = UIImage(named: "widget")!
-        return Entry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, isEmpty: false, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " widget")
+        return Entry(date: Date(), datas: datasPlaceholder, dashboard: nil, buttons: nil, isPlaceholder: true, isEmpty: false, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " widget", account: "")
     }
 
     func getSnapshot(for configuration: DashboardIntent, in context: Context, completion: @escaping (DashboardDataEntry) -> Void) {

+ 5 - 10
Widget/Dashboard/DashboardWidgetView.swift

@@ -25,13 +25,9 @@ import SwiftUI
 import WidgetKit
 
 struct DashboardWidgetView: View {
-
     var entry: DashboardDataEntry
-
     var body: some View {
-
         GeometryReader { geo in
-
             if entry.isEmpty {
                 VStack(alignment: .center) {
                     Image(systemName: "checkmark")
@@ -156,9 +152,8 @@ struct DashboardWidgetView: View {
                 if let buttons = entry.buttons, !buttons.isEmpty, !entry.isPlaceholder {
 
                     HStack(spacing: 10) {
-
-                        let brandColor = Color(NCBrandColor.shared.brandElement)
-                        let brandTextColor = Color(NCBrandColor.shared.brandText)
+                        let brandColor = Color(NCBrandColor.shared.getElement(account: entry.account))
+                        let brandTextColor = Color(NCBrandColor.shared.getText(account: entry.account))
 
                         ForEach(buttons, id: \.index) { element in
                             Link(destination: URL(string: element.link)!, label: {
@@ -183,12 +178,12 @@ struct DashboardWidgetView: View {
                         .scaledToFit()
                         .frame(width: 15, height: 15)
                         .font(Font.system(.body).weight(.light))
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
 
                     Text(entry.footerText)
                         .font(.caption2)
                         .lineLimit(1)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                 }
                 .padding(.horizontal, 15.0)
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
@@ -203,7 +198,7 @@ struct DashboardWidget_Previews: PreviewProvider {
         let datas = Array(dashboardDatasTest[0...4])
         let title = "Dashboard"
         let titleImage = UIImage(named: "widget")!
-        let entry = DashboardDataEntry(date: Date(), datas: datas, dashboard: nil, buttons: nil, isPlaceholder: false, isEmpty: true, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: "Nextcloud widget")
+        let entry = DashboardDataEntry(date: Date(), datas: datas, dashboard: nil, buttons: nil, isPlaceholder: false, isEmpty: true, titleImage: titleImage, title: title, footerImage: "checkmark.icloud", footerText: "Nextcloud widget", account: "")
         DashboardWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .systemLarge))
     }
 }

+ 38 - 36
Widget/Files/FilesData.swift

@@ -33,6 +33,7 @@ struct FilesDataEntry: TimelineEntry {
     let isEmpty: Bool
     let userId: String
     let url: String
+    let account: String
     let tile: String
     let footerImage: String
     let footerText: String
@@ -60,7 +61,7 @@ let filesDatasTest: [FilesData] = [
     .init(id: "9", image: UIImage(named: "widget")!, title: "title4", subTitle: "subTitle-description4", url: URL(string: "https://nextcloud.com/")!)
 ]
 
-func getTitleFilesWidget(account: tableAccount?) -> String {
+func getTitleFilesWidget(tableAccount: tableAccount?) -> String {
     let hour = Calendar.current.component(.hour, from: Date())
     var good = ""
 
@@ -72,8 +73,8 @@ func getTitleFilesWidget(account: tableAccount?) -> String {
     default: good = NSLocalizedString("_good_night_", value: "Good night", comment: "")
     }
 
-    if let account = account {
-        return good + ", " + account.displayName
+    if let tableAccount {
+        return good + ", " + tableAccount.displayName
     } else {
         return good
     }
@@ -89,34 +90,35 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
     let utility = NCUtility()
     let filesItems = getFilesItems(displaySize: displaySize)
     let datasPlaceholder = Array(filesDatasTest[0...filesItems - 1])
-    var account: tableAccount?
+    var activeTableAccount: tableAccount?
 
     if isPreview {
-        return completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: "", url: "", tile: getTitleFilesWidget(account: nil), footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files"))
+        return completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: "", url: "", account: "", tile: getTitleFilesWidget(tableAccount: nil), footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files"))
     }
 
     let accountIdentifier: String = configuration?.accounts?.identifier ?? "active"
     if accountIdentifier == "active" {
-        account = NCManageDatabase.shared.getActiveAccount()
+        activeTableAccount = NCManageDatabase.shared.getActiveTableAccount()
     } else {
-        account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
+        activeTableAccount = NCManageDatabase.shared.getTableAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
     }
 
-    guard let account = account else {
-        return completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: "", url: "", tile: getTitleFilesWidget(account: nil), footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", value: "No account found", comment: "")))
+    guard let activeTableAccount else {
+        return completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: "", url: "", account: "", tile: getTitleFilesWidget(tableAccount: nil), footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", value: "No account found", comment: "")))
     }
 
     // NETWORKING
-    let password = NCKeychain().getPassword(account: account.account)
-    NextcloudKit.shared.setup(
-        account: account.account,
-        user: account.user,
-        userId: account.userId,
-        password: password,
-        urlBase: account.urlBase,
-        userAgent: userAgent,
-        nextcloudVersion: 0,
-        delegate: NCNetworking.shared)
+    let password = NCKeychain().getPassword(account: activeTableAccount.account)
+
+    NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+    NextcloudKit.shared.appendSession(account: activeTableAccount.account,
+                                      urlBase: activeTableAccount.urlBase,
+                                      user: activeTableAccount.user,
+                                      userId: activeTableAccount.userId,
+                                      password: password,
+                                      userAgent: userAgent,
+                                      nextcloudVersion: NCCapabilities.shared.getCapabilities(account: activeTableAccount.account).capabilityServerVersionMajor,
+                                      groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
 
     let requestBodyRecent =
     """
@@ -173,7 +175,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
     dateFormatter.locale = Locale(identifier: "en_US_POSIX")
     dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
     let lessDateString = dateFormatter.string(from: Date())
-    let requestBody = String(format: requestBodyRecent, "/files/" + account.userId, lessDateString)
+    let requestBody = String(format: requestBodyRecent, "/files/" + activeTableAccount.userId, lessDateString)
 
     // LOG
     let levelLog = NCKeychain().logLevel
@@ -190,11 +192,11 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
     }
 
     let options = NKRequestOptions(timeout: 30, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
-    NextcloudKit.shared.searchBodyRequest(serverUrl: account.urlBase, requestBody: requestBody, showHiddenFiles: NCKeychain().showHiddenFiles, account: account.account, options: options) { _, files, data, error in
+    NextcloudKit.shared.searchBodyRequest(serverUrl: activeTableAccount.urlBase, requestBody: requestBody, showHiddenFiles: NCKeychain().showHiddenFiles, account: activeTableAccount.account, options: options) { _, files, data, error in
         Task {
             var datas: [FilesData] = []
-            let title = getTitleFilesWidget(account: account)
-            let files = files.sorted(by: { ($0.date as Date) > ($1.date as Date) })
+            let title = getTitleFilesWidget(tableAccount: activeTableAccount)
+            let files = files?.sorted(by: { ($0.date as Date) > ($1.date as Date) }) ?? []
 
             for file in files {
                 var useTypeIconFile = false
@@ -216,18 +218,18 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 guard let url = URL(string: urlString) else { continue }
 
                 // IMAGE
-                let fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)
-                let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)
-                if FileManager.default.fileExists(atPath: fileNameIconLocalPath) {
-                    image = UIImage(contentsOfFile: fileNameIconLocalPath)
-                }
+                image = utility.getImage(ocId: file.ocId, etag: file.etag, ext: NCGlobal.shared.previewExt512)
                 if image == nil, file.hasPreview {
-                    let sizePreview = NCUtility().getSizePreview(width: Int(file.width), height: Int(file.height))
-                    let (_, _, imageIcon, _, _, _) = await NCNetworking.shared.downloadPreview(fileId: file.fileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, fileNameIconLocalPath: fileNameIconLocalPath, widthPreview: Int(sizePreview.width), heightPreview: Int(sizePreview.height), sizeIcon: NCGlobal.shared.sizeIcon, account: account.account, options: options)
-                    image = imageIcon
+                    let result = await NCNetworking.shared.downloadPreview(fileId: file.fileId,
+                                                                           account: activeTableAccount.account,
+                                                                           options: options)
+                    if result.error == .success, let data = result.responseData?.data {
+                        utility.createImageFileFrom(data: data, ocId: file.ocId, etag: file.etag)
+                        image = utility.getImage(ocId: file.ocId, etag: file.etag, ext: NCGlobal.shared.previewExt256)
+                    }
                 }
                 if image == nil {
-                    image = utility.loadImage(named: file.iconName, useTypeIconFile: true)
+                    image = utility.loadImage(named: file.iconName, useTypeIconFile: true, account: file.account)
                     useTypeIconFile = true
                 }
 
@@ -240,13 +242,13 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 if datas.count == filesItems { break}
             }
 
-            let alias = (account.alias.isEmpty) ? "" : (" (" + account.alias + ")")
-            let footerText = "Files " + NSLocalizedString("_of_", comment: "") + " " + account.displayName + alias
+            let alias = (activeTableAccount.alias.isEmpty) ? "" : (" (" + activeTableAccount.alias + ")")
+            let footerText = "Files " + NSLocalizedString("_of_", comment: "") + " " + activeTableAccount.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))
+                completion(FilesDataEntry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: activeTableAccount.userId, url: activeTableAccount.urlBase, account: activeTableAccount.account, tile: title, footerImage: "xmark.icloud", footerText: error.errorDescription))
             } else {
-                completion(FilesDataEntry(date: Date(), datas: datas, isPlaceholder: false, isEmpty: datas.isEmpty, userId: account.userId, url: account.urlBase, tile: title, footerImage: "checkmark.icloud", footerText: footerText))
+                completion(FilesDataEntry(date: Date(), datas: datas, isPlaceholder: false, isEmpty: datas.isEmpty, userId: activeTableAccount.userId, url: activeTableAccount.urlBase, account: activeTableAccount.account, tile: title, footerImage: "checkmark.icloud", footerText: footerText))
             }
         }
     }

+ 2 - 2
Widget/Files/FilesWidgetProvider.swift

@@ -33,8 +33,8 @@ struct FilesWidgetProvider: IntentTimelineProvider {
     func placeholder(in context: Context) -> Entry {
         let filesItems = getFilesItems(displaySize: context.displaySize)
         let datasPlaceholder = Array(filesDatasTest[0...filesItems - 1])
-        let title = getTitleFilesWidget(account: nil)
-        return Entry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: "", url: "", tile: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files")
+        let title = getTitleFilesWidget(tableAccount: nil)
+        return Entry(date: Date(), datas: datasPlaceholder, isPlaceholder: true, isEmpty: false, userId: "", url: "", account: "", tile: title, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " files")
     }
 
     func getSnapshot(for configuration: AccountIntent, in context: Context, completion: @escaping (Entry) -> Void) {

+ 11 - 11
Widget/Files/FilesWidgetView.swift

@@ -120,9 +120,9 @@ struct FilesWidgetView: View {
                         Image("addImage")
                             .resizable()
                             .renderingMode(.template)
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -132,9 +132,9 @@ struct FilesWidgetView: View {
                         Image(systemName: "doc.text.viewfinder")
                             .resizable()
                             .renderingMode(.template)
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .font(Font.system(.body).weight(.light))
@@ -145,9 +145,9 @@ struct FilesWidgetView: View {
                         Image("note.text")
                             .resizable()
                             .renderingMode(.template)
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -157,9 +157,9 @@ struct FilesWidgetView: View {
                         Image("microphone")
                             .resizable()
                             .renderingMode(.template)
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -174,12 +174,12 @@ struct FilesWidgetView: View {
                         .scaledToFit()
                         .frame(width: 15, height: 15)
                         .font(Font.system(.body).weight(.light))
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
 
                     Text(entry.footerText)
                         .font(.caption2)
                         .lineLimit(1)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                 }
                 .padding(.horizontal, 15.0)
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
@@ -192,7 +192,7 @@ struct FilesWidgetView: View {
 struct FilesWidget_Previews: PreviewProvider {
     static var previews: some View {
         let datas = Array(filesDatasTest[0...4])
-        let entry = FilesDataEntry(date: Date(), datas: datas, isPlaceholder: false, isEmpty: true, userId: "", url: "", tile: "Good afternoon, Marino Faggiana", footerImage: "checkmark.icloud", footerText: "Nextcloud files")
+        let entry = FilesDataEntry(date: Date(), datas: datas, isPlaceholder: false, isEmpty: true, userId: "", url: "", account: "", tile: "Good afternoon, Marino Faggiana", footerImage: "checkmark.icloud", footerText: "Nextcloud files")
         FilesWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .systemLarge))
     }
 }

+ 19 - 20
Widget/Lockscreen/LockscreenData.swift

@@ -38,7 +38,7 @@ struct LockscreenData: TimelineEntry {
 
 func getLockscreenDataEntry(configuration: AccountIntent?, isPreview: Bool, family: WidgetFamily, completion: @escaping (_ entry: LockscreenData) -> Void) {
     let utilityFileSystem = NCUtilityFileSystem()
-    var account: tableAccount?
+    var activeTableAccount: tableAccount?
     var quotaRelative: Float = 0
 
     if isPreview {
@@ -47,38 +47,37 @@ func getLockscreenDataEntry(configuration: AccountIntent?, isPreview: Bool, fami
 
     let accountIdentifier: String = configuration?.accounts?.identifier ?? "active"
     if accountIdentifier == "active" {
-        account = NCManageDatabase.shared.getActiveAccount()
+        activeTableAccount = NCManageDatabase.shared.getActiveTableAccount()
     } else {
-        account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
+        activeTableAccount = NCManageDatabase.shared.getTableAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
     }
 
-    guard let account else {
+    guard let activeTableAccount,
+          let capabilities = NCManageDatabase.shared.setCapabilities(account: activeTableAccount.account) else {
         return completion(LockscreenData(date: Date(), isPlaceholder: true, activity: "", link: URL(string: "https://")!, quotaRelative: 0, quotaUsed: "", quotaTotal: "", error: false))
     }
 
-    // Capabilities
-    NCManageDatabase.shared.setCapabilities(account: account.account)
-
-    if NCGlobal.shared.capabilityServerVersionMajor < NCGlobal.shared.nextcloudVersion25 {
+    if capabilities.capabilityServerVersionMajor < NCGlobal.shared.nextcloudVersion25 {
         completion(LockscreenData(date: Date(), isPlaceholder: false, activity: NSLocalizedString("_widget_available_nc25_", comment: ""), link: URL(string: "https://")!, quotaRelative: 0, quotaUsed: "", quotaTotal: "", error: true))
     }
 
     // NETWORKING
-    let password = NCKeychain().getPassword(account: account.account)
-    NextcloudKit.shared.setup(
-        account: account.account,
-        user: account.user,
-        userId: account.userId,
-        password: password,
-        urlBase: account.urlBase,
-        userAgent: userAgent,
-        nextcloudVersion: 0,
-        delegate: NCNetworking.shared)
+    let password = NCKeychain().getPassword(account: activeTableAccount.account)
+
+    NextcloudKit.shared.setup(delegate: NCNetworking.shared)
+    NextcloudKit.shared.appendSession(account: activeTableAccount.account,
+                                      urlBase: activeTableAccount.urlBase,
+                                      user: activeTableAccount.user,
+                                      userId: activeTableAccount.userId,
+                                      password: password,
+                                      userAgent: userAgent,
+                                      nextcloudVersion: capabilities.capabilityServerVersionMajor,
+                                      groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
 
     let options = NKRequestOptions(timeout: 90, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
     if #available(iOSApplicationExtension 16.0, *) {
         if family == .accessoryCircular {
-            NextcloudKit.shared.getUserProfile(account: account.account, options: options) { _, userProfile, _, error in
+            NextcloudKit.shared.getUserProfile(account: activeTableAccount.account, options: options) { _, userProfile, _, error in
                 if error == .success, let userProfile = userProfile {
                     if userProfile.quotaRelative > 0 {
                         quotaRelative = Float(userProfile.quotaRelative) / 100
@@ -102,7 +101,7 @@ func getLockscreenDataEntry(configuration: AccountIntent?, isPreview: Bool, fami
                 }
             }
         } else if family == .accessoryRectangular {
-            NextcloudKit.shared.getDashboardWidgetsApplication("activity", account: account.account, options: options) { _, results, _, error in
+            NextcloudKit.shared.getDashboardWidgetsApplication("activity", account: activeTableAccount.account, options: options) { _, results, _, error in
                 var activity: String = NSLocalizedString("_no_data_available_", comment: "")
                 var link = URL(string: "https://")!
                 if error == .success, let result = results?.first {

+ 10 - 7
Widget/Toolbar/ToolbarData.swift

@@ -29,6 +29,7 @@ struct ToolbarDataEntry: TimelineEntry {
     let isPlaceholder: Bool
     let userId: String
     let url: String
+    let account: String
     let footerImage: String
     let footerText: String
 }
@@ -36,19 +37,21 @@ struct ToolbarDataEntry: TimelineEntry {
 func getToolbarDataEntry(isPreview: Bool, completion: @escaping (_ entry: ToolbarDataEntry) -> Void) {
     var userId = ""
     var url = ""
+    var account = ""
 
-    if let account = NCManageDatabase.shared.getActiveAccount() {
-        userId = account.userId
-        url = account.urlBase
+    if let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount() {
+        userId = activeTableAccount.userId
+        url = activeTableAccount.urlBase
+        account = activeTableAccount.account
     }
 
     if isPreview {
-        return completion(ToolbarDataEntry(date: Date(), isPlaceholder: true, userId: userId, url: url, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar"))
+        return completion(ToolbarDataEntry(date: Date(), isPlaceholder: true, userId: userId, url: url, account: account, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar"))
     }
 
-    if NCManageDatabase.shared.getActiveAccount() == nil {
-        return completion(ToolbarDataEntry(date: Date(), isPlaceholder: true, userId: userId, url: url, footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", value: "No account found", comment: "")))
+    if NCManageDatabase.shared.getActiveTableAccount() == nil {
+        return completion(ToolbarDataEntry(date: Date(), isPlaceholder: true, userId: userId, url: url, account: account, footerImage: "xmark.icloud", footerText: NSLocalizedString("_no_active_account_", value: "No account found", comment: "")))
     }
 
-    completion(ToolbarDataEntry(date: Date(), isPlaceholder: false, userId: userId, url: url, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar"))
+    completion(ToolbarDataEntry(date: Date(), isPlaceholder: false, userId: userId, url: url, account: account, footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar"))
 }

+ 1 - 1
Widget/Toolbar/ToolbarWidgetProvider.swift

@@ -29,7 +29,7 @@ struct ToolbarWidgetProvider: TimelineProvider {
     typealias Entry = ToolbarDataEntry
 
     func placeholder(in context: Context) -> Entry {
-        return Entry(date: Date(), isPlaceholder: true, userId: "", url: "", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar")
+        return Entry(date: Date(), isPlaceholder: true, userId: "", url: "", account: "", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar")
     }
 
     func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {

+ 11 - 11
Widget/Toolbar/ToolbarWidgetView.swift

@@ -49,9 +49,9 @@ struct ToolbarWidgetView: View {
                         Image("addImage")
                             .resizable()
                             .renderingMode(.template)
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -62,9 +62,9 @@ struct ToolbarWidgetView: View {
                             .resizable()
                             .renderingMode(.template)
                             .font(Font.system(.body).weight(.light))
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -74,9 +74,9 @@ struct ToolbarWidgetView: View {
                         Image("note.text")
                             .resizable()
                             .renderingMode(.template)
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -85,9 +85,9 @@ struct ToolbarWidgetView: View {
                     Link(destination: entry.isPlaceholder ? linkNoAction : linkActionVoiceMemo, label: {
                         Image("microphone")
                             .resizable()
-                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
+                            .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getText(account: entry.account)))
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                             .clipShape(Circle())
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -102,12 +102,12 @@ struct ToolbarWidgetView: View {
                         .font(Font.system(.body).weight(.light))
                         .scaledToFit()
                         .frame(width: 15, height: 15)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
 
                     Text(entry.footerText)
                         .font(.caption2)
                         .padding(.trailing, 13.0)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.getElement(account: entry.account)))
                 }
                 .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
             }
@@ -118,7 +118,7 @@ struct ToolbarWidgetView: View {
 
 struct ToolbarWidget_Previews: PreviewProvider {
     static var previews: some View {
-        let entry = ToolbarDataEntry(date: Date(), isPlaceholder: false, userId: "", url: "", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar")
+        let entry = ToolbarDataEntry(date: Date(), isPlaceholder: false, userId: "", url: "", account: "", footerImage: "checkmark.icloud", footerText: NCBrandOptions.shared.brand + " toolbar")
         ToolbarWidgetView(entry: entry).previewContext(WidgetPreviewContext(family: .systemMedium))
     }
 }

+ 10 - 10
WidgetDashboardIntentHandler/IntentHandler.swift

@@ -30,7 +30,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
 
     func provideAccountsOptionsCollection(for intent: AccountIntent, with completion: @escaping (INObjectCollection<Accounts>?, Error?) -> Void) {
         var accounts: [Accounts] = []
-        let results = NCManageDatabase.shared.getAllAccount()
+        let results = NCManageDatabase.shared.getAllTableAccount()
 
         accounts.append(Accounts(identifier: "active", display: "Active account"))
 
@@ -49,7 +49,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     func defaultAccounts(for intent: AccountIntent) -> Accounts? {
-        if NCManageDatabase.shared.getActiveAccount() == nil {
+        if NCManageDatabase.shared.getActiveTableAccount() == nil {
             return nil
         } else {
             return Accounts(identifier: "active", display: "Active account")
@@ -61,20 +61,20 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     // Application
     func provideApplicationsOptionsCollection(for intent: DashboardIntent, with completion: @escaping (INObjectCollection<Applications>?, Error?) -> Void) {
         var applications: [Applications] = []
-        var account: tableAccount?
+        var activeTableAccount: tableAccount?
 
         let accountIdentifier: String = intent.accounts?.identifier ?? "active"
         if accountIdentifier == "active" {
-            account = NCManageDatabase.shared.getActiveAccount()
+            activeTableAccount = NCManageDatabase.shared.getActiveTableAccount()
         } else {
-            account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
+            activeTableAccount = NCManageDatabase.shared.getTableAccount(predicate: NSPredicate(format: "account == %@", accountIdentifier))
         }
 
-        guard let account = account else {
+        guard let activeTableAccount else {
             return completion(nil, nil)
         }
 
-        let results = NCManageDatabase.shared.getDashboardWidgetApplications(account: account.account)
+        let results = NCManageDatabase.shared.getDashboardWidgetApplications(account: activeTableAccount.account)
         for result in results {
             let application = Applications(identifier: result.id, display: result.title)
             applications.append(application)
@@ -84,7 +84,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     func defaultApplications(for intent: DashboardIntent) -> Applications? {
-        guard let account = NCManageDatabase.shared.getActiveAccount() else {
+        guard let account = NCManageDatabase.shared.getActiveTableAccount() else {
             return nil
         }
         if let result = NCManageDatabase.shared.getDashboardWidgetApplications(account: account.account).first {
@@ -96,7 +96,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     // Account
     func provideAccountsOptionsCollection(for intent: DashboardIntent, with completion: @escaping (INObjectCollection<Accounts>?, Error?) -> Void) {
         var accounts: [Accounts] = []
-        let results = NCManageDatabase.shared.getAllAccount()
+        let results = NCManageDatabase.shared.getAllTableAccount()
 
         accounts.append(Accounts(identifier: "active", display: "Active account"))
 
@@ -115,7 +115,7 @@ class IntentHandler: INExtension, DashboardIntentHandling, AccountIntentHandling
     }
 
     func defaultAccounts(for intent: DashboardIntent) -> Accounts? {
-        if NCManageDatabase.shared.getActiveAccount() == nil {
+        if NCManageDatabase.shared.getActiveTableAccount() == nil {
             return nil
         } else {
             return Accounts(identifier: "active", display: "Active account")

+ 6 - 8
iOSClient/Account Request/NCAccountRequest.swift

@@ -26,7 +26,7 @@ import NextcloudKit
 
 public protocol NCAccountRequestDelegate: AnyObject {
     func accountRequestAddAccount()
-    func accountRequestChangeAccount(account: String)
+    func accountRequestChangeAccount(account: String, controller: UIViewController?)
 }
 
 class NCAccountRequest: UIViewController {
@@ -35,11 +35,12 @@ class NCAccountRequest: UIViewController {
     @IBOutlet weak var progressView: UIProgressView!
 
     public var accounts: [tableAccount] = []
-    public var activeAccount: tableAccount?
+    public var activeAccount: String?
     public let heightCell: CGFloat = 60
     public var enableTimerProgress: Bool = true
     public var enableAddAccount: Bool = false
     public var dismissDidEnterBackground: Bool = false
+    public var controller: UIViewController?
     public weak var delegate: NCAccountRequestDelegate?
     let utility = NCUtility()
     private var timer: Timer?
@@ -134,9 +135,9 @@ extension NCAccountRequest: UITableViewDelegate {
             delegate?.accountRequestAddAccount()
         } else {
             let account = accounts[indexPath.row]
-            if account.account != activeAccount?.account {
+            if account.account != activeAccount {
                 dismiss(animated: true) {
-                    self.delegate?.accountRequestChangeAccount(account: account.account)
+                    self.delegate?.accountRequestChangeAccount(account: account.account, controller: self.controller)
                 }
             } else {
                 dismiss(animated: true)
@@ -177,10 +178,7 @@ extension NCAccountRequest: UITableViewDataSource {
 
             let account = accounts[indexPath.row]
 
-            avatarImage?.image = utility.loadUserImage(
-                for: account.user,
-                   displayName: account.displayName,
-                   userBaseUrl: account)
+            avatarImage?.image = utility.loadUserImage(for: account.user, displayName: account.displayName, urlBase: account.urlBase)
 
             if account.alias.isEmpty {
                 userLabel?.text = account.user.uppercased()

+ 34 - 46
iOSClient/Account Settings/NCAccountSettingsModel.swift

@@ -27,7 +27,7 @@ import RealmSwift
 
 /// Protocol for know when the Account Settings has dimissed
 protocol NCAccountSettingsModelDelegate: AnyObject {
-    func accountSettingsDidDismiss(tableAccount: tableAccount?)
+    func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?)
 }
 
 /// A model that allows the user to configure the account
@@ -37,36 +37,34 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
     /// Root View Controller
     var controller: NCMainTabBarController?
     /// All account
-    var accounts: [tableAccount] = []
+    var tblAccounts: [tableAccount] = []
     /// Delegate
     weak var delegate: NCAccountSettingsModelDelegate?
-    /// Timer change user
-    var timerChangeAccount: Timer?
     /// Token observe tableAccount
     var notificationToken: NotificationToken?
-    /// Account now active
-    @Published var activeAccount: tableAccount?
+    /// Account now
+    @Published var tblAccount: tableAccount?
     /// Index
     @Published var indexActiveAccount: Int = 0
     /// Current alias
     @Published var alias: String = ""
     /// Set true for dismiss the view
     @Published var dismissView = false
+    /// DB
+    let database = NCManageDatabase.shared
 
     /// Initialization code to set up the ViewModel with the active account
     init(controller: NCMainTabBarController?, delegate: NCAccountSettingsModelDelegate?) {
         self.controller = controller
         self.delegate = delegate
         if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
-            NCManageDatabase.shared.previewCreateDB()
+            database.previewCreateDB()
         }
         onViewAppear()
         observeTableAccount()
     }
 
     deinit {
-        timerChangeAccount?.invalidate()
-        timerChangeAccount = nil
         notificationToken?.invalidate()
         notificationToken = nil
     }
@@ -96,45 +94,44 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
     /// Triggered when the view appears.
     func onViewAppear() {
         var indexActiveAccount = 0
-        let accounts = NCManageDatabase.shared.getAllAccount()
-        var activeAccount = NCManageDatabase.shared.getActiveAccount()
+        let tableAccounts = database.getAllTableAccount()
         var alias = ""
 
-        for (index, account) in accounts.enumerated() {
+        for (index, account) in tableAccounts.enumerated() {
             if account.active {
-                activeAccount = account
+                tblAccount = account
                 indexActiveAccount = index
                 alias = account.alias
             }
         }
 
         self.indexActiveAccount = indexActiveAccount
-        self.accounts = accounts
-        self.activeAccount = activeAccount
+        self.tblAccounts = tableAccounts
+        self.tblAccount = tblAccount
         self.alias = alias
     }
 
     /// Func to get the user display name + alias
     func getUserName() -> String {
-        guard let activeAccount else { return "" }
+        guard let tblAccount else { return "" }
         if alias.isEmpty {
-            return activeAccount.displayName
+            return tblAccount.displayName
         } else {
-            return activeAccount.displayName + " (\(alias))"
+            return tblAccount.displayName + " (\(alias))"
         }
     }
 
     /// Func to set alias
     func setAlias(_ value: String) {
-        guard let activeAccount else { return }
-        NCManageDatabase.shared.setAccountAlias(activeAccount.account, alias: alias)
+        guard let tblAccount else { return }
+        database.setAccountAlias(tblAccount.account, alias: alias)
     }
 
     /// Function to update the user data
     func getUserStatus() -> (statusImage: UIImage?, statusMessage: String, descriptionMessage: String) {
-        guard let activeAccount else { return (UIImage(), "", "") }
-        if NCGlobal.shared.capabilityUserStatusEnabled,
-           let tableAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", activeAccount.account)) {
+        guard let tblAccount else { return (UIImage(), "", "") }
+        if NCCapabilities.shared.getCapabilities(account: tblAccount.account).capabilityUserStatusEnabled,
+           let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", tblAccount.account)) {
             return NCUtility().getUserStatus(userIcon: tableAccount.userStatusIcon, userStatus: tableAccount.userStatusStatus, userMessage: tableAccount.userStatusMessage)
         }
         return (nil, "", "")
@@ -142,17 +139,18 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
 
     /// Is the user an Admin
     func isAdminGroup() -> Bool {
-        guard let activeAccount else { return false }
-        let groups = NCManageDatabase.shared.getAccountGroups(account: activeAccount.account)
+        guard let tblAccount else { return false }
+        let groups = database.getAccountGroups(account: tblAccount.account)
         return groups.contains(NCGlobal.shared.groupAdmin)
     }
 
     /// Function to know the height of "account" data
     func getTableViewHeight() -> CGFloat {
-        guard let activeAccount else { return 0 }
-        var height: CGFloat = NCGlobal.shared.capabilityUserStatusEnabled ? 190 : 220
-        if NCGlobal.shared.capabilityUserStatusEnabled,
-           let tableAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", activeAccount.account)) {
+        guard let tblAccount else { return 0 }
+        let capabilities = NCCapabilities.shared.getCapabilities(account: tblAccount.account)
+        var height: CGFloat = capabilities.capabilityUserStatusEnabled ? 190 : 220
+        if capabilities.capabilityUserStatusEnabled,
+           let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", tblAccount.account)) {
             if !tableAccount.email.isEmpty { height += 30 }
             if !tableAccount.phone.isEmpty { height += 30 }
             if !tableAccount.address.isEmpty { height += 30 }
@@ -163,33 +161,23 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
 
     /// Function to change account after 1.5 sec of change
     func setAccount(account: String) {
-        if let tableAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)), self.activeAccount?.account != tableAccount.account {
-            self.activeAccount = tableAccount
+        if let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", account)) {
+            self.tblAccount = tableAccount
             self.alias = tableAccount.alias
-            /// Change active account
-            timerChangeAccount?.invalidate()
-            timerChangeAccount = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(changeAccount), userInfo: nil, repeats: false)
-
-        }
-    }
-
-    @objc func changeAccount() {
-        if let activeAccount {
-            self.appDelegate.changeAccount(activeAccount.account, userProfile: nil) { }
         }
     }
 
     /// Function to delete the current account
     func deleteAccount() {
-        if let activeAccount {
-            appDelegate.deleteAccount(activeAccount.account)
-            if let account = NCManageDatabase.shared.getAllAccount().first?.account {
-                appDelegate.changeAccount(account, userProfile: nil) {
+        if let tblAccount {
+            NCAccount().deleteAccount(tblAccount.account)
+            if let account = database.getAllTableAccount().first?.account {
+                NCAccount().changeAccount(account, userProfile: nil, controller: self.controller) {
                     onViewAppear()
                 }
             } else {
                 dismissView = true
-                appDelegate.openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
+                appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
             }
         }
     }

+ 16 - 14
iOSClient/Account Settings/NCAccountSettingsView.swift

@@ -41,9 +41,9 @@ struct NCAccountSettingsView: View {
             Form {
                 Section(content: {
                     TabView(selection: $model.indexActiveAccount) {
-                        ForEach(0..<model.accounts.count, id: \.self) { index in
+                        ForEach(0..<model.tblAccounts.count, id: \.self) { index in
                             let status = model.getUserStatus()
-                            let avatar = NCUtility().loadUserImage(for: model.accounts[index].user, displayName: model.accounts[index].displayName, userBaseUrl: model.accounts[index])
+                            let avatar = NCUtility().loadUserImage(for: model.tblAccounts[index].user, displayName: model.tblAccounts[index].displayName, urlBase: model.tblAccounts[index].urlBase)
                             ///
                             /// User
                             VStack {
@@ -76,15 +76,15 @@ struct NCAccountSettingsView: View {
                                     .frame(height: 20)
                                 ///
                                 /// Personal data
-                                if let activeAccount = model.activeAccount {
-                                    if !activeAccount.email.isEmpty {
+                                if let tblAccount = model.tblAccount {
+                                    if !tblAccount.email.isEmpty {
                                         HStack {
                                             Image(systemName: "mail")
                                                 .resizable()
                                                 .scaledToFit()
                                                 .font(Font.system(.body).weight(.light))
                                                 .frame(width: 20, height: 20)
-                                            Text(activeAccount.email)
+                                            Text(tblAccount.email)
                                                 .lineLimit(1)
                                                 .truncationMode(.middle)
                                                 .frame(maxWidth: .infinity, alignment: .leading)
@@ -92,28 +92,28 @@ struct NCAccountSettingsView: View {
                                         }
                                         .frame(maxWidth: .infinity, maxHeight: 30)
                                     }
-                                    if !activeAccount.phone.isEmpty {
+                                    if !tblAccount.phone.isEmpty {
                                         HStack {
                                             Image(systemName: "phone")
                                                 .resizable()
                                                 .scaledToFit()
                                                 .font(Font.system(.body).weight(.light))
                                                 .frame(width: 20, height: 20)
-                                            Text(activeAccount.phone)
+                                            Text(tblAccount.phone)
                                                 .lineLimit(1)
                                                 .truncationMode(.middle)
                                                 .frame(maxWidth: .infinity, alignment: .leading)
                                         }
                                         .frame(maxWidth: .infinity, maxHeight: 30)
                                     }
-                                    if !activeAccount.address.isEmpty {
+                                    if !tblAccount.address.isEmpty {
                                         HStack {
                                             Image(systemName: "house")
                                                 .resizable()
                                                 .scaledToFit()
                                                 .font(Font.system(.body).weight(.light))
                                                 .frame(width: 20, height: 20)
-                                            Text(activeAccount.address)
+                                            Text(tblAccount.address)
                                                 .lineLimit(1)
                                                 .truncationMode(.middle)
                                                 .frame(maxWidth: .infinity, alignment: .leading)
@@ -130,7 +130,7 @@ struct NCAccountSettingsView: View {
                     .animation(.easeIn(duration: 0.3), value: animation)
                     .onChange(of: model.indexActiveAccount) { index in
                         animation.toggle()
-                        model.setAccount(account: model.accounts[index].account)
+                        model.setAccount(account: model.tblAccounts[index].account)
                     }
                     ///
                     /// Change alias
@@ -155,7 +155,7 @@ struct NCAccountSettingsView: View {
                     }
                     ///
                     /// User Status
-                    if NCGlobal.shared.capabilityUserStatusEnabled {
+                    if NCCapabilities.shared.getCapabilities(account: model.tblAccount?.account).capabilityUserStatusEnabled {
                         Button(action: {
                             showUserStatus = true
                         }, label: {
@@ -175,7 +175,9 @@ struct NCAccountSettingsView: View {
                             .font(.system(size: 14))
                         })
                         .sheet(isPresented: $showUserStatus) {
-                            UserStatusView(showUserStatus: $showUserStatus)
+                            if let account = model.tblAccount?.account {
+                                UserStatusView(showUserStatus: $showUserStatus, account: account)
+                            }
                         }
                         .onChange(of: showUserStatus) { _ in }
                     }
@@ -201,7 +203,7 @@ struct NCAccountSettingsView: View {
                             .font(.system(size: 14))
                         })
                         .sheet(isPresented: $showServerCertificate) {
-                            if let url = URL(string: model.activeAccount?.urlBase), let host = url.host {
+                            if let url = URL(string: model.tblAccount?.urlBase), let host = url.host {
                                 certificateDetailsView(host: host, title: NSLocalizedString("_certificate_view_", comment: ""))
                             }
                         }
@@ -279,7 +281,7 @@ struct NCAccountSettingsView: View {
             }
         }
         .onDisappear {
-            model.delegate?.accountSettingsDidDismiss(tableAccount: model.activeAccount)
+            model.delegate?.accountSettingsDidDismiss(tableAccount: model.tblAccount, controller: model.controller)
         }
     }
 }

+ 17 - 16
iOSClient/Activity/NCActivity.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="nhT-TJ-YvX">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="nhT-TJ-YvX">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -20,28 +20,28 @@
                                 <rect key="frame" x="0.0" y="48" width="414" height="848"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <prototypes>
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="tableCell" rowHeight="35" id="ggj-aE-fnh" customClass="NCActivityTableViewCell" customModule="Nextcloud" customModuleProvider="target">
-                                        <rect key="frame" x="0.0" y="50" width="414" height="35"/>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="tableCell" rowHeight="50" id="ggj-aE-fnh" customClass="NCActivityTableViewCell" customModule="Nextcloud" customModuleProvider="target">
+                                        <rect key="frame" x="0.0" y="50" width="414" height="50"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ggj-aE-fnh" id="i35-U4-bEk">
-                                            <rect key="frame" x="0.0" y="0.0" width="414" height="35"/>
+                                            <rect key="frame" x="0.0" y="0.0" width="414" height="50"/>
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
-                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" text="Label" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcO-YL-MuT">
-                                                    <rect key="frame" x="100" y="0.0" width="304" height="18"/>
-                                                    <fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="15"/>
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" text="Label" lineBreakMode="tailTruncation" numberOfLines="4" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcO-YL-MuT">
+                                                    <rect key="frame" x="90" y="3" width="314" height="44"/>
+                                                    <fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="14"/>
                                                     <nil key="textColor"/>
                                                     <nil key="highlightedColor"/>
                                                 </label>
-                                                <imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="LQ8-cO-794" userLabel="avatar">
-                                                    <rect key="frame" x="50" y="0.0" width="35" height="35"/>
+                                                <imageView contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="LQ8-cO-794" userLabel="avatar">
+                                                    <rect key="frame" x="50" y="12.5" width="25" height="25"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="width" constant="35" id="OKz-e8-DzD"/>
-                                                        <constraint firstAttribute="height" constant="35" id="fwd-J4-5uY"/>
+                                                        <constraint firstAttribute="width" constant="25" id="OKz-e8-DzD" userLabel="25"/>
+                                                        <constraint firstAttribute="height" constant="25" id="fwd-J4-5uY"/>
                                                     </constraints>
                                                 </imageView>
                                                 <imageView userInteractionEnabled="NO" alpha="0.59999999999999998" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xNG-sf-PnA">
-                                                    <rect key="frame" x="20" y="8" width="20" height="20"/>
+                                                    <rect key="frame" x="20" y="15" width="20" height="20"/>
                                                     <constraints>
                                                         <constraint firstAttribute="width" constant="20" id="Lbv-yi-vAh"/>
                                                         <constraint firstAttribute="height" constant="20" id="TML-VJ-2i3"/>
@@ -50,12 +50,13 @@
                                             </subviews>
                                             <constraints>
                                                 <constraint firstAttribute="trailing" secondItem="fcO-YL-MuT" secondAttribute="trailing" constant="10" id="1pG-qk-inI"/>
-                                                <constraint firstItem="LQ8-cO-794" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" id="3fU-rp-D7s"/>
-                                                <constraint firstItem="xNG-sf-PnA" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" constant="8" id="4EB-59-y1Y"/>
+                                                <constraint firstAttribute="bottom" secondItem="fcO-YL-MuT" secondAttribute="bottom" constant="3" id="9Yo-kF-q3Y"/>
                                                 <constraint firstItem="xNG-sf-PnA" firstAttribute="leading" secondItem="i35-U4-bEk" secondAttribute="leading" constant="20" id="CRN-18-SeU"/>
+                                                <constraint firstItem="LQ8-cO-794" firstAttribute="centerY" secondItem="i35-U4-bEk" secondAttribute="centerY" id="Giz-bM-Jpm"/>
+                                                <constraint firstItem="xNG-sf-PnA" firstAttribute="centerY" secondItem="i35-U4-bEk" secondAttribute="centerY" id="PDf-gj-Y2F"/>
                                                 <constraint firstItem="fcO-YL-MuT" firstAttribute="leading" secondItem="LQ8-cO-794" secondAttribute="trailing" constant="15" id="am5-CT-0kZ" userLabel="Subject.leading = Icon.trailing + 50"/>
                                                 <constraint firstItem="LQ8-cO-794" firstAttribute="leading" secondItem="xNG-sf-PnA" secondAttribute="trailing" constant="10" id="aqp-Wu-9Hk"/>
-                                                <constraint firstItem="fcO-YL-MuT" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" id="faC-by-km5"/>
+                                                <constraint firstItem="fcO-YL-MuT" firstAttribute="top" secondItem="i35-U4-bEk" secondAttribute="top" constant="3" id="faC-by-km5"/>
                                             </constraints>
                                         </tableViewCellContentView>
                                         <connections>

+ 78 - 40
iOSClient/Activity/NCActivity.swift

@@ -25,6 +25,7 @@
 import UIKit
 import SwiftRichString
 import NextcloudKit
+import SVGKit
 
 class NCActivity: UIViewController, NCSharePagingContent {
     @IBOutlet weak var viewContainerConstraint: NSLayoutConstraint!
@@ -36,9 +37,9 @@ class NCActivity: UIViewController, NCSharePagingContent {
     var metadata: tableMetadata?
     var showComments: Bool = false
 
-    let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     let utilityFileSystem = NCUtilityFileSystem()
     let utility = NCUtility()
+    let database = NCManageDatabase.shared
     var allItems: [DateCompareable] = []
     var sectionDates: [Date] = []
     var dataSourceTask: URLSessionTask?
@@ -46,6 +47,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
     var insets = UIEdgeInsets(top: 8, left: 0, bottom: 0, right: 0)
     var didSelectItemEnable: Bool = true
     var objectType: String?
+    var account: String = ""
 
     var isFetchingActivity = false
     var hasActivityToLoad = true {
@@ -53,6 +55,14 @@ class NCActivity: UIViewController, NCSharePagingContent {
     }
     var dateAutomaticFetch: Date?
 
+    var session: NCSession.Session {
+        if account.isEmpty {
+            NCSession.shared.getSession(controller: tabBarController)
+        } else {
+            NCSession.shared.getSession(account: account)
+        }
+    }
+
     // MARK: - View Life Cycle
 
     override func viewDidLoad() {
@@ -74,15 +84,12 @@ class NCActivity: UIViewController, NCSharePagingContent {
 
     func setupComments() {
         // Display Name & Quota
-        guard let activeAccount = NCManageDatabase.shared.getActiveAccount(), height > 0 else {
-            return
-        }
-
+        guard let metadata else { return }
         tableView.register(UINib(nibName: "NCShareCommentsCell", bundle: nil), forCellReuseIdentifier: "cell")
         commentView = Bundle.main.loadNibNamed("NCActivityCommentView", owner: self, options: nil)?.first as? NCActivityCommentView
-        commentView?.setup(urlBase: appDelegate, account: activeAccount) { newComment in
+        commentView?.setup(account: metadata.account) { newComment in
             guard let newComment = newComment, !newComment.isEmpty, let metadata = self.metadata else { return }
-            NextcloudKit.shared.putComments(fileId: metadata.fileId, message: newComment, account: self.appDelegate.account) { _, error in
+            NextcloudKit.shared.putComments(fileId: metadata.fileId, message: newComment, account: metadata.account) { _, _, error in
                 if error == .success {
                     self.commentView?.newCommentField.text?.removeAll()
                     self.loadComments()
@@ -145,7 +152,7 @@ extension NCActivity: UITableViewDelegate {
     }
 
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
-        return 80
+        return 80.0
     }
 
     func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
@@ -209,34 +216,49 @@ extension NCActivity: UITableViewDataSource {
     }
 
     func makeCommentCell(_ comment: tableComments, for indexPath: IndexPath) -> UITableViewCell {
-        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? NCShareCommentsCell else {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? NCShareCommentsCell,
+              let metadata else {
             return UITableViewCell()
         }
 
         cell.indexPath = indexPath
         cell.tableComments = comment
         cell.delegate = self
-        cell.sizeToFit()
 
-        // Image
-        let fileName = appDelegate.userBaseUrl + "-" + comment.actorId + ".png"
-        NCNetworking.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView)
+        // Avatar
+        let fileName = NCSession.shared.getFileName(urlBase: metadata.urlBase, user: comment.actorId)
+        let results = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName)
+
+        if results.image == nil {
+            cell.fileAvatarImageView?.image = utility.loadUserImage(for: comment.actorId, displayName: comment.actorDisplayName, urlBase: NCSession.shared.getSession(account: account).urlBase)
+        } else {
+            cell.fileAvatarImageView?.image = results.image
+        }
+
+        if let tableAvatar = results.tableAvatar,
+           !tableAvatar.loaded,
+           NCNetworking.shared.downloadAvatarQueue.operations.filter({ ($0 as? NCOperationDownloadAvatar)?.fileName == fileName }).isEmpty {
+            NCNetworking.shared.downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: comment.actorId, fileName: fileName, account: account, view: tableView))
+        }
+
         // Username
         cell.labelUser.text = comment.actorDisplayName
         cell.labelUser.textColor = NCBrandColor.shared.textColor
         // Date
         cell.labelDate.text = utility.dateDiff(comment.creationDateTime as Date)
-        cell.labelDate.textColor = .systemGray4
+        cell.labelDate.textColor = .lightGray
         // Message
         cell.labelMessage.text = comment.message
         cell.labelMessage.textColor = NCBrandColor.shared.textColor
         // Button Menu
-        if comment.actorId == appDelegate.userId {
+        if comment.actorId == metadata.userId {
             cell.buttonMenu.isHidden = false
         } else {
             cell.buttonMenu.isHidden = true
         }
 
+        cell.sizeToFit()
+
         return cell
     }
 
@@ -247,6 +269,7 @@ extension NCActivity: UITableViewDataSource {
         var orderKeysId: [String] = []
 
         cell.idActivity = activity.idActivity
+        cell.account = activity.account
         cell.indexPath = indexPath
         cell.avatar.image = nil
         cell.avatar.isHidden = true
@@ -256,18 +279,21 @@ extension NCActivity: UITableViewDataSource {
 
         // icon
         if !activity.icon.isEmpty {
+            activity.icon = activity.icon.replacingOccurrences(of: ".png", with: ".svg")
             let fileNameIcon = (activity.icon as NSString).lastPathComponent
             let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileNameIcon
 
             if FileManager.default.fileExists(atPath: fileNameLocalPath) {
-                if let image = UIImage(contentsOfFile: fileNameLocalPath) {
+                let image = fileNameIcon.contains(".svg") ? SVGKImage(contentsOfFile: fileNameLocalPath)?.uiImage : UIImage(contentsOfFile: fileNameLocalPath)
+
+                if let image {
                     cell.icon.image = image.withTintColor(NCBrandColor.shared.textColor, renderingMode: .alwaysOriginal)
                 }
             } else {
-                NextcloudKit.shared.downloadContent(serverUrl: activity.icon, account: appDelegate.account) { _, data, error in
-                    if error == .success {
+                NextcloudKit.shared.downloadContent(serverUrl: activity.icon, account: activity.account) { _, responseData, error in
+                    if error == .success, let data = responseData?.data {
                         do {
-                            try data!.write(to: NSURL(fileURLWithPath: fileNameLocalPath) as URL, options: .atomic)
+                            try data.write(to: NSURL(fileURLWithPath: fileNameLocalPath) as URL, options: .atomic)
                             self.tableView.reloadData()
                         } catch { return }
                     }
@@ -276,12 +302,24 @@ extension NCActivity: UITableViewDataSource {
         }
 
         // avatar
-        if !activity.user.isEmpty && activity.user != appDelegate.userId {
+        if !activity.user.isEmpty && activity.user != session.userId {
             cell.avatar.isHidden = false
             cell.fileUser = activity.user
-            let fileName = appDelegate.userBaseUrl + "-" + activity.user + ".png"
-            NCNetworking.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView)
             cell.subjectLeadingConstraint.constant = 15
+
+            let fileName = NCSession.shared.getFileName(urlBase: session.urlBase, user: activity.user)
+            let results = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName)
+
+            if results.image == nil {
+                cell.fileAvatarImageView?.image = utility.loadUserImage(for: activity.user, displayName: nil, urlBase: session.urlBase)
+            } else {
+                cell.fileAvatarImageView?.image = results.image
+            }
+
+            if !(results.tableAvatar?.loaded ?? false),
+               NCNetworking.shared.downloadAvatarQueue.operations.filter({ ($0 as? NCOperationDownloadAvatar)?.fileName == fileName }).isEmpty {
+                NCNetworking.shared.downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: activity.user, fileName: fileName, account: session.account, view: tableView))
+            }
         } else {
             cell.subjectLeadingConstraint.constant = -30
         }
@@ -300,7 +338,7 @@ extension NCActivity: UITableViewDataSource {
             }
 
             for key in keys {
-                if let result = NCManageDatabase.shared.getActivitySubjectRich(account: appDelegate.account, idActivity: activity.idActivity, key: key) {
+                if let result = database.getActivitySubjectRich(account: session.account, idActivity: activity.idActivity, key: key) {
                     orderKeysId.append(result.id)
                     subject = subject.replacingOccurrences(of: "{\(key)}", with: "<bold>" + result.name + "</bold>")
                 }
@@ -344,7 +382,7 @@ extension NCActivity {
         var bottom: CGFloat = 0
 
         if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
-            bottom = -mainTabBar.getHeight()
+           bottom = -mainTabBar.getHeight()
         }
         NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom - 35, style: .medium)
 
@@ -371,13 +409,13 @@ extension NCActivity {
     func loadDataSource() {
         var newItems = [DateCompareable]()
 
-        if showComments, let metadata = metadata, let account = NCManageDatabase.shared.getActiveAccount() {
-            let comments = NCManageDatabase.shared.getComments(account: account.account, objectId: metadata.fileId)
+        if showComments, let metadata {
+            let comments = database.getComments(account: metadata.account, objectId: metadata.fileId)
             newItems += comments
         }
 
-        let activities = NCManageDatabase.shared.getActivity(
-            predicate: NSPredicate(format: "account == %@", appDelegate.account),
+        let activities = database.getActivity(
+            predicate: NSPredicate(format: "account == %@", session.account),
             filterFileId: metadata?.fileId)
         newItems += activities.filter
 
@@ -395,7 +433,7 @@ extension NCActivity {
 
         NextcloudKit.shared.getComments(fileId: metadata.fileId, account: metadata.account) { _, comments, _, error in
             if error == .success, let comments = comments {
-                NCManageDatabase.shared.addComments(comments, account: metadata.account, objectId: metadata.fileId)
+                self.database.addComments(comments, account: metadata.account, objectId: metadata.fileId)
             } else if error.errorCode != NCGlobal.shared.errorResourceNotFound {
                 NCContentPresenter().showError(error: error)
             }
@@ -410,7 +448,7 @@ extension NCActivity {
 
     /// Check if most recent activivities are loaded, if not trigger reload
     func checkRecentActivity(disptachGroup: DispatchGroup) {
-        guard let result = NCManageDatabase.shared.getLatestActivityId(account: appDelegate.account), metadata == nil, hasActivityToLoad else {
+        guard let result = database.getLatestActivityId(account: session.account), metadata == nil, hasActivityToLoad else {
             return self.loadActivity(idActivity: 0, disptachGroup: disptachGroup)
         }
         let resultActivityId = max(result.activityFirstKnown, result.activityLastGiven)
@@ -423,14 +461,14 @@ extension NCActivity {
             objectId: nil,
             objectType: objectType,
             previews: true,
-            account: appDelegate.account) { task in
+            account: session.account) { task in
                 self.dataSourceTask = task
             } completion: { account, _, activityFirstKnown, activityLastGiven, _, error in
                 defer { disptachGroup.leave() }
 
                 let largestActivityId = max(activityFirstKnown, activityLastGiven)
                 guard error == .success,
-                      account == self.appDelegate.account,
+                      account == self.session.account,
                       largestActivityId > resultActivityId
                 else {
                     self.hasActivityToLoad = error.errorCode == NCGlobal.shared.errorNotModified ? false : self.hasActivityToLoad
@@ -452,26 +490,26 @@ extension NCActivity {
             objectId: metadata?.fileId,
             objectType: objectType,
             previews: true,
-            account: appDelegate.account) { task in
+            account: session.account) { task in
                 self.dataSourceTask = task
             } completion: { account, activities, activityFirstKnown, activityLastGiven, _, error in
                 defer { disptachGroup.leave() }
                 guard error == .success,
-                      account == self.appDelegate.account,
+                      account == self.session.account,
                       !activities.isEmpty
                 else {
                     self.hasActivityToLoad = error.errorCode == NCGlobal.shared.errorNotModified ? false : self.hasActivityToLoad
                     return
                 }
-                NCManageDatabase.shared.addActivity(activities, account: account)
+                self.database.addActivity(activities, account: account)
 
                 // update most recently loaded activity only when all activities are loaded (not filtered)
                 let largestActivityId = max(activityFirstKnown, activityLastGiven)
-                if let result = NCManageDatabase.shared.getLatestActivityId(account: self.appDelegate.account) {
+                if let result = self.database.getLatestActivityId(account: self.session.account) {
                     resultActivityId = max(result.activityFirstKnown, result.activityLastGiven)
                 }
                 if self.metadata == nil, largestActivityId > resultActivityId {
-                    NCManageDatabase.shared.updateLatestActivityId(activityFirstKnown: activityFirstKnown, activityLastGiven: activityLastGiven, account: account)
+                    self.database.updateLatestActivityId(activityFirstKnown: activityFirstKnown, activityLastGiven: activityLastGiven, account: account)
                 }
             }
     }
@@ -482,7 +520,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
         guard let tableComment = tableComment else {
             return
         }
-        self.showProfileMenu(userId: tableComment.actorId)
+        self.showProfileMenu(userId: tableComment.actorId, session: session)
     }
 
     func tapMenu(with tableComments: tableComments?, sender: Any) {
@@ -509,7 +547,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
                     alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
                         guard let message = alert.textFields?.first?.text, !message.isEmpty else { return }
 
-                        NextcloudKit.shared.updateComments(fileId: metadata.fileId, messageId: tableComments.messageId, message: message, account: self.appDelegate.account) { _, error in
+                        NextcloudKit.shared.updateComments(fileId: metadata.fileId, messageId: tableComments.messageId, message: message, account: metadata.account) { _, _, error in
                             if error == .success {
                                 self.loadComments()
                             } else {
@@ -531,7 +569,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
                 action: { _ in
                     guard let metadata = self.metadata, let tableComments = tableComments else { return }
 
-                    NextcloudKit.shared.deleteComments(fileId: metadata.fileId, messageId: tableComments.messageId, account: metadata.account) { _, error in
+                    NextcloudKit.shared.deleteComments(fileId: metadata.fileId, messageId: tableComments.messageId, account: metadata.account) { _, _, error in
                         if error == .success {
                             self.loadComments()
                         } else {

+ 3 - 2
iOSClient/Activity/NCActivityCommentView.swift

@@ -29,12 +29,13 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
 
     var completionHandler: ((String?) -> Void)?
 
-    func setup(urlBase: NCUserBaseUrl, account: tableAccount, completionHandler: @escaping (String?) -> Void) {
+    func setup(account: String, completionHandler: @escaping (String?) -> Void) {
+        let session = NCSession.shared.getSession(account: account)
         self.completionHandler = completionHandler
         newCommentField.placeholder = NSLocalizedString("_new_comment_", comment: "")
         newCommentField.delegate = self
 
-        let fileName = urlBase.userBaseUrl + "-" + urlBase.user + ".png"
+        let fileName = NCSession.shared.getFileName(urlBase: session.urlBase, user: session.user)
         let fileNameLocalPath = NCUtilityFileSystem().directoryUserData + "/" + fileName
         if let image = UIImage(contentsOfFile: fileNameLocalPath) {
             imageItem.image = image

+ 25 - 31
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -22,10 +22,11 @@
 //
 
 import Foundation
+import UIKit
 import NextcloudKit
 import FloatingPanel
-import JGProgressHUD
 import Queuer
+import Alamofire
 
 class NCActivityCollectionViewCell: UICollectionViewCell {
     @IBOutlet weak var imageView: UIImageView!
@@ -44,14 +45,15 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
     @IBOutlet weak var subject: UILabel!
     @IBOutlet weak var subjectLeadingConstraint: NSLayoutConstraint!
 
-    private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     private var user: String = ""
     private var index = IndexPath()
 
     var idActivity: Int = 0
     var activityPreviews: [tableActivityPreview] = []
     var didSelectItemEnable: Bool = true
-    var viewController = UIViewController()
+    var viewController = NCActivity()
+    var account: String!
+
     let utility = NCUtility()
 
     var indexPath: IndexPath {
@@ -75,7 +77,7 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
 
     @objc func tapAvatarImage() {
         guard let fileUser = fileUser else { return }
-        viewController.showProfileMenu(userId: fileUser)
+        viewController.showProfileMenu(userId: fileUser, session: NCSession.shared.getSession(account: account))
     }
 }
 
@@ -100,9 +102,9 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
             }
             if (responder as? UIViewController)!.navigationController != nil {
                 if let viewController = UIStoryboard(name: "NCTrash", bundle: nil).instantiateInitialViewController() as? NCTrash {
-                    if let result = NCManageDatabase.shared.getTrashItem(fileId: String(activityPreview.fileId), account: activityPreview.account) {
-                        viewController.blinkFileId = result.fileId
-                        viewController.filePath = result.filePath
+                    if let resultTableTrash = NCManageDatabase.shared.getResultTrashItem(fileId: String(activityPreview.fileId), account: activityPreview.account) {
+                        viewController.blinkFileId = resultTableTrash.fileId
+                        viewController.filePath = resultTableTrash.filePath
                         (responder as? UIViewController)!.navigationController?.pushViewController(viewController, animated: true)
                     } else {
                         let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_trash_file_not_found_")
@@ -117,7 +119,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
             guard let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: activityPreview.idActivity, id: String(activityPreview.fileId)) else {
                 return
             }
-            NCActionCenter.shared.viewerFile(account: appDelegate.account, fileId: activitySubjectRich.id, viewController: viewController)
+            NCActionCenter.shared.viewerFile(account: account, fileId: activitySubjectRich.id, viewController: viewController)
         }
     }
 }
@@ -133,9 +135,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
     }
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
-        guard let cell: NCActivityCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as? NCActivityCollectionViewCell else {
-            return UICollectionViewCell()
-        }
+        let cell: NCActivityCollectionViewCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as? NCActivityCollectionViewCell)!
 
         cell.imageView.image = nil
         cell.indexPath = indexPath
@@ -147,22 +147,22 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
         if activityPreview.view == "trashbin" {
             let source = activityPreview.source
 
-            utility.convertSVGtoPNGWriteToUserData(svgUrlString: source, width: 100, rewrite: false, account: appDelegate.account, id: idActivity) { imageNamePath, id in
+            utility.convertSVGtoPNGWriteToUserData(svgUrlString: source, width: 100, rewrite: false, account: account, id: idActivity) { imageNamePath, id in
                 if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
                     cell.imageView.image = image
                 } else {
-                    cell.imageView.image = NCImageCache.images.file
+                    cell.imageView.image = NCImageCache.shared.getImageFile()
                 }
             }
         } else {
             if activityPreview.isMimeTypeIcon {
                 let source = activityPreview.source
 
-                utility.convertSVGtoPNGWriteToUserData(svgUrlString: source, width: 150, rewrite: false, account: appDelegate.account, id: idActivity) { imageNamePath, id in
+                utility.convertSVGtoPNGWriteToUserData(svgUrlString: source, width: 150, rewrite: false, account: account, id: idActivity) { imageNamePath, id in
                     if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
                         cell.imageView.image = image
                     } else {
-                        cell.imageView.image = NCImageCache.images.file
+                        cell.imageView.image = NCImageCache.shared.getImageFile()
                     }
                 }
             } else {
@@ -178,7 +178,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                         cell.fileId = fileId
                         if !FileManager.default.fileExists(atPath: fileNamePath) {
                             if NCNetworking.shared.downloadThumbnailActivityQueue.operations.filter({ ($0 as? NCOperationDownloadThumbnailActivity)?.fileId == fileId }).isEmpty {
-                                NCNetworking.shared.downloadThumbnailActivityQueue.addOperation(NCOperationDownloadThumbnailActivity(fileId: fileId, fileNamePreviewLocalPath: fileNamePath, account: appDelegate.account, cell: cell, collectionView: collectionView))
+                                NCNetworking.shared.downloadThumbnailActivityQueue.addOperation(NCOperationDownloadThumbnailActivity(fileId: fileId, fileNamePreviewLocalPath: fileNamePath, account: account, collectionView: collectionView))
                             }
                         }
                     }
@@ -203,18 +203,16 @@ extension NCActivityTableViewCell: UICollectionViewDelegateFlowLayout {
     }
 }
 
-class NCOperationDownloadThumbnailActivity: ConcurrentOperation {
-    var cell: NCActivityCollectionViewCell?
+class NCOperationDownloadThumbnailActivity: ConcurrentOperation, @unchecked Sendable {
     var collectionView: UICollectionView?
     var fileNamePreviewLocalPath: String
     var fileId: String
     var account: String
 
-    init(fileId: String, fileNamePreviewLocalPath: String, account: String, cell: NCActivityCollectionViewCell?, collectionView: UICollectionView?) {
+    init(fileId: String, fileNamePreviewLocalPath: String, account: String, collectionView: UICollectionView?) {
         self.fileNamePreviewLocalPath = fileNamePreviewLocalPath
         self.fileId = fileId
         self.account = account
-        self.cell = cell
         self.collectionView = collectionView
     }
 
@@ -222,23 +220,19 @@ class NCOperationDownloadThumbnailActivity: ConcurrentOperation {
         guard !isCancelled else { return self.finish() }
 
         NextcloudKit.shared.downloadPreview(fileId: fileId,
-                                            fileNamePreviewLocalPath: fileNamePreviewLocalPath,
-                                            account: account,
-                                            options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, imagePreview, _, _, _, error in
-            if error == .success, let imagePreview = imagePreview {
-                DispatchQueue.main.async {
-                    if self.fileId == self.cell?.fileId, let imageView = self.cell?.imageView {
-                        UIView.transition(with: imageView,
+                                            account: account) { _, _, _, _, responseData, error in
+            if error == .success, let data = responseData?.data, let collectionView = self.collectionView {
+                for case let cell as NCActivityCollectionViewCell in collectionView.visibleCells {
+                    if self.fileId == cell.fileId {
+                        UIView.transition(with: cell.imageView,
                                           duration: 0.75,
                                           options: .transitionCrossDissolve,
-                                          animations: { imageView.image = imagePreview },
+                                          animations: { cell.imageView.image = UIImage(data: data) },
                                           completion: nil)
-                    } else {
-                        self.collectionView?.reloadData()
                     }
                 }
             }
             self.finish()
         }
-    }
+            }
 }

+ 58 - 253
iOSClient/AppDelegate.swift

@@ -32,20 +32,11 @@ import EasyTipView
 import SwiftUI
 
 @UIApplicationMain
-class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, NCUserBaseUrl {
-
-    var account: String = ""
-    var urlBase: String = ""
-    var user: String = ""
-    var userId: String = ""
-    var password: String = ""
-
+class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
     var tipView: EasyTipView?
     var backgroundSessionCompletionHandler: (() -> Void)?
     var activeLogin: NCLogin?
     var activeLoginWeb: NCLoginProvider?
-    var timerErrorNetworking: Timer?
-    var timerErrorNetworkingDisabled: Bool = false
     var taskAutoUploadDate: Date = Date()
     var isUiTestingEnabled: Bool {
         return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
@@ -58,15 +49,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         if isUiTestingEnabled {
-            deleteAllAccounts()
+            NCAccount().deleteAllAccounts()
         }
         let utilityFileSystem = NCUtilityFileSystem()
         let utility = NCUtility()
+        var levelLog = 0
+        let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionApp())
 
         NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0)
 
-        let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionApp())
-
         UserDefaults.standard.register(defaults: ["UserAgent": userAgent])
         if !NCKeychain().disableCrashservice, !NCBrandOptions.shared.disable_crash_service {
             FirebaseApp.configure()
@@ -76,14 +67,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         utilityFileSystem.emptyTemporaryDirectory()
         utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto")
 
-        // Activated singleton
-        _ = NCActionCenter.shared
-        _ = NCNetworking.shared
+        // Create users colors
+        NCBrandColor.shared.createUserColors()
 
         NextcloudKit.shared.setup(delegate: NCNetworking.shared)
-        NextcloudKit.shared.setup(userAgent: userAgent)
-
-        var levelLog = 0
         NextcloudKit.shared.nkCommonInstance.pathLog = utilityFileSystem.directoryGroup
 
         if NCBrandOptions.shared.disable_log {
@@ -96,37 +83,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(levelLog) " + versionNextcloudiOS)
         }
 
-        if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Account active \(activeAccount.account)")
-
-            if NCKeychain().getPassword(account: activeAccount.account).isEmpty {
-                NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] PASSWORD NOT FOUND for \(activeAccount.account)")
-            }
-
-            account = activeAccount.account
-            urlBase = activeAccount.urlBase
-            user = activeAccount.user
-            userId = activeAccount.userId
-            password = NCKeychain().getPassword(account: 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)
-            DispatchQueue.global().async {
-                NCImageCache.shared.createMediaCache(account: self.account, withCacheSize: true)
-            }
-        } else {
-            NCKeychain().removeAll()
-            if let bundleID = Bundle.main.bundleIdentifier {
-                UserDefaults.standard.removePersistentDomain(forName: bundleID)
-            }
-        }
-
-        NCBrandColor.shared.createUserColors()
-        NCImageCache.shared.createImagesCache()
-
-        // Push Notification & display notification
+        /// Push Notification & display notification
         UNUserNotificationCenter.current().getNotificationSettings { settings in
             self.notificationSettings = settings
         }
@@ -140,7 +97,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             review.showStoreReview()
         }
 
-        // Background task register
+        /// Background task register
         BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in
             self.handleAppRefresh(task)
         }
@@ -148,12 +105,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             self.handleProcessingTask(task)
         }
 
-        FileNameValidator.shared.setup(
-            forbiddenFileNames: NCGlobal.shared.capabilityForbiddenFileNames,
-            forbiddenFileNameBasenames: NCGlobal.shared.capabilityForbiddenFileNameBasenames,
-            forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters,
-            forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions
-        )
+        /// Activation singleton
+        _ = NCActionCenter.shared
+        _ = NCNetworkingProcess.shared
 
         return true
     }
@@ -235,39 +189,46 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     func handleAppRefreshProcessingTask(taskText: String, completion: @escaping () -> Void = {}) {
         Task {
-            var itemsAutoUpload = 0
+            var numAutoUpload = 0
+            guard let account = NCManageDatabase.shared.getActiveTableAccount()?.account else {
+                return
+            }
 
             NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) start handle")
 
             // Test every > 1 min
             if Date() > self.taskAutoUploadDate.addingTimeInterval(60) {
                 self.taskAutoUploadDate = Date()
-                itemsAutoUpload = await NCAutoUpload.shared.initAutoUpload()
-                NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) auto upload with \(itemsAutoUpload) uploads")
+                numAutoUpload = await NCAutoUpload.shared.initAutoUpload(account: account)
+                NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) auto upload with \(numAutoUpload) uploads")
             } else {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) disabled auto upload")
             }
 
-            let results = await NCNetworkingProcess.shared.start(scene: nil)
+            let results = await NCNetworkingProcess.shared.refreshProcessingTask()
             NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) networking process with download: \(results.counterDownloading) upload: \(results.counterUploading)")
 
             if taskText == "ProcessingTask",
-               itemsAutoUpload == 0,
+               numAutoUpload == 0,
                results.counterDownloading == 0,
                results.counterUploading == 0,
-               let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.account), sorted: "offlineDate", ascending: true) {
+               let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", account), sorted: "offlineDate", ascending: true) {
                 for directory: tableDirectory in directories {
                     // test only 3 time for day (every 8 h.)
                     if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() {
                         NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)")
                         continue
                     }
-                    let results = await NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false)
-                    NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(results.errorCode), item: \(results.items)")
+                    let results = await NCNetworking.shared.synchronization(account: account, serverUrl: directory.serverUrl, add: false)
+                    NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(results.errorCode), item: \(results.num)")
                 }
             }
 
-            let counter = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "account == %@ AND (session == %@ || session == %@) AND status != %d", self.account, NCNetworking.shared.sessionDownloadBackground, NCNetworking.shared.sessionUploadBackground, NCGlobal.shared.metadataStatusNormal))?.count ?? 0
+            let counter = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "account == %@ AND (session == %@ || session == %@) AND status != %d",
+                                                                                   account,
+                                                                                   NCNetworking.shared.sessionDownloadBackground,
+                                                                                   NCNetworking.shared.sessionUploadBackground,
+                                                                                   NCGlobal.shared.metadataStatusNormal))?.count ?? 0
             UIApplication.shared.applicationIconBadgeNumber = counter
 
             NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) completion handle")
@@ -315,28 +276,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     func nextcloudPushNotificationAction(data: [String: AnyObject]) {
         guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data) else { return }
-        var findAccount: Bool = false
+        var findAccount: String?
 
         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
+            for tableAccount in NCManageDatabase.shared.getAllTableAccount() {
+                if tableAccount.account == accountPush {
+                    for controller in SceneManager.shared.getControllers() {
+                        if controller.account == accountPush {
+                            NCAccount().changeAccount(tableAccount.account, userProfile: nil, controller: controller) {
+                                findAccount = tableAccount.account
+                            }
                         }
                     }
                 }
             }
-            if findAccount, let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
+            if let account = findAccount, let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
+                viewController.session = NCSession.shared.getSession(account: account)
                 DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                     let navigationController = UINavigationController(rootViewController: viewController)
                     navigationController.modalPresentationStyle = .fullScreen
                     UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true)
                 }
-            } else if !findAccount {
+            } else {
                 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 }))
@@ -347,7 +308,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // MARK: - Login
 
-    func openLogin(selector: Int, openLoginWeb: Bool, windowForRootViewController: UIWindow? = nil) {
+    func openLogin(selector: Int) {
+        UIApplication.shared.allSceneSessionDestructionExceptFirst()
+
         func showLoginViewController(_ viewController: UIViewController?) {
             guard let viewController else { return }
             let navigationController = NCLoginNavigationController(rootViewController: viewController)
@@ -358,20 +321,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
             navigationController.navigationBar.isTranslucent = false
 
-            if let window = windowForRootViewController {
-                window.rootViewController = navigationController
-                window.makeKeyAndVisible()
-            } else {
-                UIApplication.shared.allSceneSessionDestructionExceptFirst()
-
-                if let rootVC = UIApplication.shared.firstWindow?.rootViewController {
-                    if let presentedVC = rootVC.presentedViewController, !(presentedVC is NCLoginNavigationController) {
-                        presentedVC.dismiss(animated: false) {
-                            rootVC.present(navigationController, animated: true)
-                        }
-                    } else {
-                        rootVC.present(navigationController, animated: true)
+            if let controller = UIApplication.shared.firstWindow?.rootViewController {
+                if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) {
+                    presentedVC.dismiss(animated: false) {
+                        controller.present(navigationController, animated: true)
                     }
+                } else {
+                    controller.present(navigationController, animated: true)
                 }
             }
         }
@@ -379,14 +335,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         // Nextcloud standard login
         if selector == NCGlobal.shared.introSignup {
             if activeLogin?.view.window == nil {
-                activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
                 if selector == NCGlobal.shared.introSignup {
-                    activeLogin?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders
                     let web = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider
                     web?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders
                     showLoginViewController(web)
                 } else {
-                    activeLogin?.urlBase = self.urlBase
+                    activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
+                    if let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController, !controller.account.isEmpty {
+                        let session = NCSession.shared.getSession(account: controller.account)
+                        activeLogin?.urlBase = session.urlBase
+                    }
                     showLoginViewController(activeLogin)
                 }
             }
@@ -399,35 +357,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
     }
 
-    // MARK: - Error Networking
-
-    func startTimerErrorNetworking(scene: UIScene) {
-        timerErrorNetworkingDisabled = false
-        timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking(_:)), userInfo: nil, repeats: true)
-    }
-
-    @objc private func checkErrorNetworking(_ notification: NSNotification) {
-        guard !self.timerErrorNetworkingDisabled,
-              !account.isEmpty,
-              NCKeychain().getPassword(account: account).isEmpty else { return }
-
-        let description = String.localizedStringWithFormat(NSLocalizedString("_error_check_remote_user_", comment: ""))
-        let error = NKError(errorCode: NCKeychain().getPassword(account: account).isEmpty ? NCGlobal.shared.errorUnauthorized997 : NCGlobal.shared.errorInternalServerError, errorDescription: description)
-        NCContentPresenter().showError(error: error, priority: .max)
-
-        deleteAccount(account)
-
-        let accounts = NCManageDatabase.shared.getAccounts()
-
-        if accounts?.count ?? 0 > 0, let newAccount = accounts?.first {
-            changeAccount(newAccount, userProfile: nil) { }
-        } else {
-            openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
-        }
-    }
+    // MARK: -
 
     func trustCertificateError(host: String) {
-        guard let currentHost = URL(string: self.urlBase)?.host,
+        guard let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(),
+              let currentHost = URL(string: activeTableAccount.urlBase)?.host,
               let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host,
               host != pushNotificationServerProxyHost,
               host == currentHost
@@ -459,136 +393,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true)
     }
 
-    // MARK: - Account
-
-    func createAccount(urlBase: String,
-                       user: String,
-                       password: String,
-                       completion: @escaping (_ error: NKError) -> Void) {
-        var urlBase = urlBase
-        if urlBase.last == "/" { urlBase = String(urlBase.dropLast()) }
-        let account: String = "\(user) \(urlBase)"
-
-        NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
-        NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in
-            if error == .success, let userProfile {
-                NCManageDatabase.shared.deleteAccount(account)
-                NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
-                NCKeychain().setClientCertificate(account: account, p12Data: NCNetworking.shared.p12Data, p12Password: NCNetworking.shared.p12Password)
-                self.changeAccount(account, userProfile: userProfile) {
-                    completion(error)
-                }
-            } else {
-                NextcloudKit.shared.setup(account: self.account, user: self.user, userId: self.userId, password: self.password, urlBase: self.urlBase)
-                let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true)
-                completion(error)
-            }
-        }
-    }
-
-    func changeAccount(_ account: String,
-                       userProfile: NKUserProfile?,
-                       completion: () -> Void) {
-        guard let tableAccount = NCManageDatabase.shared.setAccountActive(account) else {
-            return completion()
-        }
-
-        NCNetworking.shared.cancelAllQueue()
-        NCNetworking.shared.cancelDataTask()
-        NCNetworking.shared.cancelDownloadTasks()
-        NCNetworking.shared.cancelUploadTasks()
-
-        if account != self.account {
-            DispatchQueue.global().async {
-                if NCManageDatabase.shared.getAccounts()?.count == 1 {
-                    NCImageCache.shared.createMediaCache(account: account, withCacheSize: true)
-                } else {
-                    NCImageCache.shared.createMediaCache(account: account, withCacheSize: false)
-                }
-            }
-        }
-
-        self.account = tableAccount.account
-        self.urlBase = tableAccount.urlBase
-        self.user = tableAccount.user
-        self.userId = tableAccount.userId
-        self.password = NCKeychain().getPassword(account: 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)
-        }
-
-        NCPushNotification.shared.pushNotification()
-        NCService().startRequestServicesServer(account: self.account, user: self.user, userId: self.userId)
-
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
-        }
-
-        FileNameValidator.shared.setup(
-            forbiddenFileNames: NCGlobal.shared.capabilityForbiddenFileNames,
-            forbiddenFileNameBasenames: NCGlobal.shared.capabilityForbiddenFileNameBasenames,
-            forbiddenFileNameCharacters: NCGlobal.shared.capabilityForbiddenFileNameCharacters,
-            forbiddenFileNameExtensions: NCGlobal.shared.capabilityForbiddenFileNameExtensions
-        )
-
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser)
-        completion()
-    }
-
-    func deleteAccount(_ account: String) {
-        UIApplication.shared.allSceneSessionDestructionExceptFirst()
-
-        if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
-            NCPushNotification.shared.unsubscribingNextcloudServerPushNotification(account: account.account, urlBase: account.urlBase, user: account.user, withSubscribing: false)
-        }
-
-        let results = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@", account), sorted: "ocId", ascending: false)
-        let utilityFileSystem = NCUtilityFileSystem()
-        for result in results {
-            utilityFileSystem.removeFile(atPath: utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId))
-        }
-        NCManageDatabase.shared.clearDatabase(account: account, removeAccount: true)
-
-        NCKeychain().clearAllKeysEndToEnd(account: account)
-        NCKeychain().clearAllKeysPushNotification(account: account)
-        NCKeychain().setPassword(account: account, password: nil)
-
-        self.account = ""
-        self.urlBase = ""
-        self.user = ""
-        self.userId = ""
-        self.password = ""
-    }
-
-    func deleteAllAccounts() {
-        let accounts = NCManageDatabase.shared.getAccounts()
-        accounts?.forEach({ account in
-            deleteAccount(account)
-        })
-    }
-
-    func updateShareAccounts() -> Error? {
-        guard let dirGroupApps = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroupApps) else { return nil }
-        let tableAccount = NCManageDatabase.shared.getAllAccount()
-        var accounts = [NKShareAccounts.DataAccounts]()
-
-        for account in tableAccount {
-            let name = account.alias.isEmpty ? account.displayName : account.alias
-            let userBaseUrl = account.user + "-" + (URL(string: account.urlBase)?.host ?? "")
-            let avatarFileName = userBaseUrl + "-\(account.user).png"
-            let pathAvatarFileName = NCUtilityFileSystem().directoryUserData + "/" + avatarFileName
-            let image = UIImage(contentsOfFile: pathAvatarFileName)
-            accounts.append(NKShareAccounts.DataAccounts(withUrl: account.urlBase, user: account.user, name: name, image: image))
-        }
-        return NKShareAccounts().putShareAccounts(at: dirGroupApps, app: NCGlobal.shared.appScheme, dataAccounts: accounts)
-    }
-
     // MARK: - Reset Application
 
     func resetApplication() {
@@ -596,8 +400,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         NCNetworking.shared.cancelAllTask()
 
-        URLCache.shared.memoryCapacity = 0
-        URLCache.shared.diskCapacity = 0
+        URLCache.shared.removeAllCachedResponses()
 
         utilityFileSystem.removeGroupDirectoryProviderStorage()
         utilityFileSystem.removeGroupApplicationSupport()
@@ -605,6 +408,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         utilityFileSystem.removeTemporaryDirectory()
 
         NCKeychain().removeAll()
+        NCNetworking.shared.removeAllKeyUserDefaultsData(account: nil)
+
         exit(0)
     }
 

+ 1 - 1
iOSClient/Assistant/Create Task/NCAssistantCreateNewTask.swift

@@ -55,7 +55,7 @@ struct NCAssistantCreateNewTask: View {
 }
 
 #Preview {
-    let model = NCAssistantTask()
+    let model = NCAssistantTask(controller: nil)
 
     return NCAssistantCreateNewTask()
         .environmentObject(model)

+ 12 - 7
iOSClient/Assistant/Models/NCAssistantTask.swift

@@ -7,6 +7,7 @@
 //
 
 import Foundation
+import UIKit
 import NextcloudKit
 import SwiftUI
 
@@ -17,12 +18,16 @@ class NCAssistantTask: ObservableObject {
     @Published var selectedTask: NKTextProcessingTask?
     @Published var hasError: Bool = false
     @Published var isLoading: Bool = false
+    @Published var controller: NCMainTabBarController?
 
     private var tasks: [NKTextProcessingTask] = []
     private let excludedTypeIds = ["OCA\\ContextChat\\TextProcessing\\ContextChatTaskType"]
-    private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+    private var session: NCSession.Session {
+        NCSession.shared.getSession(controller: controller)
+    }
 
-    init() {
+    init(controller: NCMainTabBarController?) {
+        self.controller = controller
         load()
     }
 
@@ -50,7 +55,7 @@ class NCAssistantTask: ObservableObject {
         guard let id = task.id else { return }
         isLoading = true
 
-        NextcloudKit.shared.textProcessingGetTask(taskId: id, account: appDelegate.account) { _, task, _, error in
+        NextcloudKit.shared.textProcessingGetTask(taskId: id, account: session.account) { _, task, _, error in
             self.isLoading = false
 
             if error != .success {
@@ -65,7 +70,7 @@ class NCAssistantTask: ObservableObject {
     func scheduleTask(input: String) {
         isLoading = true
 
-        NextcloudKit.shared.textProcessingSchedule(input: input, typeId: selectedType?.id ?? "", identifier: "assistant", account: appDelegate.account) { _, task, _, error in
+        NextcloudKit.shared.textProcessingSchedule(input: input, typeId: selectedType?.id ?? "", identifier: "assistant", account: session.account) { _, task, _, error in
             self.isLoading = false
 
             if error != .success {
@@ -86,7 +91,7 @@ class NCAssistantTask: ObservableObject {
         guard let id = task.id else { return }
         isLoading = true
 
-        NextcloudKit.shared.textProcessingDeleteTask(taskId: id, account: appDelegate.account) { _, task, _, error in
+        NextcloudKit.shared.textProcessingDeleteTask(taskId: id, account: session.account) { _, task, _, error in
             self.isLoading = false
 
             if error != .success {
@@ -105,7 +110,7 @@ class NCAssistantTask: ObservableObject {
     private func loadAllTypes() {
         isLoading = true
 
-        NextcloudKit.shared.textProcessingGetTypes(account: appDelegate.account) { _, types, _, error in
+        NextcloudKit.shared.textProcessingGetTypes(account: session.account) { _, types, _, error in
             self.isLoading = false
 
             if error != .success {
@@ -130,7 +135,7 @@ class NCAssistantTask: ObservableObject {
     private func loadAllTasks(appId: String = "assistant") {
         isLoading = true
 
-        NextcloudKit.shared.textProcessingTaskList(appId: appId, account: appDelegate.account) { _, tasks, _, error in
+        NextcloudKit.shared.textProcessingTaskList(appId: appId, account: session.account) { _, tasks, _, error in
             self.isLoading = false
 
             if error != .success {

+ 2 - 2
iOSClient/Assistant/NCAssistant.swift

@@ -81,7 +81,7 @@ struct NCAssistant: View {
 }
 
 #Preview {
-    let model = NCAssistantTask()
+    let model = NCAssistantTask(controller: nil)
 
     return NCAssistant()
         .environmentObject(model)
@@ -125,7 +125,7 @@ struct TypeButton: View {
         .padding(.vertical, 7)
         .foregroundStyle(model.selectedType?.id == taskType?.id ? .white : .primary)
         .if(model.selectedType?.id == taskType?.id) { view in
-            view.background(Color(NCBrandColor.shared.brandElement))
+            view.background(Color(NCBrandColor.shared.getElement(account: model.controller?.account)))
         }
         .if(model.selectedType?.id != taskType?.id) { view in
             view.background(.ultraThinMaterial)

+ 2 - 1
iOSClient/Assistant/NCAssistantEmptyView.swift

@@ -9,6 +9,7 @@
 import SwiftUI
 
 struct NCAssistantEmptyView: View {
+    @EnvironmentObject var model: NCAssistantTask
     let titleKey, subtitleKey: String
 
     var body: some View {
@@ -17,7 +18,7 @@ struct NCAssistantEmptyView: View {
                 .renderingMode(.template)
                 .resizable()
                 .aspectRatio(contentMode: .fit)
-                .foregroundStyle(Color(NCBrandColor.shared.brandElement))
+                .foregroundStyle(Color(NCBrandColor.shared.getElement(account: model.controller?.account)))
                 .font(Font.system(.body).weight(.light))
                 .frame(height: 100)
 

+ 1 - 1
iOSClient/Assistant/Task Detail/NCAssistantTaskDetail.swift

@@ -28,7 +28,7 @@ struct NCAssistantTaskDetail: View {
 }
 
 #Preview {
-    let model = NCAssistantTask()
+    let model = NCAssistantTask(controller: nil)
 
     return NCAssistantTaskDetail(task: NKTextProcessingTask(id: 1, type: "OCP\\TextProcessing\\FreePromptTaskType", status: 1, userId: "christine", appId: "assistant", input: "", output: "", identifier: "", completionExpectedAt: 1712666412))
         .environmentObject(model)

+ 19 - 9
iOSClient/AudioRecorder/NCAudioRecorderViewController.swift

@@ -27,20 +27,22 @@
 import UIKit
 import AVFoundation
 import QuartzCore
+import NextcloudKit
 
 class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
-
-    var recording: NCAudioRecorder!
-    var startDate: Date = Date()
-    var fileName: String = ""
-    var serverUrl = ""
-    let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
-
     @IBOutlet weak var contentContainerView: UIView!
     @IBOutlet weak var durationLabel: UILabel!
     @IBOutlet weak var startStopLabel: UILabel!
     @IBOutlet weak var voiceRecordHUD: VoiceRecordHUD!
 
+    var recording: NCAudioRecorder!
+    var startDate: Date = Date()
+    var fileName: String = ""
+    var controller: NCMainTabBarController!
+    var session: NCSession.Session {
+        NCSession.shared.getSession(controller: controller)
+    }
+
     // MARK: - View Life Cycle
 
     override func viewDidLoad() {
@@ -55,7 +57,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
         voiceRecordHUD.fillColor = UIColor.green
 
         Task {
-            self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.appDelegate.account, serverUrl: self.serverUrl)
+            self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.session.account, serverUrl: controller.currentServerUrl())
             recording = NCAudioRecorder(to: self.fileName)
             recording.delegate = self
             do {
@@ -97,7 +99,15 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
     func uploadMetadata() {
         let fileNamePath = NSTemporaryDirectory() + self.fileName
-        let metadata = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
+        let metadata = NCManageDatabase.shared.createMetadata(fileName: fileName,
+                                                              fileNameView: fileName,
+                                                              ocId: UUID().uuidString,
+                                                              serverUrl: controller.currentServerUrl(),
+                                                              url: "",
+                                                              contentType: "",
+                                                              session: self.session,
+                                                              sceneIdentifier: self.controller?.sceneIdentifier)
+
         metadata.session = NCNetworking.shared.sessionUploadBackground
         metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
         metadata.status = NCGlobal.shared.metadataStatusWaitUpload

+ 1 - 1
iOSClient/BrowserWeb/NCBrowserWeb.swift

@@ -22,7 +22,7 @@
 //
 
 import UIKit
-import WebKit
+@preconcurrency import WebKit
 
 @objc protocol NCBrowserWebDelegate: AnyObject {
     @objc optional func browserWebDismiss()

+ 2 - 2
iOSClient/Color/NCColorPicker.swift

@@ -129,7 +129,7 @@ class NCColorPicker: UIViewController {
         systemIndigoButton.layer.cornerRadius = 5
         systemIndigoButton.layer.masksToBounds = true
 
-        defaultButton.backgroundColor = NCBrandColor.shared.brandElement
+        defaultButton.backgroundColor = NCBrandColor.shared.customer
         defaultButton.layer.cornerRadius = 5
         defaultButton.layer.masksToBounds = true
     }
@@ -214,7 +214,7 @@ class NCColorPicker: UIViewController {
             let serverUrl = metadata.serverUrl + "/" + metadata.fileName
             NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, colorFolder: hexColor, metadata: metadata)
             self.dismiss(animated: true)
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl, "clearDataSource": true])
         }
         self.dismiss(animated: true)
     }

+ 29 - 44
iOSClient/Data/NCManageDatabase+Account.swift

@@ -22,10 +22,11 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
-class tableAccount: Object, NCUserBaseUrl {
+class tableAccount: Object {
     @objc dynamic var account = ""
     @objc dynamic var active: Bool = false
     @objc dynamic var address = ""
@@ -84,13 +85,16 @@ extension NCManageDatabase {
         do {
             let realm = try Realm()
             try realm.write {
-                let addObject = tableAccount()
-                addObject.account = account
+                if let result = realm.objects(tableAccount.self).filter("account == %@", account).first {
+                    realm.delete(result)
+                }
+                let tableAccount = tableAccount()
+                tableAccount.account = account
                 NCKeychain().setPassword(account: account, password: password)
-                addObject.urlBase = urlBase
-                addObject.user = user
-                addObject.userId = userId
-                realm.add(addObject, update: .all)
+                tableAccount.urlBase = urlBase
+                tableAccount.user = user
+                tableAccount.userId = userId
+                realm.add(tableAccount, update: .all)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
@@ -108,22 +112,21 @@ extension NCManageDatabase {
         }
     }
 
-    func deleteAccount(_ account: String) {
+    func getActiveTableAccount() -> tableAccount? {
         do {
             let realm = try Realm()
-            try realm.write {
-                let result = realm.objects(tableAccount.self).filter("account == %@", account)
-                realm.delete(result)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+            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("[ERROR] Could not access database: \(error)")
         }
+        return nil
     }
 
-    func getActiveAccount() -> tableAccount? {
+    func getTableAccount(account: String) -> tableAccount? {
         do {
             let realm = try Realm()
-            guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return nil }
+            guard let result = realm.objects(tableAccount.self).filter("account == %@", account).first else { return nil }
             return tableAccount.init(value: result)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
@@ -144,7 +147,7 @@ extension NCManageDatabase {
         return nil
     }
 
-    func getAccount(predicate: NSPredicate) -> tableAccount? {
+    func getTableAccount(predicate: NSPredicate) -> tableAccount? {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter(predicate).first else { return nil }
@@ -155,7 +158,7 @@ extension NCManageDatabase {
         return nil
     }
 
-    func getAllAccount() -> [tableAccount] {
+    func getAllTableAccount() -> [tableAccount] {
         do {
             let realm = try Realm()
             let sorted = [SortDescriptor(keyPath: "active", ascending: false), SortDescriptor(keyPath: "user", ascending: true)]
@@ -194,16 +197,16 @@ extension NCManageDatabase {
         return ""
     }
 
-    func getAccountAutoUploadDirectory(urlBase: String, userId: String, account: String) -> String {
+    func getAccountAutoUploadDirectory(session: NCSession.Session) -> String {
         do {
             let realm = try Realm()
             guard let result = realm.objects(tableAccount.self).filter("active == true").first else { return "" }
             if result.autoUploadDirectory.isEmpty {
-                return utilityFileSystem.getHomeServer(urlBase: urlBase, userId: userId)
+                return utilityFileSystem.getHomeServer(session: session)
             } else {
                 // FIX change webdav -> /dav/files/
                 if result.autoUploadDirectory.contains("/webdav") {
-                    return utilityFileSystem.getHomeServer(urlBase: urlBase, userId: userId)
+                    return utilityFileSystem.getHomeServer(session: session)
                 } else {
                     return result.autoUploadDirectory
                 }
@@ -214,9 +217,9 @@ extension NCManageDatabase {
         return ""
     }
 
-    func getAccountAutoUploadPath(urlBase: String, userId: String, account: String) -> String {
+    func getAccountAutoUploadPath(session: NCSession.Session) -> String {
         let cameraFileName = self.getAccountAutoUploadFileName()
-        let cameraDirectory = self.getAccountAutoUploadDirectory(urlBase: urlBase, userId: userId, account: account)
+        let cameraDirectory = self.getAccountAutoUploadDirectory(session: session)
         let folderPhotos = utilityFileSystem.stringAppendServerUrl(cameraDirectory, addFileName: cameraFileName)
         return folderPhotos
     }
@@ -232,9 +235,7 @@ extension NCManageDatabase {
         return NCGlobal.shared.subfolderGranularityMonthly
     }
 
-    func setAccountActive(_ account: String) -> tableAccount? {
-        var accountReturn = tableAccount()
-
+    func setAccountActive(_ account: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -242,27 +243,11 @@ extension NCManageDatabase {
                 for result in results {
                     if result.account == account {
                         result.active = true
-                        accountReturn = result
                     } else {
                         result.active = false
                     }
                 }
             }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-            return nil
-        }
-        return tableAccount.init(value: accountReturn)
-    }
-
-    func removePasswordAccount(_ account: String) {
-        do {
-            let realm = try Realm()
-            try realm.write {
-                if let result = realm.objects(tableAccount.self).filter("account == %@", account).first {
-                    result.password = "********"
-                }
-            }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
@@ -309,7 +294,7 @@ extension NCManageDatabase {
         }
     }
 
-    func setAccountAutoUploadDirectory(_ serverUrl: String?, urlBase: String, userId: String, account: String) {
+    func setAccountAutoUploadDirectory(_ serverUrl: String?, session: NCSession.Session) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -317,7 +302,7 @@ extension NCManageDatabase {
                     if let serverUrl = serverUrl {
                         result.autoUploadDirectory = serverUrl
                     } else {
-                        result.autoUploadDirectory = self.getAccountAutoUploadDirectory(urlBase: urlBase, userId: userId, account: account)
+                        result.autoUploadDirectory = self.getAccountAutoUploadDirectory(session: session)
                     }
                 }
             }

+ 1 - 0
iOSClient/Data/NCManageDatabase+Activity.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 import SwiftyJSON

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

@@ -104,8 +104,9 @@ extension NCManageDatabase {
         return image
     }
 
-    func getImageAvatarLoaded(fileName: String) -> UIImage? {
+    func getImageAvatarLoaded(fileName: String) -> (image: UIImage?, tableAvatar: tableAvatar?) {
         let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
+        let image = UIImage(contentsOfFile: fileNameLocalPath)
 
         do {
             let realm = try Realm()
@@ -113,16 +114,13 @@ extension NCManageDatabase {
             let result = realm.objects(tableAvatar.self).filter("fileName == %@", fileName).first
             if result == nil {
                 utilityFileSystem.removeFile(atPath: fileNameLocalPath)
-                return nil
-            } else if result?.loaded == false {
-                return nil
             }
-            return UIImage(contentsOfFile: fileNameLocalPath)
+            return (image, result)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
 
         utilityFileSystem.removeFile(atPath: fileNameLocalPath)
-        return nil
+        return (nil, nil)
     }
 }

+ 63 - 50
iOSClient/Data/NCManageDatabase+Capabilities.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
@@ -62,7 +63,8 @@ extension NCManageDatabase {
         return nil
     }
 
-    func setCapabilities(account: String, data: Data? = nil) {
+    @discardableResult
+    func setCapabilities(account: String, data: Data? = nil) -> NCCapabilities.Capabilities? {
         let jsonData: Data?
 
         struct CapabilityNextcloud: Codable {
@@ -285,97 +287,108 @@ extension NCManageDatabase {
             let ocs: Ocs
         }
 
-        if let data = data {
+        if let data {
             jsonData = data
         } else {
             do {
                 let realm = try Realm()
                 guard let result = realm.objects(tableCapabilities.self).filter("account == %@", account).first,
                       let data = result.jsondata else {
-                    return
+                    return nil
                 }
                 jsonData = data
             } catch let error as NSError {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
-                return
+                return nil
             }
         }
-        guard let jsonData = jsonData else { return }
+        guard let jsonData = jsonData else {
+            return nil
+        }
 
         do {
-            let global = NCGlobal.shared
             let json = try JSONDecoder().decode(CapabilityNextcloud.self, from: jsonData)
             let data = json.ocs.data
+            let capabilities = NCCapabilities.Capabilities()
 
-            global.capabilityServerVersion = data.version.string
-            global.capabilityServerVersionMajor = data.version.major
+            capabilities.capabilityServerVersion = data.version.string
+            capabilities.capabilityServerVersionMajor = data.version.major
 
-            if global.capabilityServerVersionMajor > 0 {
-                NextcloudKit.shared.setup(nextcloudVersion: global.capabilityServerVersionMajor)
+            if capabilities.capabilityServerVersionMajor > 0 {
+                NextcloudKit.shared.updateSession(account: account, nextcloudVersion: capabilities.capabilityServerVersionMajor)
             }
 
-            global.capabilityFileSharingApiEnabled = data.capabilities.filessharing?.apienabled ?? false
-            global.capabilityFileSharingDefaultPermission = data.capabilities.filessharing?.defaultpermissions ?? 0
-            global.capabilityFileSharingPubPasswdEnforced = data.capabilities.filessharing?.ncpublic?.password?.enforced ?? false
-            global.capabilityFileSharingPubExpireDateEnforced = data.capabilities.filessharing?.ncpublic?.expiredate?.enforced ?? false
-            global.capabilityFileSharingPubExpireDateDays = data.capabilities.filessharing?.ncpublic?.expiredate?.days ?? 0
-            global.capabilityFileSharingInternalExpireDateEnforced = data.capabilities.filessharing?.ncpublic?.expiredateinternal?.enforced ?? false
-            global.capabilityFileSharingInternalExpireDateDays = data.capabilities.filessharing?.ncpublic?.expiredateinternal?.days ?? 0
-            global.capabilityFileSharingRemoteExpireDateEnforced = data.capabilities.filessharing?.ncpublic?.expiredateremote?.enforced ?? false
-            global.capabilityFileSharingRemoteExpireDateDays = data.capabilities.filessharing?.ncpublic?.expiredateremote?.days ?? 0
-
-            global.capabilityThemingColor = data.capabilities.theming?.color ?? ""
-            global.capabilityThemingColorElement = data.capabilities.theming?.colorelement ?? ""
-            global.capabilityThemingColorText = data.capabilities.theming?.colortext ?? ""
-            global.capabilityThemingName = data.capabilities.theming?.name ?? ""
-            global.capabilityThemingSlogan = data.capabilities.theming?.slogan ?? ""
-
-            global.capabilityE2EEEnabled = data.capabilities.endtoendencryption?.enabled ?? false
-            global.capabilityE2EEApiVersion = data.capabilities.endtoendencryption?.apiversion ?? ""
-
-            global.capabilityRichDocumentsEnabled = json.ocs.data.capabilities.richdocuments?.directediting ?? false
-            global.capabilityRichDocumentsMimetypes.removeAll()
+            capabilities.capabilityFileSharingApiEnabled = data.capabilities.filessharing?.apienabled ?? false
+            capabilities.capabilityFileSharingDefaultPermission = data.capabilities.filessharing?.defaultpermissions ?? 0
+            capabilities.capabilityFileSharingPubPasswdEnforced = data.capabilities.filessharing?.ncpublic?.password?.enforced ?? false
+            capabilities.capabilityFileSharingPubExpireDateEnforced = data.capabilities.filessharing?.ncpublic?.expiredate?.enforced ?? false
+            capabilities.capabilityFileSharingPubExpireDateDays = data.capabilities.filessharing?.ncpublic?.expiredate?.days ?? 0
+            capabilities.capabilityFileSharingInternalExpireDateEnforced = data.capabilities.filessharing?.ncpublic?.expiredateinternal?.enforced ?? false
+            capabilities.capabilityFileSharingInternalExpireDateDays = data.capabilities.filessharing?.ncpublic?.expiredateinternal?.days ?? 0
+            capabilities.capabilityFileSharingRemoteExpireDateEnforced = data.capabilities.filessharing?.ncpublic?.expiredateremote?.enforced ?? false
+            capabilities.capabilityFileSharingRemoteExpireDateDays = data.capabilities.filessharing?.ncpublic?.expiredateremote?.days ?? 0
+
+            capabilities.capabilityThemingColor = data.capabilities.theming?.color ?? ""
+            capabilities.capabilityThemingColorElement = data.capabilities.theming?.colorelement ?? ""
+            capabilities.capabilityThemingColorText = data.capabilities.theming?.colortext ?? ""
+            capabilities.capabilityThemingName = data.capabilities.theming?.name ?? ""
+            capabilities.capabilityThemingSlogan = data.capabilities.theming?.slogan ?? ""
+
+            capabilities.capabilityE2EEEnabled = data.capabilities.endtoendencryption?.enabled ?? false
+            capabilities.capabilityE2EEApiVersion = data.capabilities.endtoendencryption?.apiversion ?? ""
+
+            capabilities.capabilityRichDocumentsEnabled = json.ocs.data.capabilities.richdocuments?.directediting ?? false
+            capabilities.capabilityRichDocumentsMimetypes.removeAll()
             if let mimetypes = data.capabilities.richdocuments?.mimetypes {
                 for mimetype in mimetypes {
-                    global.capabilityRichDocumentsMimetypes.append(mimetype)
+                    capabilities.capabilityRichDocumentsMimetypes.append(mimetype)
                 }
             }
 
-            global.capabilityAssistantEnabled = data.capabilities.assistant?.enabled ?? false
+            capabilities.capabilityAssistantEnabled = data.capabilities.assistant?.enabled ?? false
 
-            global.capabilityActivity.removeAll()
+            capabilities.capabilityActivity.removeAll()
             if let activities = data.capabilities.activity?.apiv2 {
                 for activity in activities {
-                    global.capabilityActivity.append(activity)
+                    capabilities.capabilityActivity.append(activity)
                 }
             }
 
-            global.capabilityNotification.removeAll()
+            capabilities.capabilityNotification.removeAll()
             if let notifications = data.capabilities.notifications?.ocsendpoints {
                 for notification in notifications {
-                    global.capabilityNotification.append(notification)
+                    capabilities.capabilityNotification.append(notification)
                 }
             }
 
-            global.capabilityFilesUndelete = data.capabilities.files?.undelete ?? false
-            global.capabilityFilesLockVersion = data.capabilities.files?.locking ?? ""
-            global.capabilityFilesComments = data.capabilities.files?.comments ?? false
-            global.capabilityFilesBigfilechunking = data.capabilities.files?.bigfilechunking ?? false
+            capabilities.capabilityFilesUndelete = data.capabilities.files?.undelete ?? false
+            capabilities.capabilityFilesLockVersion = data.capabilities.files?.locking ?? ""
+            capabilities.capabilityFilesComments = data.capabilities.files?.comments ?? false
+            capabilities.capabilityFilesBigfilechunking = data.capabilities.files?.bigfilechunking ?? false
 
-            global.capabilityUserStatusEnabled = data.capabilities.userstatus?.enabled ?? false
+            capabilities.capabilityUserStatusEnabled = data.capabilities.userstatus?.enabled ?? false
             if data.capabilities.external != nil {
-                global.capabilityExternalSites = true
+                capabilities.capabilityExternalSites = true
+            }
+            capabilities.capabilityGroupfoldersEnabled = data.capabilities.groupfolders?.hasGroupFolders ?? false
+
+            if capabilities.capabilityServerVersionMajor >= NCGlobal.shared.nextcloudVersion28 {
+                capabilities.isLivePhotoServerAvailable = true
             }
-            global.capabilityGroupfoldersEnabled = data.capabilities.groupfolders?.hasGroupFolders ?? false
-            global.capabilitySecurityGuardDiagnostics = data.capabilities.securityguard?.diagnostics ?? false
 
-            global.capabilityForbiddenFileNames = data.capabilities.files?.forbiddenFileNames ?? []
-            global.capabilityForbiddenFileNameBasenames = data.capabilities.files?.forbiddenFileNameBasenames ?? []
-            global.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters ?? []
-            global.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions ?? []
+            capabilities.capabilitySecurityGuardDiagnostics = data.capabilities.securityguard?.diagnostics ?? false
+
+            capabilities.capabilityForbiddenFileNames = data.capabilities.files?.forbiddenFileNames ?? []
+            capabilities.capabilityForbiddenFileNameBasenames = data.capabilities.files?.forbiddenFileNameBasenames ?? []
+            capabilities.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters ?? []
+            capabilities.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions ?? []
+
+            NCCapabilities.shared.appendCapabilities(account: account, capabilities: capabilities)
+
+            return capabilities
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
-            return
+            return nil
         }
     }
 }

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

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+Comments.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+DashboardWidget.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+DirectEditing.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 2 - 14
iOSClient/Data/NCManageDatabase+Directory.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
@@ -102,7 +103,7 @@ extension NCManageDatabase {
             let results = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl BEGINSWITH %@", account, serverUrl)
             for result in results {
                 self.deleteMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", result.account, result.serverUrl))
-                self.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", result.ocId))
+                self.deleteLocalFileOcId(result.ocId)
             }
             try realm.write {
                 realm.delete(results)
@@ -142,19 +143,6 @@ extension NCManageDatabase {
         }
     }
 
-    func cleanEtagDirectory(account: String, serverUrl: String) {
-        do {
-            let realm = try Realm()
-            try realm.write {
-                if let result = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first {
-                    result.etag = ""
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-        }
-    }
-
     func getTableDirectory(predicate: NSPredicate) -> tableDirectory? {
         do {
             let realm = try Realm()

+ 1 - 0
iOSClient/Data/NCManageDatabase+E2EE.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+ExternalSites.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+GPS.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+Groupfolders.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

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

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 5 - 2
iOSClient/Data/NCManageDatabase+LocalFile.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
@@ -100,11 +101,13 @@ extension NCManageDatabase {
         }
     }
 
-    func deleteLocalFile(predicate: NSPredicate) {
+    func deleteLocalFileOcId(_ ocId: String?) {
+        guard let ocId else { return }
+
         do {
             let realm = try Realm()
             try realm.write {
-                let results = realm.objects(tableLocalFile.self).filter(predicate)
+                let results = realm.objects(tableLocalFile.self).filter("ocId == %@", ocId)
                 realm.delete(results)
             }
         } catch let error {

+ 46 - 33
iOSClient/Data/NCManageDatabase+Metadata+Session.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
@@ -29,6 +30,7 @@ extension NCManageDatabase {
     func setMetadataSession(ocId: String,
                             newFileName: String? = nil,
                             session: String? = nil,
+                            sessionTaskIdentifier: Int? = nil,
                             sessionError: String? = nil,
                             selector: String? = nil,
                             status: Int? = nil,
@@ -46,6 +48,9 @@ extension NCManageDatabase {
                     if let session {
                         result.session = session
                     }
+                    if let sessionTaskIdentifier {
+                        result.sessionTaskIdentifier = sessionTaskIdentifier
+                    }
                     if let sessionError {
                         result.sessionError = sessionError
                         if sessionError.isEmpty {
@@ -76,31 +81,6 @@ extension NCManageDatabase {
         }
     }
 
-    func setMetadataSession(ocId: String,
-                            status: Int? = nil,
-                            taskIdentifier: Int? = nil) {
-        do {
-            let realm = try Realm()
-            try realm.write {
-                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
-                    if let status {
-                        result.status = status
-                        if status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload {
-                            result.sessionDate = Date()
-                        } else if status == NCGlobal.shared.metadataStatusNormal {
-                            result.sessionDate = nil
-                        }
-                    }
-                    if let taskIdentifier {
-                        result.sessionTaskIdentifier = taskIdentifier
-                    }
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-        }
-    }
-
     @discardableResult
     func setMetadatasSessionInWaitDownload(metadatas: [tableMetadata], session: String, selector: String, sceneIdentifier: String? = nil) -> tableMetadata? {
         if metadatas.isEmpty { return nil }
@@ -113,6 +93,7 @@ extension NCManageDatabase {
                     if let result = realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first {
                         result.sceneIdentifier = sceneIdentifier
                         result.session = session
+                        result.sessionTaskIdentifier = 0
                         result.sessionError = ""
                         result.sessionSelector = selector
                         result.status = NCGlobal.shared.metadataStatusWaitDownload
@@ -121,6 +102,7 @@ extension NCManageDatabase {
                     } else {
                         metadata.sceneIdentifier = sceneIdentifier
                         metadata.session = session
+                        metadata.sessionTaskIdentifier = 0
                         metadata.sessionError = ""
                         metadata.sessionSelector = selector
                         metadata.status = NCGlobal.shared.metadataStatusWaitDownload
@@ -137,17 +119,39 @@ extension NCManageDatabase {
         return metadataUpdated
     }
 
-    func clearMetadataSession(metadatas: Results<tableMetadata>) {
+    func clearMetadataSession(metadatas: [tableMetadata]) {
         do {
             let realm = try Realm()
             try realm.write {
                 for metadata in metadatas {
-                    metadata.sceneIdentifier = nil
-                    metadata.session = ""
-                    metadata.sessionError = ""
-                    metadata.sessionSelector = ""
-                    metadata.sessionDate = nil
-                    metadata.status = NCGlobal.shared.metadataStatusNormal
+                    if let result = realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first {
+                        result.sceneIdentifier = nil
+                        result.session = ""
+                        result.sessionTaskIdentifier = 0
+                        result.sessionError = ""
+                        result.sessionSelector = ""
+                        result.sessionDate = nil
+                        result.status = NCGlobal.shared.metadataStatusNormal
+                    }
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+        }
+    }
+
+    func clearMetadataSession(metadata: tableMetadata) {
+        do {
+            let realm = try Realm()
+            try realm.write {
+                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first {
+                    result.sceneIdentifier = nil
+                    result.session = ""
+                    result.sessionTaskIdentifier = 0
+                    result.sessionError = ""
+                    result.sessionSelector = ""
+                    result.sessionDate = nil
+                    result.status = NCGlobal.shared.metadataStatusNormal
                 }
             }
         } catch let error {
@@ -164,6 +168,12 @@ extension NCManageDatabase {
             try realm.write {
                 result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
                 result?.status = status
+
+                if status == NCGlobal.shared.metadataStatusNormal {
+                    result?.sessionDate = nil
+                } else {
+                    result?.sessionDate = Date()
+                }
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
@@ -184,6 +194,9 @@ extension NCManageDatabase {
         if serverUrl.hasSuffix("/") {
             serverUrl = String(serverUrl.dropLast())
         }
-        return NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "serverUrl == %@ AND fileName == %@ AND sessionTaskIdentifier == %d", serverUrl, fileName, sessionTaskIdentifier))
+        return getMetadata(predicate: NSPredicate(format: "serverUrl == %@ AND fileName == %@ AND sessionTaskIdentifier == %d",
+                                                  serverUrl,
+                                                  fileName,
+                                                  sessionTaskIdentifier))
     }
 }

+ 393 - 326
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -22,10 +22,11 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
-class tableMetadata: Object, NCUserBaseUrl {
+class tableMetadata: Object {
     override func isEqual(_ object: Any?) -> Bool {
         if let object = object as? tableMetadata,
            self.account == object.account,
@@ -35,6 +36,7 @@ class tableMetadata: Object, NCUserBaseUrl {
            self.fileName == object.fileName,
            self.fileNameView == object.fileNameView,
            self.date == object.date,
+           self.datePhotosOriginal == object.datePhotosOriginal,
            self.permissions == object.permissions,
            self.hasPreview == object.hasPreview,
            self.note == object.note,
@@ -47,6 +49,7 @@ class tableMetadata: Object, NCUserBaseUrl {
            self.latitude == object.latitude,
            self.longitude == object.longitude,
            self.altitude == object.altitude,
+           self.status == object.status,
            Array(self.tags).elementsEqual(Array(object.tags)),
            Array(self.shareType).elementsEqual(Array(object.shareType)),
            Array(self.sharePermissionsCloudMesh).elementsEqual(Array(object.sharePermissionsCloudMesh)) {
@@ -66,12 +69,14 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var creationDate = NSDate()
     @objc dynamic var dataFingerprint = ""
     @objc dynamic var date = NSDate()
+    @objc dynamic var datePhotosOriginal = NSDate()
     @objc dynamic var directory: Bool = false
     @objc dynamic var downloadURL = ""
     @objc dynamic var e2eEncrypted: Bool = false
     @objc dynamic var edited: Bool = false
     @objc dynamic var etag = ""
     @objc dynamic var etagResource = ""
+    let exifPhotos = List<NCKeyValue>()
     @objc dynamic var favorite: Bool = false
     @objc dynamic var fileId = ""
     @objc dynamic var fileName = ""
@@ -87,7 +92,7 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var name = "" // for unifiedSearch is the provider.id
     @objc dynamic var note = ""
     @objc dynamic var ocId = ""
-    @objc dynamic var ocIdTemp = ""
+    @objc dynamic var ocIdTransfer = ""
     @objc dynamic var ownerId = ""
     @objc dynamic var ownerDisplayName = ""
     @objc public var lock = false
@@ -99,12 +104,15 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc public var lockTimeOut: Date?
     @objc dynamic var path = ""
     @objc dynamic var permissions = ""
+    @objc dynamic var placePhotos: String?
     @objc dynamic var quotaUsedBytes: Int64 = 0
     @objc dynamic var quotaAvailableBytes: Int64 = 0
     @objc dynamic var resourceType = ""
     @objc dynamic var richWorkspace: String?
     @objc dynamic var sceneIdentifier: String?
     @objc dynamic var serverUrl = ""
+    @objc dynamic var serveUrlFileName = ""
+    @objc dynamic var serverUrlTo = ""
     @objc dynamic var session = ""
     @objc dynamic var sessionDate: Date?
     @objc dynamic var sessionError = ""
@@ -115,6 +123,7 @@ class tableMetadata: Object, NCUserBaseUrl {
     let shareType = List<Int>()
     @objc dynamic var size: Int64 = 0
     @objc dynamic var status: Int = 0
+    @objc dynamic var storeFlag: String?
     @objc dynamic var subline: String?
     let tags = List<String>()
     @objc dynamic var trashbinFileName = ""
@@ -178,6 +187,10 @@ extension tableMetadata {
         return classFile == NKCommon.TypeClassFile.audio.rawValue || classFile == NKCommon.TypeClassFile.video.rawValue
     }
 
+    var isImageOrVideo: Bool {
+        return classFile == NKCommon.TypeClassFile.image.rawValue || classFile == NKCommon.TypeClassFile.video.rawValue
+    }
+
     var isVideo: Bool {
         return classFile == NKCommon.TypeClassFile.video.rawValue
     }
@@ -232,28 +245,17 @@ extension tableMetadata {
         return !isDirectoryE2EE && directory && size == 0 && e2eEncrypted && NCKeychain().isEndToEndEnabled(account: account)
     }
 
-    var isWaitingTransfer: Bool {
-        status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploadError
-    }
-
-    var isInTransfer: Bool {
-        status == NCGlobal.shared.metadataStatusDownloading || status == NCGlobal.shared.metadataStatusUploading
-    }
-
-    var isTransferInForeground: Bool {
-        (status > 0 && (chunk > 0 || e2eEncrypted))
-    }
-
     var isDownload: Bool {
-        status == NCGlobal.shared.metadataStatusDownloading
+        status == NCGlobal.shared.metadataStatusWaitDownload || status == NCGlobal.shared.metadataStatusDownloading
     }
 
     var isUpload: Bool {
-        status == NCGlobal.shared.metadataStatusUploading
+        status == NCGlobal.shared.metadataStatusWaitUpload || status == NCGlobal.shared.metadataStatusUploading
     }
 
     @objc var isDirectoryE2EE: Bool {
-        NCUtilityFileSystem().isDirectoryE2EE(account: account, urlBase: urlBase, userId: userId, serverUrl: serverUrl)
+        let session = NCSession.Session(account: account, urlBase: urlBase, user: user, userId: userId)
+        return NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
     }
 
     var isDirectoryE2EETop: Bool {
@@ -273,7 +275,7 @@ extension tableMetadata {
     }
 
     var hasPreviewBorder: Bool {
-        !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStoragePreviewIconExists(ocId, etag: etag)
+        !isImage && !isAudioOrVideo && hasPreview && NCUtilityFileSystem().fileProviderStorageImageExists(ocId, etag: etag, ext: NCGlobal.shared.previewExt1024)
     }
 
     var isAvailableEditorView: Bool {
@@ -284,7 +286,7 @@ extension tableMetadata {
         let directEditingEditors = utility.editorsDirectEditing(account: account, contentType: contentType)
         let richDocumentEditor = utility.isTypeFileRichDocument(self)
 
-        if NCGlobal.shared.capabilityRichDocumentsEnabled && richDocumentEditor && directEditingEditors.isEmpty {
+        if NCCapabilities.shared.getCapabilities(account: account).capabilityRichDocumentsEnabled && richDocumentEditor && directEditingEditors.isEmpty {
             // RichDocument: Collabora
             return true
         } else if directEditingEditors.contains(NCGlobal.shared.editorText) || directEditingEditors.contains(NCGlobal.shared.editorOnlyoffice) {
@@ -295,7 +297,9 @@ extension tableMetadata {
     }
 
     var isAvailableRichDocumentEditorView: Bool {
-        guard (classFile == NKCommon.TypeClassFile.document.rawValue) && NCGlobal.shared.capabilityRichDocumentsEnabled && NextcloudKit.shared.isNetworkReachable() else { return false }
+        guard (classFile == NKCommon.TypeClassFile.document.rawValue),
+              NCCapabilities.shared.getCapabilities(account: account).capabilityRichDocumentsEnabled,
+              NextcloudKit.shared.isNetworkReachable() else { return false }
 
         if NCUtility().isTypeFileRichDocument(self) {
             return true
@@ -324,7 +328,7 @@ extension tableMetadata {
 
     // Return if is sharable
     func isSharable() -> Bool {
-        if !NCGlobal.shared.capabilityFileSharingApiEnabled || (NCGlobal.shared.capabilityE2EEEnabled && isDirectoryE2EE) {
+        if !NCCapabilities.shared.getCapabilities(account: account).capabilityFileSharingApiEnabled || (NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEEnabled && isDirectoryE2EE) {
             return false
         }
         return true
@@ -346,10 +350,23 @@ extension NCManageDatabase {
         }
         metadata.dataFingerprint = file.dataFingerprint
         metadata.date = file.date as NSDate
+        if let datePhotosOriginal = file.datePhotosOriginal {
+            metadata.datePhotosOriginal = datePhotosOriginal as NSDate
+        } else {
+            metadata.datePhotosOriginal = metadata.date
+        }
         metadata.directory = file.directory
         metadata.downloadURL = file.downloadURL
         metadata.e2eEncrypted = file.e2eEncrypted
         metadata.etag = file.etag
+        for dict in file.exifPhotos {
+            for (key, value) in dict {
+                let keyValue = NCKeyValue()
+                keyValue.key = key
+                keyValue.value = value
+                metadata.exifPhotos.append(keyValue)
+            }
+        }
         metadata.favorite = file.favorite
         metadata.fileId = file.fileId
         metadata.fileName = file.fileName
@@ -361,6 +378,7 @@ extension NCManageDatabase {
         metadata.name = file.name
         metadata.note = file.note
         metadata.ocId = file.ocId
+        metadata.ocIdTransfer = file.ocId
         metadata.ownerId = file.ownerId
         metadata.ownerDisplayName = file.ownerDisplayName
         metadata.lock = file.lock
@@ -372,11 +390,13 @@ extension NCManageDatabase {
         metadata.lockTimeOut = file.lockTimeOut
         metadata.path = file.path
         metadata.permissions = file.permissions
+        metadata.placePhotos = file.placePhotos
         metadata.quotaUsedBytes = file.quotaUsedBytes
         metadata.quotaAvailableBytes = file.quotaAvailableBytes
         metadata.richWorkspace = file.richWorkspace
         metadata.resourceType = file.resourceType
         metadata.serverUrl = file.serverUrl
+        metadata.serveUrlFileName = file.serverUrl + "/" + file.fileName
         metadata.sharePermissionsCollaborationServices = file.sharePermissionsCollaborationServices
         for element in file.sharePermissionsCloudMesh {
             metadata.sharePermissionsCloudMesh.append(element)
@@ -411,9 +431,9 @@ extension NCManageDatabase {
 
         // E2EE find the fileName for fileNameView
         if isDirectoryE2EE || file.e2eEncrypted {
-            if let tableE2eEncryption = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", file.account, file.serverUrl, file.fileName)) {
+            if let tableE2eEncryption = getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", file.account, file.serverUrl, file.fileName)) {
                 metadata.fileNameView = tableE2eEncryption.fileName
-                let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: file.contentType, directory: file.directory)
+                let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadata.fileNameView, mimeType: file.contentType, directory: file.directory, account: file.account)
                 metadata.contentType = results.mimeType
                 metadata.iconName = results.iconName
                 metadata.classFile = results.classFile
@@ -450,6 +470,14 @@ extension NCManageDatabase {
         completion(metadataFolder, metadatas)
     }
 
+    func getMetadataDirectoryFrom(files: [NKFile]) -> tableMetadata? {
+        guard let file = files.first else { return nil }
+        let isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(file: file)
+        let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+
+        return metadata
+    }
+
     func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool) async -> (metadataFolder: tableMetadata, metadatas: [tableMetadata]) {
         await withUnsafeContinuation({ continuation in
             convertFilesToMetadatas(files, useFirstAsMetadataFolder: useFirstAsMetadataFolder) { metadataFolder, metadatas in
@@ -458,7 +486,7 @@ extension NCManageDatabase {
         })
     }
 
-    func createMetadata(account: String, user: String, userId: String, fileName: String, fileNameView: String, ocId: String, serverUrl: String, urlBase: String, url: String, contentType: String, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil) -> tableMetadata {
+    func createMetadata(fileName: String, fileNameView: String, ocId: String, serverUrl: String, url: String, contentType: String, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil, directory: Bool = false, session: NCSession.Session, sceneIdentifier: String?) -> tableMetadata {
         let metadata = tableMetadata()
 
         if isUrl {
@@ -470,7 +498,7 @@ extension NCManageDatabase {
             }
             metadata.classFile = NKCommon.TypeClassFile.url.rawValue
         } else {
-            let (mimeType, classFile, iconName, _, _, _) = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: contentType, directory: false)
+            let (mimeType, classFile, iconName, _, _, _) = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileName, mimeType: contentType, directory: directory, account: session.account)
             metadata.contentType = mimeType
             metadata.iconName = iconName
             metadata.classFile = classFile
@@ -486,24 +514,27 @@ extension NCManageDatabase {
 
         let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
 
-        metadata.account = account
+        metadata.account = session.account
         metadata.creationDate = Date() as NSDate
         metadata.date = Date() as NSDate
+        metadata.directory = directory
         metadata.hasPreview = true
         metadata.etag = ocId
         metadata.fileName = fileName
         metadata.fileNameView = fileName
         metadata.name = name
         metadata.ocId = ocId
-        metadata.ocIdTemp = ocId
+        metadata.ocIdTransfer = ocId
         metadata.permissions = "RGDNVW"
         metadata.serverUrl = serverUrl
+        metadata.serveUrlFileName = serverUrl + "/" + fileName
         metadata.subline = subline
         metadata.uploadDate = Date() as NSDate
         metadata.url = url
-        metadata.urlBase = urlBase
-        metadata.user = user
-        metadata.userId = userId
+        metadata.urlBase = session.urlBase
+        metadata.user = session.user
+        metadata.userId = session.userId
+        metadata.sceneIdentifier = sceneIdentifier
 
         if !metadata.urlBase.isEmpty, metadata.serverUrl.hasPrefix(metadata.urlBase) {
             metadata.path = String(metadata.serverUrl.dropFirst(metadata.urlBase.count)) + "/"
@@ -511,38 +542,40 @@ extension NCManageDatabase {
         return metadata
     }
 
-    @discardableResult
-    func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
-        let result = tableMetadata(value: metadata)
+    func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
+        let permissions = NCPermissions()
+        var isShare = false
+        var isMounted = false
 
-        do {
-            let realm = try Realm()
-            try realm.write {
-                realm.add(result, update: .all)
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-            return nil
+        if metadataFolder != nil {
+            isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared)
+            isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted)
+        } else if let directory = getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+            isShare = metadata.permissions.contains(permissions.permissionShared) && !directory.permissions.contains(permissions.permissionShared)
+            isMounted = metadata.permissions.contains(permissions.permissionMounted) && !directory.permissions.contains(permissions.permissionMounted)
+        }
+
+        if isShare || isMounted {
+            return true
+        } else {
+            return false
         }
-        return tableMetadata(value: result)
     }
 
-    func addMetadatasWithoutUpdate(_ metadatas: [tableMetadata]) {
-        if metadatas.isEmpty { return }
+    // MARK: - Set
 
+    @discardableResult
+    func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
         do {
             let realm = try Realm()
-            for metadata in metadatas {
-                if realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first != nil {
-                    return
-                }
-                try realm.write {
-                    realm.add(metadata, update: .all)
-                }
+            try realm.write {
+                realm.add(tableMetadata(value: metadata), update: .all)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+            return nil
         }
+        return tableMetadata(value: metadata)
     }
 
     func addMetadatas(_ metadatas: [tableMetadata]) {
@@ -568,41 +601,79 @@ extension NCManageDatabase {
         }
     }
 
-    func deleteMetadata(results: Results<tableMetadata>) {
+    func deleteMetadataOcId(_ ocId: String?) {
+        guard let ocId else { return }
+
         do {
             let realm = try Realm()
             try realm.write {
+                let results = realm.objects(tableMetadata.self).filter("ocId == %@", ocId)
                 realm.delete(results)
             }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
     }
 
-    func moveMetadata(ocId: String, serverUrlTo: String) {
+    func deleteMetadatas(_ metadatas: [tableMetadata]) {
         do {
             let realm = try Realm()
             try realm.write {
-                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
-                    result.serverUrl = serverUrlTo
-                }
+                realm.delete(metadatas)
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
     }
 
-    func renameMetadata(fileNameTo: String, ocId: String) {
+    func renameMetadata(fileNameNew: String, ocId: String, status: Int = NCGlobal.shared.metadataStatusNormal) {
         do {
             let realm = try Realm()
             try realm.write {
                 if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
-                    let resultsType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameTo, mimeType: "", directory: result.directory)
-                    result.fileName = fileNameTo
-                    result.fileNameView = fileNameTo
+                    let fileNameView = result.fileNameView
+                    let fileIdMOV = result.livePhotoFile
+                    let directoryServerUrl = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileNameView)
+                    let resultsType = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: fileNameNew, mimeType: "", directory: result.directory, account: result.account)
+
+                    result.fileName = fileNameNew
+                    result.fileNameView = fileNameNew
                     result.iconName = resultsType.iconName
                     result.contentType = resultsType.mimeType
                     result.classFile = resultsType.classFile
+                    result.status = status
+
+                    if status == NCGlobal.shared.metadataStatusNormal {
+                        result.sessionDate = nil
+                    } else {
+                        result.sessionDate = Date()
+                    }
+
+                    if result.directory,
+                       let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
+                        let serverUrlTo = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileNameNew)
+
+                        resultDirectory.serverUrl = serverUrlTo
+                    } else {
+                        let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameView
+                        let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameNew
+
+                        self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+                    }
+
+                    if result.isLivePhoto,
+                       let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
+                        let fileNameView = resultMOV.fileNameView
+                        let fileName = (fileNameNew as NSString).deletingPathExtension
+                        let ext = (resultMOV.fileName as NSString).pathExtension
+                        resultMOV.fileName = fileName + "." + ext
+                        resultMOV.fileNameView = fileName + "." + ext
+
+                        let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileNameView
+                        let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileName + "." + ext
+
+                        self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+                    }
                 }
             }
         } catch let error {
@@ -610,26 +681,79 @@ extension NCManageDatabase {
         }
     }
 
-    func setMetadataEtagResource(ocId: String, etagResource: String?) {
-        guard let etagResource = etagResource else { return }
+    func restoreMetadataFileName(ocId: String) {
+        do {
+            let realm = try Realm()
+            try realm.write {
+                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first,
+                   let encodedURLString = result.serveUrlFileName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
+                   let url = URL(string: encodedURLString) {
+                    let fileIdMOV = result.livePhotoFile
+                    let directoryServerUrl = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: result.fileNameView)
+                    let lastPathComponent = url.lastPathComponent
+                    let fileName = lastPathComponent.removingPercentEncoding ?? lastPathComponent
+                    let fileNameView = result.fileNameView
+
+                    result.fileName = fileName
+                    result.fileNameView = fileName
+                    result.status = NCGlobal.shared.metadataStatusNormal
+                    result.sessionDate = nil
+
+                    if result.directory,
+                       let resultDirectory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", result.account, directoryServerUrl).first {
+                        let serverUrlTo = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: fileName)
+
+                        resultDirectory.serverUrl = serverUrlTo
+                    } else {
+                        let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileNameView
+                        let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(result.ocId) + "/" + fileName
+
+                        self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+                    }
+
+                    if result.isLivePhoto,
+                       let resultMOV = realm.objects(tableMetadata.self).filter("fileId == %@ AND account == %@", fileIdMOV, result.account).first {
+                        let fileNameView = resultMOV.fileNameView
+                        let fileName = (fileName as NSString).deletingPathExtension
+                        let ext = (resultMOV.fileName as NSString).pathExtension
+                        resultMOV.fileName = fileName + "." + ext
+                        resultMOV.fileNameView = fileName + "." + ext
+
+                        let atPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileNameView
+                        let toPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(resultMOV.ocId) + "/" + fileName + "." + ext
+
+                        self.utilityFileSystem.moveFile(atPath: atPath, toPath: toPath)
+                    }
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+        }
+    }
 
+    func setMetadataServeUrlFileNameStatusNormal(ocId: String) {
         do {
             let realm = try Realm()
             try realm.write {
-                let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
-                result?.etagResource = etagResource
+                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+                    result.serveUrlFileName = self.utilityFileSystem.stringAppendServerUrl(result.serverUrl, addFileName: result.fileName)
+                    result.status = NCGlobal.shared.metadataStatusNormal
+                    result.sessionDate = nil
+                }
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
     }
 
-    func setMetadataFavorite(ocId: String, favorite: Bool) {
+    func setMetadataEtagResource(ocId: String, etagResource: String?) {
+        guard let etagResource else { return }
+
         do {
             let realm = try Realm()
             try realm.write {
                 let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
-                result?.favorite = favorite
+                result?.etagResource = etagResource
             }
         } catch let error {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
@@ -671,6 +795,24 @@ extension NCManageDatabase {
         }
     }
 
+    func updateMetadatasFiles(_ metadatas: [tableMetadata], serverUrl: String, account: String) {
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let results = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal))
+                realm.delete(results)
+                for metadata in metadatas {
+                    if realm.objects(tableMetadata.self).filter(NSPredicate(format: "ocId == %@ AND status != %d", metadata.ocId, NCGlobal.shared.metadataStatusNormal)).first != nil {
+                        continue
+                    }
+                    realm.add(tableMetadata(value: metadata), update: .all)
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
+        }
+    }
+
     func setMetadataEncrypted(ocId: String, encrypted: Bool) {
         do {
             let realm = try Realm()
@@ -695,82 +837,105 @@ extension NCManageDatabase {
         }
     }
 
-    func getMetadata(predicate: NSPredicate) -> tableMetadata? {
+    func moveMetadata(ocId: String, serverUrlTo: String) {
         do {
             let realm = try Realm()
-            realm.refresh()
-            guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
-            return tableMetadata(value: result)
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+            try realm.write {
+                if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
+                    result.serverUrl = serverUrlTo
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-
-        return nil
     }
 
-    func getMetadata(predicate: NSPredicate, sorted: String, ascending: Bool) -> tableMetadata? {
+    func clearAssetLocalIdentifiers(_ assetLocalIdentifiers: [String]) {
         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(value: result)
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+            try realm.write {
+                let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier IN %@", assetLocalIdentifiers)
+                for result in results {
+                    result.assetLocalIdentifier = ""
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-        return nil
     }
 
-    func getMetadatas(predicate: NSPredicate) -> [tableMetadata] {
+    func setMetadataFavorite(ocId: String, favorite: Bool?, saveOldFavorite: String?, status: Int) {
         do {
             let realm = try Realm()
-            realm.refresh()
-            let results = realm.objects(tableMetadata.self).filter(predicate)
-            return Array(results.map { tableMetadata(value: $0) })
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+            try realm.write {
+                let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+                if let favorite {
+                    result?.favorite = favorite
+                }
+                result?.storeFlag = saveOldFavorite
+                result?.status = status
+
+                if status == NCGlobal.shared.metadataStatusNormal {
+                    result?.sessionDate = nil
+                } else {
+                    result?.sessionDate = Date()
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-        return []
     }
 
-    func getMetadatas(predicate: NSPredicate, sorted: String, ascending: Bool = false) -> [tableMetadata]? {
+    func setMetadataCopyMove(ocId: String, serverUrlTo: String, overwrite: String?, status: Int) {
         do {
             let realm = try Realm()
-            let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
-            return Array(results.map { tableMetadata(value: $0) })
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+            try realm.write {
+                let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+                result?.serverUrlTo = serverUrlTo
+                result?.storeFlag = overwrite
+                result?.status = status
+
+                if status == NCGlobal.shared.metadataStatusNormal {
+                    result?.sessionDate = nil
+                } else {
+                    result?.sessionDate = Date()
+                }
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
         }
-        return nil
     }
 
-    func getResultsMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> Results<tableMetadata>? {
+    // MARK: - GetMetadata
+
+    func getMetadata(predicate: NSPredicate) -> tableMetadata? {
         do {
             let realm = try Realm()
-            if let sorted {
-                return realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
-            } else {
-                return realm.objects(tableMetadata.self).filter(predicate)
-            }
+            guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
+            return tableMetadata(value: result)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
+
         return nil
     }
 
-    func getResultsMetadatas(predicate: NSPredicate, sorted: [RealmSwift.SortDescriptor]) -> Results<tableMetadata>? {
+    func getMetadatas(predicate: NSPredicate) -> [tableMetadata] {
         do {
             let realm = try Realm()
-            return realm.objects(tableMetadata.self).filter(predicate).sorted(by: sorted)
+            let results = realm.objects(tableMetadata.self).filter(predicate)
+            return Array(results.map { tableMetadata(value: $0) })
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-        return nil
+        return []
     }
 
-    func getResultMetadata(predicate: NSPredicate) -> tableMetadata? {
+    func getMetadatas(predicate: NSPredicate, sortedByKeyPath: String, ascending: Bool = false) -> [tableMetadata]? {
         do {
             let realm = try Realm()
-            return realm.objects(tableMetadata.self).filter(predicate).first
+            let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending)
+            return Array(results.map { tableMetadata(value: $0) })
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
@@ -783,7 +948,6 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
-            realm.refresh()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             for result in results where counter < numItems {
                 metadatas.append(tableMetadata(value: result))
@@ -795,28 +959,11 @@ extension NCManageDatabase {
         return metadatas
     }
 
-    func getMetadataAtIndex(predicate: NSPredicate, sorted: String, ascending: Bool, index: Int) -> tableMetadata? {
-        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
-            } else {
-                return tableMetadata(value: results[index])
-            }
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
-        }
-        return nil
-    }
-
     func getMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
         guard let ocId else { return nil }
 
         do {
             let realm = try Realm()
-            realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
             return tableMetadata(value: result)
         } catch let error as NSError {
@@ -825,16 +972,15 @@ extension NCManageDatabase {
         return nil
     }
 
-    func getMetadataFromOcIdAndOcIdTemp(_ ocId: String?) -> tableMetadata? {
+    func getMetadataFromOcIdAndocIdTransfer(_ ocId: String?) -> tableMetadata? {
         guard let ocId else { return nil }
 
         do {
             let realm = try Realm()
-            realm.refresh()
             if let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first {
                 return tableMetadata(value: result)
             }
-            if let result = realm.objects(tableMetadata.self).filter("ocIdTemp == %@", ocId).first {
+            if let result = realm.objects(tableMetadata.self).filter("ocIdTransfer == %@", ocId).first {
                 return tableMetadata(value: result)
             }
         } catch let error as NSError {
@@ -843,23 +989,40 @@ extension NCManageDatabase {
         return nil
     }
 
-    func getResultMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
-        guard let ocId else { return nil }
+    func getMetadataFolder(session: NCSession.Session, serverUrl: String) -> tableMetadata? {
+        var serverUrl = serverUrl
+        var fileName = ""
+        let serverUrlHome = utilityFileSystem.getHomeServer(session: session)
+
+        if serverUrlHome == serverUrl {
+            fileName = "."
+            serverUrl = ".."
+        } else {
+            fileName = (serverUrl as NSString).lastPathComponent
+            if let path = utilityFileSystem.deleteLastPath(serverUrlPath: serverUrl) {
+                serverUrl = path
+            }
+        }
 
         do {
             let realm = try Realm()
-            return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+            guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", session.account, serverUrl, fileName).first else { return nil }
+            return tableMetadata(value: result)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
 
-    func getMetadataFromFileName(_ fileName: String, serverUrl: String) -> tableMetadata? {
+    func getMetadataLivePhoto(metadata: tableMetadata) -> tableMetadata? {
+        guard metadata.isLivePhoto else { return nil }
+
         do {
             let realm = try Realm()
-            realm.refresh()
-            guard let result = realm.objects(tableMetadata.self).filter("fileName == %@ AND serverUrl == %@", fileName, serverUrl).first else { return nil }
+            guard let result = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@",
+                                                                                    metadata.account,
+                                                                                    metadata.serverUrl,
+                                                                                    metadata.livePhotoFile)).first else { return nil }
             return tableMetadata(value: result)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
@@ -867,70 +1030,101 @@ extension NCManageDatabase {
         return nil
     }
 
-    func getMetadataFromFileNameLocalPath(_ fileNameLocalPath: String?) -> tableMetadata? {
-        let components = fileNameLocalPath?.components(separatedBy: "/")
+    func getMetadataConflict(account: String, serverUrl: String, fileNameView: String) -> tableMetadata? {
+        let fileNameExtension = (fileNameView as NSString).pathExtension.lowercased()
+        let fileNameNoExtension = (fileNameView as NSString).deletingPathExtension
+        var fileNameConflict = fileNameView
 
-        if let count = components?.count,
-           components?.count ?? 0 > 2,
-           let ocId = components?[count - 2] {
-            do {
-                let realm = try Realm()
-                realm.refresh()
-                guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
-                return tableMetadata(value: result)
-            } catch let error as NSError {
-                NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+        if fileNameExtension == "heic", NCKeychain().formatCompatibility {
+            fileNameConflict = fileNameNoExtension + ".jpg"
+        }
+        return getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@",
+                                                  account,
+                                                  serverUrl,
+                                                  fileNameConflict))
+    }
+
+    // MARK: - GetResult(s)Metadata
+
+    func getResultsMetadatasPredicate(_ predicate: NSPredicate, layoutForView: NCDBLayoutForView?) -> Results<tableMetadata>? {
+        do {
+            let realm = try Realm()
+            var results = realm.objects(tableMetadata.self).filter(predicate)
+            if let layoutForView {
+                if layoutForView.directoryOnTop {
+                    results = results.sorted(byKeyPath: layoutForView.sort, ascending: layoutForView.ascending).sorted(byKeyPath: "directory", ascending: false).sorted(byKeyPath: "favorite", ascending: false)
+                } else {
+                    results = results.sorted(byKeyPath: layoutForView.sort, ascending: layoutForView.ascending).sorted(byKeyPath: "favorite", ascending: false)
+                }
             }
+            return results
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
 
-    func getTableMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
-        guard let ocId else { return nil }
+    func getResultsMetadatas(predicate: NSPredicate, sortedByKeyPath: String, ascending: Bool, arraySlice: Int) -> [tableMetadata] {
+        do {
+            let realm = try Realm()
+            let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending).prefix(arraySlice)
+            return Array(results)
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
+        }
+        return []
+    }
 
+    func getResultMetadata(predicate: NSPredicate) -> tableMetadata? {
         do {
             let realm = try Realm()
-            realm.refresh()
-            return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
+            return realm.objects(tableMetadata.self).filter(predicate).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
 
-    func getMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
+    func getResultMetadataFromFileName(_ fileName: String, serverUrl: String, sessionTaskIdentifier: Int) -> 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(value: result)
+            return realm.objects(tableMetadata.self).filter("fileName == %@ AND serverUrl == %@ AND sessionTaskIdentifier == %d", fileName, serverUrl, sessionTaskIdentifier).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
 
-    func getMetadataFolder(account: String, urlBase: String, userId: String, serverUrl: String) -> tableMetadata? {
-        var serverUrl = serverUrl
-        var fileName = ""
-        let serverUrlHome = utilityFileSystem.getHomeServer(urlBase: urlBase, userId: userId)
+    func getResultsMetadatasFromGroupfolders(session: NCSession.Session) -> Results<tableMetadata>? {
+        var ocId: [String] = []
+        let homeServerUrl = utilityFileSystem.getHomeServer(session: session)
 
-        if serverUrlHome == serverUrl {
-            fileName = "."
-            serverUrl = ".."
-        } else {
-            fileName = (serverUrl as NSString).lastPathComponent
-            if let path = utilityFileSystem.deleteLastPath(serverUrlPath: serverUrl) {
-                serverUrl = path
+        do {
+            let realm = try Realm()
+            let groupfolders = realm.objects(TableGroupfolders.self).filter("account == %@", session.account).sorted(byKeyPath: "mountPoint", ascending: true)
+
+            for groupfolder in groupfolders {
+                let mountPoint = groupfolder.mountPoint.hasPrefix("/") ? groupfolder.mountPoint : "/" + groupfolder.mountPoint
+                let serverUrlFileName = homeServerUrl + mountPoint
+
+                if let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", session.account, serverUrlFileName).first,
+                   let result = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first {
+                    ocId.append(result.ocId)
+                }
             }
+
+            return realm.objects(tableMetadata.self).filter("ocId IN %@", ocId)
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
 
+        return nil
+    }
+
+    func getResultsImageCacheMetadatas(predicate: NSPredicate) -> Results<tableMetadata>? {
         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(value: result)
+            return realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
@@ -943,7 +1137,6 @@ 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
@@ -955,7 +1148,7 @@ extension NCManageDatabase {
         return listIdentifierRank
     }
 
-    @objc func clearMetadatasUpload(account: String) {
+    func clearMetadatasUpload(account: String) {
         do {
             let realm = try Realm()
             try realm.write {
@@ -967,26 +1160,12 @@ extension NCManageDatabase {
         }
     }
 
-    func readMarkerMetadata(account: String, fileId: String) {
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let results = realm.objects(tableMetadata.self).filter("account == %@ AND fileId == %@", account, fileId)
-                for result in results {
-                    result.commentsUnread = false
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-        }
-    }
-
-    func getAssetLocalIdentifiersUploaded(account: String) -> [String]? {
+    func getAssetLocalIdentifiersUploaded() -> [String]? {
         var assetLocalIdentifiers: [String] = []
 
         do {
             let realm = try Realm()
-            let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != ''", account)
+            let results = realm.objects(tableMetadata.self).filter("assetLocalIdentifier != ''")
             for result in results {
                 assetLocalIdentifiers.append(result.assetLocalIdentifier)
             }
@@ -997,179 +1176,67 @@ extension NCManageDatabase {
         return nil
     }
 
-    func clearAssetLocalIdentifiers(_ assetLocalIdentifiers: [String], account: String) {
-        do {
-            let realm = try Realm()
-            try realm.write {
-                let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier IN %@", account, assetLocalIdentifiers)
-                for result in results {
-                    result.assetLocalIdentifier = ""
-                }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-        }
-    }
-
-    func getMetadataLivePhoto(metadata: tableMetadata) -> tableMetadata? {
-        guard metadata.isLivePhoto else { return nil }
-
+    func getMetadataFromDirectory(account: String, serverUrl: String) -> Bool {
         do {
             let realm = try Realm()
-            guard let result = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@", metadata.account, metadata.serverUrl, metadata.livePhotoFile)).first else { return nil }
-            return tableMetadata(value: result)
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
-        }
-        return nil
-    }
-
-    func isMetadataShareOrMounted(metadata: tableMetadata, metadataFolder: tableMetadata?) -> Bool {
-        let permissions = NCPermissions()
-        var isShare = false
-        var isMounted = false
-
-        if metadataFolder != nil {
-            isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared)
-            isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted)
-        } else if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
-            isShare = metadata.permissions.contains(permissions.permissionShared) && !directory.permissions.contains(permissions.permissionShared)
-            isMounted = metadata.permissions.contains(permissions.permissionMounted) && !directory.permissions.contains(permissions.permissionMounted)
-        }
-
-        if isShare || isMounted {
+            guard let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first,
+                  realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first != nil else { return false }
             return true
-        } else {
-            return false
-        }
-    }
-
-    func getMetadataConflict(account: String, serverUrl: String, fileNameView: String) -> tableMetadata? {
-        // verify exists conflict
-        let fileNameExtension = (fileNameView as NSString).pathExtension.lowercased()
-        let fileNameNoExtension = (fileNameView as NSString).deletingPathExtension
-        var fileNameConflict = fileNameView
-
-        if fileNameExtension == "heic", NCKeychain().formatCompatibility {
-            fileNameConflict = fileNameNoExtension + ".jpg"
-        }
-        return getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", account, serverUrl, fileNameConflict))
-    }
-
-    func getNumMetadatasInUpload() -> Int {
-        do {
-            let realm = try Realm()
-            realm.refresh()
-            return realm.objects(tableMetadata.self).filter(NSPredicate(format: "status == %i", NCGlobal.shared.metadataStatusUploading)).count
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
-        return 0
+        return false
     }
 
-    func getMetadataFromDirectory(account: String, serverUrl: String) -> tableMetadata? {
+    func getResultMetadataFromFileId(_ fileId: String?) -> tableMetadata? {
+        guard let fileId else { return nil }
+
         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(value: result)
+            return realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
 
-    func getMetadatasFromGroupfolders(account: String, urlBase: String, userId: String) -> [tableMetadata] {
-        var metadatas: [tableMetadata] = []
-        let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: urlBase, userId: userId)
-
-        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
-                let serverUrlFileName = homeServerUrl + mountPoint
-                if let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrlFileName).first,
-                   let metadata = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first {
-                    metadatas.append(tableMetadata(value: metadata))
-                }
-            }
-        } catch let error as NSError {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
-        }
-        return metadatas
-    }
+    func getResultMetadataFromOcId(_ ocId: String?) -> tableMetadata? {
+        guard let ocId else { return nil }
 
-    func getMetadatasInError(account: String) -> Results<tableMetadata>? {
         do {
             let realm = try Realm()
-            let results = realm.objects(tableMetadata.self).filter("account == %@ AND errorCodeCounter > 1", account)
-            return results
+            return realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
 
-    @discardableResult
-    func updateMetadatas(_ metadatas: [tableMetadata], predicate: NSPredicate) -> (metadatasDifferentCount: Int, metadatasModified: Int) {
-        var metadatasDifferentCount: Int = 0
-        var metadatasModified: Int = 0
-
+    func getResultsMetadatas(predicate: NSPredicate, sortedByKeyPath: String? = nil, ascending: Bool = false, freeze: Bool = false) -> Results<tableMetadata>? {
         do {
             let realm = try Realm()
-            try realm.write {
-                let results = realm.objects(tableMetadata.self).filter(predicate)
-                metadatasDifferentCount = metadatas.count - results.count
-                for metadata in metadatas {
-                    if let result = results.first(where: { $0.ocId == metadata.ocId }) {
-                        // before realm.add copy the value not available from server
-                        metadata.assetLocalIdentifier = result.assetLocalIdentifier
-                        if !metadata.isEqual(result) { metadatasModified += 1 }
-                    }
-                }
-                if metadatasDifferentCount != 0 || metadatasModified > 0 {
-                    realm.delete(results)
-                    for metadata in metadatas {
-                        realm.add(tableMetadata(value: metadata), update: .all)
-                    }
+            realm.refresh()
+            if let sortedByKeyPath {
+                let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sortedByKeyPath, ascending: ascending)
+                if freeze {
+                    return results.freeze()
                 }
-            }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-        }
-        return (metadatasDifferentCount, metadatasModified)
-    }
-
-    func replaceMetadata(_ metadatas: [tableMetadata], predicate: NSPredicate) {
-        do {
-            let realm = try Realm()
-            try realm.write {
+                return results
+            } else {
                 let results = realm.objects(tableMetadata.self).filter(predicate)
-                realm.delete(results)
-                for metadata in metadatas {
-                    if results.where({ $0.ocId == metadata.ocId }).isEmpty {
-                        realm.add(tableMetadata(value: metadata), update: .modified)
-                    } else {
-                        continue
-                    }
+                if freeze {
+                    return results.freeze()
                 }
+                return results
             }
-        } catch let error {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
-        }
-    }
-
-    func getMediaMetadatas(predicate: NSPredicate) -> ThreadSafeArray<tableMetadata>? {
-        do {
-            let realm = try Realm()
-            let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
-            return ThreadSafeArray(results.map { tableMetadata(value: $0) })
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         return nil
     }
+
+    func getCalculateCumulativeHash(for metadatas: [tableMetadata], account: String, serverUrl: String) -> String {
+        let concatenatedEtags = metadatas.map { $0.etag }.joined(separator: "-")
+        return sha256Hash(concatenatedEtags)
+    }
 }

+ 1 - 0
iOSClient/Data/NCManageDatabase+SecurityGuard.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+Share.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+Tag.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 1 - 0
iOSClient/Data/NCManageDatabase+Tip.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 6 - 7
iOSClient/Data/NCManageDatabase+Trash.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 
@@ -113,24 +114,22 @@ extension NCManageDatabase {
         }
     }
 
-    func getTrash(filePath: String, account: String) -> [tableTrash] {
+    func getResultsTrash(filePath: String, account: String) -> Results<tableTrash>? {
         do {
             let realm = try Realm()
             realm.refresh()
-            let results = realm.objects(tableTrash.self).filter("account == %@ AND filePath == %@", account, filePath).sorted(byKeyPath: "trashbinDeletionTime", ascending: false)
-            return Array(results.map { tableTrash.init(value: $0) })
+            return realm.objects(tableTrash.self).filter("account == %@ AND filePath == %@", account, filePath).sorted(byKeyPath: "trashbinDeletionTime", ascending: false)
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }
-        return []
+        return nil
     }
 
-    func getTrashItem(fileId: String, account: String) -> tableTrash? {
+    func getResultTrashItem(fileId: String, account: String) -> tableTrash? {
         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)
+            return realm.objects(tableTrash.self).filter("account == %@ AND fileId == %@", account, fileId).first
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
         }

+ 1 - 0
iOSClient/Data/NCManageDatabase+UserStatus.swift

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

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

@@ -22,6 +22,7 @@
 //
 
 import Foundation
+import UIKit
 import RealmSwift
 import NextcloudKit
 

+ 45 - 16
iOSClient/Data/NCManageDatabase.swift

@@ -27,6 +27,7 @@ import RealmSwift
 import NextcloudKit
 import CoreMedia
 import Photos
+import CommonCrypto
 
 protocol DateCompareable {
     var dateKey: Date { get }
@@ -41,17 +42,20 @@ class NCManageDatabase: NSObject {
 
     override init() {
         func migrationSchema(_ migration: Migration, _ oldSchemaVersion: UInt64) {
-            if oldSchemaVersion < 354 {
-                migration.deleteData(forType: NCDBLayoutForView.className())
+            if oldSchemaVersion < 365 {
+                migration.deleteData(forType: tableMetadata.className())
+                migration.enumerateObjects(ofType: tableDirectory.className()) { _, newObject in
+                    newObject?["etag"] = ""
+                }
             }
         }
 
         func compactDB(_ totalBytes: Int, _ usedBytes: Int) -> Bool {
-            // totalBytes refers to the size of the file on disk in bytes (data + free space)
-            // usedBytes refers to the number of bytes used by data in the file
-            // Compact if the file is over 100MB in size and less than 50% 'used'
-            let oneHundredMB = 100 * 1024 * 1024
-            return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
+            let usedPercentage = (Double(usedBytes) / Double(totalBytes)) * 100
+            /// Compact the database if more than 25% of the space is free
+            let shouldCompact = (usedPercentage < 75.0) && (totalBytes > 100 * 1024 * 1024)
+
+            return shouldCompact
         }
         var realm: Realm?
         let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
@@ -60,7 +64,8 @@ class NCManageDatabase: NSObject {
         let bundlePathExtension: String = bundleUrl.pathExtension
         let bundleFileName: String = (bundleUrl.path as NSString).lastPathComponent
         let isAppex: Bool = bundlePathExtension == "appex"
-        var objectTypesAppex = [tableMetadata.self,
+        var objectTypesAppex = [NCKeyValue.self,
+                                tableMetadata.self,
                                 tableLocalFile.self,
                                 tableDirectory.self,
                                 tableTag.self,
@@ -94,12 +99,14 @@ class NCManageDatabase: NSObject {
 
         if isAppex {
             if bundleFileName == "File Provider Extension.appex" {
-                objectTypesAppex = [tableMetadata.self,
+                objectTypesAppex = [NCKeyValue.self,
+                                    tableMetadata.self,
                                     tableLocalFile.self,
                                     tableDirectory.self,
                                     tableTag.self,
                                     tableAccount.self,
-                                    tableCapabilities.self]
+                                    tableCapabilities.self,
+                                    tableE2eEncryption.self]
             }
             do {
                 Realm.Configuration.defaultConfiguration =
@@ -145,7 +152,6 @@ class NCManageDatabase: NSObject {
             let realm = try Realm()
             try realm.write {
                 var results: Results<Object>
-
                 if let account = account {
                     results = realm.objects(table).filter("account == %@", account)
                 } else {
@@ -159,7 +165,7 @@ class NCManageDatabase: NSObject {
         }
     }
 
-    func clearDatabase(account: String?, removeAccount: Bool) {
+    func clearDatabase(account: String? = nil, removeAccount: Bool = false) {
         if removeAccount {
             self.clearTable(tableAccount.self, account: account)
         }
@@ -216,6 +222,24 @@ class NCManageDatabase: NSObject {
         return nil
     }
 
+    func realmRefresh() {
+        do {
+            let realm = try Realm()
+            realm.refresh()
+        } catch let error as NSError {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not refresh database: \(error)")
+        }
+    }
+
+    func sha256Hash(_ input: String) -> String {
+        let data = Data(input.utf8)
+        var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
+        data.withUnsafeBytes {
+            _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &digest)
+        }
+        return digest.map { String(format: "%02hhx", $0) }.joined()
+    }
+
     // MARK: -
     // MARK: Func T
 
@@ -250,17 +274,22 @@ class NCManageDatabase: NSObject {
         /// Account
         let account = "marinofaggiana https://cloudtest.nextcloud.com"
         let account2 = "mariorossi https://cloudtest.nextcloud.com"
-        NCManageDatabase.shared.addAccount(account, urlBase: "https://cloudtest.nextcloud.com", user: "marinofaggiana", userId: "marinofaggiana", password: "password")
-        NCManageDatabase.shared.addAccount(account2, urlBase: "https://cloudtest.nextcloud.com", user: "mariorossi", userId: "mariorossi", password: "password")
+        addAccount(account, urlBase: "https://cloudtest.nextcloud.com", user: "marinofaggiana", userId: "marinofaggiana", password: "password")
+        addAccount(account2, urlBase: "https://cloudtest.nextcloud.com", user: "mariorossi", userId: "mariorossi", password: "password")
         let userProfile = NKUserProfile()
         userProfile.displayName = "Marino Faggiana"
         userProfile.address = "Hirschstrasse 26, 70192 Stuttgart, Germany"
         userProfile.phone = "+49 (711) 252 428 - 90"
         userProfile.email = "cloudtest@nextcloud.com"
-        NCManageDatabase.shared.setAccountUserProfile(account: account, userProfile: userProfile)
+        setAccountUserProfile(account: account, userProfile: userProfile)
         let userProfile2 = NKUserProfile()
         userProfile2.displayName = "Mario Rossi"
         userProfile2.email = "cloudtest@nextcloud.com"
-        NCManageDatabase.shared.setAccountUserProfile(account: account2, userProfile: userProfile2)
+        setAccountUserProfile(account: account2, userProfile: userProfile2)
     }
 }
+
+class NCKeyValue: Object {
+    @Persisted var key: String = ""
+    @Persisted var value: String?
+}

+ 4 - 2
iOSClient/DeepLink/NCDeepLinkHandler.swift

@@ -103,6 +103,7 @@ class NCDeepLinkHandler {
         controller.selectedIndex = ControllerConstants.filesIndex
         guard let navigationController = controller.viewControllers?[controller.selectedIndex] as? UINavigationController,
               let viewController = UIStoryboard(name: ControllerConstants.notification, bundle: nil).instantiateInitialViewController() as? NCNotification else { return }
+        viewController.session = NCSession.shared.getSession(controller: controller)
         navigationController.pushViewController(viewController, animated: true)
     }
 
@@ -111,10 +112,11 @@ class NCDeepLinkHandler {
         controller.selectedIndex = ControllerConstants.filesIndex
         DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
             let serverUrl = controller.currentServerUrl()
-            let fileFolderPath = NCUtilityFileSystem().getFileNamePath("", serverUrl: serverUrl, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+            let session = NCSession.shared.getSession(controller: controller)
+            let fileFolderPath = NCUtilityFileSystem().getFileNamePath("", serverUrl: serverUrl, session: session)
             let fileFolderName = (serverUrl as NSString).lastPathComponent
 
-            if !FileNameValidator.shared.checkFolderPath(folderPath: fileFolderPath) {
+            if !FileNameValidator.shared.checkFolderPath(fileFolderPath, account: controller.account) {
                 controller.present(UIAlertController.warning(message: "\(String(format: NSLocalizedString("_file_name_validator_error_reserved_name_", comment: ""), fileFolderName)) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true)
 
                 return

+ 0 - 1
iOSClient/Extensions/NotificationCenter+MainThread.swift

@@ -25,7 +25,6 @@ import Foundation
 import UIKit
 
 extension NotificationCenter {
-
     func postOnMainThread(name: String, object anObject: Any? = nil, userInfo aUserInfo: [AnyHashable: Any]? = nil, second: Double = 0) {
         DispatchQueue.main.asyncAfter(deadline: .now() + second) {
             NotificationCenter.default.post(name: Notification.Name(rawValue: name), object: anObject, userInfo: aUserInfo)

+ 45 - 44
iOSClient/Extensions/UIAlertController+Extension.swift

@@ -32,16 +32,21 @@ extension UIAlertController {
     ///   - urlBase: UrlBase object
     ///   - completion: If not` nil` it overrides the default behavior which shows an error using `NCContentPresenter`
     /// - Returns: The presentable alert controller
-    static func createFolder(serverUrl: String, userBaseUrl: NCUserBaseUrl, markE2ee: Bool = false, sceneIdentifier: String? = nil, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController {
+    static func createFolder(serverUrl: String, account: String, markE2ee: Bool = false, sceneIdentifier: String? = nil, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController {
         let alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: nil, preferredStyle: .alert)
+        let session = NCSession.shared.getSession(account: account)
+        let isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
 
         let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
             guard let fileNameFolder = alertController.textFields?.first?.text else { return }
             if markE2ee {
+                if NCNetworking.shared.isOffline {
+                    return NCContentPresenter().showInfo(error: NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_offline_not_allowed_"))
+                }
                 Task {
-                    let createFolderResults = await NCNetworking.shared.createFolder(serverUrlFileName: serverUrl + "/" + fileNameFolder, account: userBaseUrl.account)
+                    let createFolderResults = await NCNetworking.shared.createFolder(serverUrlFileName: serverUrl + "/" + fileNameFolder, account: session.account)
                     if createFolderResults.error == .success {
-                        let error = await NCNetworkingE2EEMarkFolder().markFolderE2ee(account: userBaseUrl.account, fileName: fileNameFolder, serverUrl: serverUrl, userId: userBaseUrl.userId)
+                        let error = await NCNetworkingE2EEMarkFolder().markFolderE2ee(account: session.account, fileName: fileNameFolder, serverUrl: serverUrl, userId: session.userId)
                         if error != .success {
                             NCContentPresenter().showError(error: error)
                         }
@@ -49,14 +54,29 @@ extension UIAlertController {
                         NCContentPresenter().showError(error: createFolderResults.error)
                     }
                 }
-            } else {
-                NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: serverUrl, account: userBaseUrl.account, urlBase: userBaseUrl.urlBase, userId: userBaseUrl.userId, overwrite: false, withPush: true, sceneIdentifier: sceneIdentifier) { error in
-                    if let completion = completion {
-                        completion(error)
-                    } else if error != .success {
-                        NCContentPresenter().showError(error: error)
-                    } // else: successful, no action
+            } else if isDirectoryEncrypted {
+                if NCNetworking.shared.isOffline {
+                    return NCContentPresenter().showInfo(error: NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_offline_not_allowed_"))
+                }
+                #if !EXTENSION
+                Task {
+                    await NCNetworkingE2EECreateFolder().createFolder(fileName: fileNameFolder, serverUrl: serverUrl, withPush: true, sceneIdentifier: sceneIdentifier, session: session)
                 }
+                #endif
+            } else {
+                let metadataForCreateFolder = NCManageDatabase.shared.createMetadata(fileName: fileNameFolder,
+                                                                                     fileNameView: fileNameFolder,
+                                                                                     ocId: NSUUID().uuidString,
+                                                                                     serverUrl: serverUrl,
+                                                                                     url: "",
+                                                                                     contentType: "httpd/unix-directory",
+                                                                                     directory: true,
+                                                                                     session: session,
+                                                                                     sceneIdentifier: sceneIdentifier)
+                metadataForCreateFolder.status = NCGlobal.shared.metadataStatusWaitCreateFolder
+                metadataForCreateFolder.sessionDate = Date()
+                NCManageDatabase.shared.addMetadata(metadataForCreateFolder)
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCreateFolder, userInfo: ["ocId": metadataForCreateFolder.ocId, "serverUrl": metadataForCreateFolder.serverUrl, "account": metadataForCreateFolder.account, "withPush": true, "sceneIdentifier": sceneIdentifier as Any])
             }
         })
 
@@ -76,7 +96,7 @@ extension UIAlertController {
                 guard let text = alertController.textFields?.first?.text else { return }
                 let folderName = text.trimmingCharacters(in: .whitespaces)
 
-                let textCheck = FileNameValidator.shared.checkFileName(folderName)
+                let textCheck = FileNameValidator.shared.checkFileName(folderName, account: account)
                 okAction.isEnabled = textCheck?.error == nil && !folderName.isEmpty
                 alertController.message = textCheck?.error.localizedDescription
             }
@@ -107,24 +127,15 @@ extension UIAlertController {
         }, completion: completion)
     }
 
-    static func deleteFileOrFolder(titleString: String, message: String?, canDeleteServer: Bool, selectedMetadatas: [tableMetadata], completion: @escaping (_ cancelled: Bool) -> Void) -> UIAlertController {
+    static func deleteFileOrFolder(titleString: String, message: String?, canDeleteServer: Bool, selectedMetadatas: [tableMetadata], sceneIdentifier: String?, completion: @escaping (_ cancelled: Bool) -> Void) -> UIAlertController {
         let alertController = UIAlertController(
             title: titleString,
             message: message,
             preferredStyle: .alert)
         if canDeleteServer {
             alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { (_: UIAlertAction) in
-                Task {
-                    var error = NKError()
-                    var ocId: [String] = []
-                    for metadata in selectedMetadatas where error == .success {
-                        error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
-                        if error == .success {
-                            ocId.append(metadata.ocId)
-                        }
-                    }
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "onlyLocalCache": false, "error": error])
-                }
+                NCNetworking.shared.deleteMetadatas(selectedMetadatas, sceneIdentifier: sceneIdentifier)
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
                 completion(false)
             })
         }
@@ -134,15 +145,12 @@ extension UIAlertController {
                 var error = NKError()
                 var ocId: [String] = []
                 for metadata in selectedMetadatas where error == .success {
-                    error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
+                    error = await NCNetworking.shared.deleteCache(metadata, sceneIdentifier: sceneIdentifier)
                     if error == .success {
                         ocId.append(metadata.ocId)
                     }
                 }
-                if error != .success {
-                    NCContentPresenter().showError(error: error)
-                }
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "onlyLocalCache": true, "error": error])
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "error": error])
             }
             completion(false)
         })
@@ -153,7 +161,7 @@ extension UIAlertController {
         return alertController
     }
 
-    static func renameFile(fileName: String, completion: @escaping (_ newFileName: String) -> Void) -> UIAlertController {
+    static func renameFile(fileName: String, account: String, completion: @escaping (_ newFileName: String) -> Void) -> UIAlertController {
         let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert)
 
         let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
@@ -190,7 +198,7 @@ extension UIAlertController {
             queue: .main) { _ in
                 guard let text = alertController.textFields?.first?.text else { return }
 
-                let textCheck = FileNameValidator.shared.checkFileName(text)
+                let textCheck = FileNameValidator.shared.checkFileName(text, account: account)
                 okAction.isEnabled = textCheck?.error == nil && !text.isEmpty
                 alertController.message = textCheck?.error.localizedDescription
             }
@@ -200,28 +208,21 @@ extension UIAlertController {
         return alertController
     }
 
-    static func renameFile(metadata: tableMetadata, indexPath: IndexPath) -> UIAlertController {
+    static func renameFile(metadata: tableMetadata) -> UIAlertController {
         let alertController = UIAlertController(title: NSLocalizedString("_rename_", comment: ""), message: nil, preferredStyle: .alert)
 
         let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
-            guard let newFileName = alertController.textFields?.first?.text else { return }
+            guard let fileNameNew = alertController.textFields?.first?.text else { return }
 
             // verify if already exists
-            if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, newFileName)) != nil {
+            if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil {
                 NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
                 return
             }
 
-            NCActivityIndicator.shared.start()
-
-            NCNetworking.shared.renameMetadata(metadata, fileNameNew: newFileName, indexPath: indexPath) { error in
+            NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew)
 
-                NCActivityIndicator.shared.stop()
-
-                if error != .success {
-                    NCContentPresenter().showError(error: error)
-                }
-            }
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl])
         })
 
         // text field is initially empty, no action
@@ -229,7 +230,7 @@ extension UIAlertController {
         let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)
 
         alertController.addTextField { textField in
-            textField.text = metadata.fileName
+            textField.text = metadata.fileNameView
             textField.autocapitalizationType = .words
         }
 
@@ -252,7 +253,7 @@ extension UIAlertController {
             queue: .main) { _ in
                 guard let text = alertController.textFields?.first?.text else { return }
 
-                let textCheck = FileNameValidator.shared.checkFileName(text)
+                let textCheck = FileNameValidator.shared.checkFileName(text, account: NCManageDatabase.shared.getActiveTableAccount()?.account)
                 okAction.isEnabled = textCheck?.error == nil && !text.isEmpty
                 alertController.message = textCheck?.error.localizedDescription
             }

+ 4 - 10
iOSClient/Extensions/UIImage+Extension.swift

@@ -26,9 +26,7 @@ import UIKit
 import Accelerate
 
 extension UIImage {
-
-    @objc func resizeImage(size: CGSize, isAspectRation: Bool = true) -> UIImage? {
-
+    func resizeImage(size: CGSize, isAspectRation: Bool = true) -> UIImage? {
         let originRatio = self.size.width / self.size.height
         let newRatio = size.width / size.height
         var newSize = size
@@ -44,13 +42,9 @@ extension UIImage {
         }
 
         UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
-        self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
-        let newImage = UIGraphicsGetImageFromCurrentImageContext()
-        UIGraphicsEndImageContext()
-        if let image = newImage {
-            return image
-        }
-        return self
+        self.draw(in: CGRect(origin: .zero, size: newSize))
+        defer { UIGraphicsEndImageContext() }
+        return UIGraphicsGetImageFromCurrentImageContext()
     }
 
     func fixedOrientation() -> UIImage? {

+ 26 - 24
iOSClient/Favorites/NCFavorite.swift

@@ -33,7 +33,8 @@ class NCFavorite: NCCollectionViewCommon {
         layoutKey = NCGlobal.shared.layoutViewFavorite
         enableSearchBar = false
         headerRichWorkspaceDisable = true
-        emptyImage = utility.loadImage(named: "star.fill", colors: [NCBrandColor.shared.yellowFavorite])
+        emptyImageName = "star.fill"
+        emptyImageColors = [NCBrandColor.shared.yellowFavorite]
         emptyTitle = "_favorite_no_files_"
         emptyDescription = "_tutorial_favorite_view_"
     }
@@ -43,43 +44,44 @@ class NCFavorite: NCCollectionViewCommon {
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
 
-        if dataSource.metadatas.isEmpty {
-            reloadDataSource()
-        }
-        reloadDataSourceNetwork()
+        reloadDataSource()
     }
 
-    // MARK: - DataSource + NC Endpoint
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        getServerData()
+    }
 
-    override func queryDB() {
-        super.queryDB()
+    // MARK: - DataSource
 
-        var metadatas: [tableMetadata] = []
+    override func reloadDataSource() {
+        var predicate = self.defaultPredicate
 
         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))
+           predicate = NSPredicate(format: "account == %@ AND favorite == true", session.account)
         }
 
-        self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account, layoutForView: layoutForView, providers: self.providers, searchResults: self.searchResults)
-    }
+        let results = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView)
+        self.dataSource = NCCollectionViewDataSource(results: results, layoutForView: layoutForView)
 
-    override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
-        super.reloadDataSourceNetwork()
+        super.reloadDataSource()
+    }
 
-        NextcloudKit.shared.listingFavorites(showHiddenFiles: NCKeychain().showHiddenFiles, account: self.appDelegate.account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { task in
+    override func getServerData() {
+        NextcloudKit.shared.listingFavorites(showHiddenFiles: NCKeychain().showHiddenFiles, account: session.account) { task in
             self.dataSourceTask = task
-            self.collectionView.reloadData()
+            if self.dataSource.isEmpty() {
+                self.collectionView.reloadData()
+            }
         } completion: { account, files, _, error in
-            if error == .success {
-                NCManageDatabase.shared.convertFilesToMetadatas(files, useFirstAsMetadataFolder: false) { _, metadatas in
-                    NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas)
-                    self.reloadDataSource()
+            if error == .success, let files {
+                self.database.convertFilesToMetadatas(files, useFirstAsMetadataFolder: false) { _, metadatas in
+                    self.database.updateMetadatasFavorite(account: account, metadatas: metadatas)
                 }
-            } else {
-                self.reloadDataSource(withQueryDB: withQueryDB)
+                self.reloadDataSource()
             }
+            self.refreshControl.endRefreshing()
         }
     }
 }

+ 153 - 120
iOSClient/Files/NCFiles.swift

@@ -23,11 +23,13 @@
 
 import UIKit
 import NextcloudKit
+import RealmSwift
 
 class NCFiles: NCCollectionViewCommon {
     internal var isRoot: Bool = true
     internal var fileNameBlink: String?
     internal var fileNameOpen: String?
+    internal var matadatasHash: String = ""
 
     required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
@@ -37,7 +39,6 @@ class NCFiles: NCCollectionViewCommon {
         enableSearchBar = true
         headerRichWorkspaceDisable = false
         headerMenuTransferView = true
-        emptyImage = NCImageCache.images.folder
         emptyTitle = "_files_no_files_"
         emptyDescription = "_no_file_pull_down_"
     }
@@ -48,44 +49,65 @@ class NCFiles: NCCollectionViewCommon {
         super.viewDidLoad()
 
         if isRoot {
-            NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { _ in
+            NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { notification in
 
-                self.navigationController?.popToRootViewController(animated: false)
+                if let userInfo = notification.userInfo, let account = userInfo["account"] as? String {
+                    if let controller = userInfo["controller"] as? NCMainTabBarController,
+                       controller == self.controller {
+                        controller.account = account
+                    } else {
+                        return
+                    }
+                }
 
-                self.serverUrl = self.utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
+                self.navigationController?.popToRootViewController(animated: false)
+                self.serverUrl = self.utilityFileSystem.getHomeServer(session: self.session)
                 self.isSearchingMode = false
                 self.isEditMode = false
-                self.selectOcId.removeAll()
+                self.fileSelect.removeAll()
+                self.layoutForView = self.database.getLayoutForView(account: self.session.account, key: self.layoutKey, serverUrl: self.serverUrl)
 
-                self.layoutForView = NCManageDatabase.shared.getLayoutForView(account: self.appDelegate.account, key: self.layoutKey, serverUrl: self.serverUrl)
-                if self.layoutForView?.layout == NCGlobal.shared.layoutList {
+                if self.isLayoutList {
                     self.collectionView?.collectionViewLayout = self.listLayout
-                } else if self.layoutForView?.layout == NCGlobal.shared.layoutGrid {
+                } else if self.isLayoutGrid {
                     self.collectionView?.collectionViewLayout = self.gridLayout
-                } else if self.layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare || self.layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio {
+                } else if self.isLayoutPhoto {
                     self.collectionView?.collectionViewLayout = self.mediaLayout
                 }
 
                 self.titleCurrentFolder = self.getNavigationTitle()
                 self.setNavigationLeftItems()
 
+                self.dataSource.removeAll()
                 self.reloadDataSource()
-                self.reloadDataSourceNetwork()
+                self.getServerData()
             }
         }
     }
 
     override func viewWillAppear(_ animated: Bool) {
         if isRoot {
-            serverUrl = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+            serverUrl = utilityFileSystem.getHomeServer(session: session)
             titleCurrentFolder = getNavigationTitle()
         }
         super.viewWillAppear(animated)
 
-        if dataSource.metadatas.isEmpty {
-            reloadDataSource(withQueryDB: true)
+        reloadDataSource()
+    }
+
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        if !self.dataSource.isEmpty() {
+            self.blinkCell(fileName: self.fileNameBlink)
+            self.openFile(fileName: self.fileNameOpen)
+            self.fileNameBlink = nil
+            self.fileNameOpen = nil
+        }
+
+        if !isSearchingMode {
+            getServerData()
         }
-        reloadDataSourceNetwork(withQueryDB: true)
     }
 
     override func viewDidDisappear(_ animated: Bool) {
@@ -95,38 +117,36 @@ class NCFiles: NCCollectionViewCommon {
         fileNameOpen = nil
     }
 
-    // MARK: - DataSource + NC Endpoint
+    // MARK: - DataSource
 
-    override func queryDB() {
-        super.queryDB()
+    override func reloadDataSource() {
+        var predicate = self.defaultPredicate
+        let predicateDirectory = NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, self.serverUrl)
+        let dataSourceMetadatas = self.dataSource.getMetadatas()
 
-        var metadatas: [tableMetadata] = []
-        if NCKeychain().getPersonalFilesOnly(account: self.appDelegate.account) {
-            metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == ''", self.appDelegate.account, self.serverUrl, self.appDelegate.userId))
-        } else {
-            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))
-        if self.metadataFolder == nil {
-            self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId, serverUrl: self.serverUrl)
+        if NCKeychain().getPersonalFilesOnly(account: session.account) {
+            predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@)", session.account, self.serverUrl, session.userId, global.metadataStatusHideInView)
         }
 
-        self.richWorkspaceText = directory?.richWorkspace
-        self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account, layoutForView: layoutForView, providers: self.providers, searchResults: self.searchResults)
-    }
+        self.metadataFolder = database.getMetadataFolder(session: session, serverUrl: self.serverUrl)
+        self.richWorkspaceText = database.getTableDirectory(predicate: predicateDirectory)?.richWorkspace
 
-    override func reloadDataSource(withQueryDB: Bool = true) {
-        super.reloadDataSource(withQueryDB: withQueryDB)
+        let results = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView)
+        self.dataSource = NCCollectionViewDataSource(results: results, layoutForView: layoutForView)
 
-        if !self.dataSource.metadatas.isEmpty {
-            self.blinkCell(fileName: self.fileNameBlink)
-            self.openFile(fileName: self.fileNameOpen)
-            self.fileNameBlink = nil
-            self.fileNameOpen = nil
+        guard let results else {
+            return super.reloadDataSource()
+        }
+        let metadatas = Array(results.freeze())
+
+        self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) { updated in
+            if updated || self.isNumberOfItemsInAllSectionsNull || self.numberOfItemsInAllSections != metadatas.count {
+                super.reloadDataSource()
+            }
         }
     }
 
-    override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
+    override func getServerData() {
         if UIApplication.shared.applicationState == .background {
             NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] Files not reload datasource network with the application in background")
             return
@@ -136,125 +156,137 @@ class NCFiles: NCCollectionViewCommon {
         }
 
         func downloadMetadata(_ metadata: tableMetadata) -> Bool {
-
             let fileSize = utilityFileSystem.fileProviderStorageSize(metadata.ocId, fileNameView: metadata.fileNameView)
             guard fileSize > 0 else { return false }
 
-            if let localFile = NCManageDatabase.shared.getResultsTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))?.first {
+            if let localFile = database.getResultsTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))?.first {
                 if localFile.etag != metadata.etag {
                     return true
                 }
             }
-
             return false
         }
 
-        super.reloadDataSourceNetwork()
+        DispatchQueue.global().async {
+            self.networkReadFolder { metadatas, isChanged, error in
+                DispatchQueue.main.async {
+                    self.refreshControl.endRefreshing()
+
+                    if isChanged || self.isNumberOfItemsInAllSectionsNull {
+                        self.reloadDataSource()
+                    }
+                }
 
-        networkReadFolder { tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error in
-            DispatchQueue.global(qos: .userInteractive).async {
                 if error == .success {
-                    for metadata in metadatas ?? [] where !metadata.directory && downloadMetadata(metadata) {
+                    let metadatas: [tableMetadata] = metadatas ?? self.dataSource.getMetadatas()
+                    for metadata in metadatas where !metadata.directory && downloadMetadata(metadata) {
                         if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty {
                             NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile))
                         }
                     }
-                    self.richWorkspaceText = tableDirectory?.richWorkspace
-                    if metadatasDifferentCount != 0 || metadatasModified != 0 {
-                        self.reloadDataSource()
-                    } else {
-                        self.reloadDataSource(withQueryDB: withQueryDB)
-                    }
-                } else {
-                    self.reloadDataSource(withQueryDB: withQueryDB)
                 }
             }
         }
     }
 
-    private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasDifferentCount: Int, _ metadatasModified: Int, _ error: NKError) -> Void) {
-        var tableDirectory: tableDirectory?
-
-        NCNetworking.shared.readFile(serverUrlFileName: serverUrl, account: appDelegate.account) { task in
+    private func networkReadFolder(completion: @escaping (_ metadatas: [tableMetadata]?, _ isDataChanged: Bool, _ error: NKError) -> Void) {
+        NCNetworking.shared.readFile(serverUrlFileName: serverUrl, account: session.account) { task in
             self.dataSourceTask = task
-            self.collectionView.reloadData()
+            if self.dataSource.isEmpty() {
+                self.collectionView.reloadData()
+            }
         } completion: { account, metadata, error in
+            let isDirectoryE2EE = NCUtilityFileSystem().isDirectoryE2EE(session: self.session, serverUrl: self.serverUrl)
             guard error == .success, let metadata else {
-                return completion(nil, nil, 0, 0, error)
+                return completion(nil, false, error)
+            }
+            /// Check change eTag or E2EE  or DataSource empty
+            let tableDirectory = self.database.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadata.richWorkspace, account: account)
+            guard tableDirectory?.etag != metadata.etag || metadata.e2eEncrypted || self.dataSource.isEmpty() else {
+                return completion(nil, false, NKError())
+            }
+            /// Check Response DataC hanged
+            var checkResponseDataChanged = true
+            if tableDirectory?.etag.isEmpty ?? true || isDirectoryE2EE {
+                checkResponseDataChanged = false
             }
-            tableDirectory = NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadata.richWorkspace, account: account)
-            // swiftlint:disable empty_string
-            let forceReplaceMetadatas = tableDirectory?.etag == ""
-            // swiftlint:enable empty_string
-
-            if tableDirectory?.etag != metadata.etag || metadata.e2eEncrypted {
-                NCNetworking.shared.readFolder(serverUrl: self.serverUrl,
-                                               account: self.appDelegate.account,
-                                               forceReplaceMetadatas: forceReplaceMetadatas) { task in
-                    self.dataSourceTask = task
+
+            NCNetworking.shared.readFolder(serverUrl: self.serverUrl,
+                                           account: metadata.account,
+                                           checkResponseDataChanged: checkResponseDataChanged,
+                                           queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue) { task in
+                self.dataSourceTask = task
+                if self.dataSource.isEmpty() {
                     self.collectionView.reloadData()
-                } completion: { account, metadataFolder, metadatas, metadatasDifferentCount, metadatasModified, error in
-                    guard account == self.appDelegate.account, error == .success else {
-                        return completion(tableDirectory, nil, 0, 0, error)
-                    }
+                }
+            } completion: { account, metadataFolder, metadatas, isDataChanged, error in
+                /// Error
+                guard error == .success else {
+                    return completion(nil, false, error)
+                }
+                /// Updata folder
+                if let metadataFolder {
                     self.metadataFolder = metadataFolder
-                    // E2EE
-                    if let metadataFolder = metadataFolder,
-                       metadataFolder.e2eEncrypted,
-                       NCKeychain().isEndToEndEnabled(account: account),
-                       !NCNetworkingE2EE().isInUpload(account: account, serverUrl: self.serverUrl) {
-                        let lock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: self.serverUrl)
-                        NCNetworkingE2EE().getMetadata(fileId: metadataFolder.ocId, e2eToken: lock?.e2eToken, account: account) { account, version, e2eMetadata, signature, _, error in
-                            if account == self.appDelegate.account, error == .success, let e2eMetadata = e2eMetadata {
-                                let error = NCEndToEndMetadata().decodeMetadata(e2eMetadata, signature: signature, serverUrl: self.serverUrl, account: account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
-                                if error == .success {
-                                    if version == "v1", NCGlobal.shared.capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
-                                        NextcloudKit.shared.nkCommonInstance.writeLog("[E2EE] Conversion v1 to v2")
-                                        NCActivityIndicator.shared.start()
-                                        Task {
-                                            let serverUrl = metadataFolder.serverUrl + "/" + metadataFolder.fileName
-                                            let error = await NCNetworkingE2EE().uploadMetadata(account: metadataFolder.account, serverUrl: serverUrl, userId: metadataFolder.userId, updateVersionV1V2: true)
-                                            if error != .success {
-                                                NCContentPresenter().showError(error: error)
-                                            }
-                                            NCActivityIndicator.shared.stop()
-                                            self.reloadDataSource()
-                                        }
-                                    } else {
-                                        self.reloadDataSource()
-                                    }
-                                } else {
-                                    // Client Diagnostic
-                                    NCManageDatabase.shared.addDiagnostic(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
-                                    NCContentPresenter().showError(error: error)
-                                }
-                            } else if error.errorCode == NCGlobal.shared.errorResourceNotFound {
-                                // no metadata found, send a new metadata
+                    self.richWorkspaceText = metadataFolder.richWorkspace
+                }
+                /// check Response Data Changed
+                if !isDataChanged {
+                    return completion(nil, false, error)
+                }
+
+                guard let metadataFolder,
+                      isDirectoryE2EE,
+                      NCKeychain().isEndToEndEnabled(account: account),
+                      !NCNetworkingE2EE().isInUpload(account: account, serverUrl: self.serverUrl) else {
+                    return completion(metadatas, true, error)
+                }
+
+                /// E2EE
+                let lock = self.database.getE2ETokenLock(account: account, serverUrl: self.serverUrl)
+                NCNetworkingE2EE().getMetadata(fileId: metadataFolder.ocId, e2eToken: lock?.e2eToken, account: account) { account, version, e2eMetadata, signature, _, error in
+
+                    if error == .success, let e2eMetadata = e2eMetadata {
+                        let error = NCEndToEndMetadata().decodeMetadata(e2eMetadata, signature: signature, serverUrl: self.serverUrl, session: self.session)
+
+                        if error == .success {
+                            if version == "v1", NCCapabilities.shared.getCapabilities(account: account).capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
+                                NextcloudKit.shared.nkCommonInstance.writeLog("[E2EE] Conversion v1 to v2")
+                                NCActivityIndicator.shared.start()
                                 Task {
                                     let serverUrl = metadataFolder.serverUrl + "/" + metadataFolder.fileName
-                                    let error = await NCNetworkingE2EE().uploadMetadata(account: metadataFolder.account, serverUrl: serverUrl, userId: metadataFolder.userId)
+                                    let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account)
                                     if error != .success {
                                         NCContentPresenter().showError(error: error)
                                     }
+                                    NCActivityIndicator.shared.stop()
                                 }
-                            } else {
-                                NCContentPresenter().showError(error: NKError(errorCode: NCGlobal.shared.errorE2EEKeyDecodeMetadata, errorDescription: "_e2e_error_"))
                             }
-                            completion(tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error)
+                        } else {
+                            // Client Diagnostic
+                            self.database.addDiagnostic(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
+                            NCContentPresenter().showError(error: error)
+                        }
+                    } else if error.errorCode == NCGlobal.shared.errorResourceNotFound {
+                        // no metadata found, send a new metadata
+                        Task {
+                            let serverUrl = metadataFolder.serverUrl + "/" + metadataFolder.fileName
+                            let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, account: account)
+                            if error != .success {
+                                NCContentPresenter().showError(error: error)
+                            }
                         }
                     } else {
-                        completion(tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error)
+                        NCContentPresenter().showError(error: NKError(errorCode: NCGlobal.shared.errorE2EEKeyDecodeMetadata, errorDescription: "_e2e_error_"))
                     }
+                    completion(metadatas, true, error)
                 }
-            } else {
-                completion(tableDirectory, nil, 0, 0, NKError())
             }
         }
     }
 
     func blinkCell(fileName: String?) {
-        if let fileName = fileName, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, fileName)) {
-            let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
+        if let fileName = fileName, let metadata = database.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", session.account, self.serverUrl, fileName)) {
+            let indexPath = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
             if let indexPath = indexPath {
                 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                     UIView.animate(withDuration: 0.3) {
@@ -273,8 +305,8 @@ class NCFiles: NCCollectionViewCommon {
     }
 
     func openFile(fileName: String?) {
-        if let fileName = fileName, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, fileName)) {
-            let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
+        if let fileName = fileName, let metadata = database.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", session.account, self.serverUrl, fileName)) {
+            let indexPath = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
             if let indexPath = indexPath {
                 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                     self.collectionView(self.collectionView, didSelectItemAt: indexPath)
@@ -285,11 +317,12 @@ class NCFiles: NCCollectionViewCommon {
 
     // MARK: - NCAccountSettingsModelDelegate
 
-    override func accountSettingsDidDismiss(tableAccount: tableAccount?) {
-        if NCManageDatabase.shared.getAllAccount().isEmpty {
-            appDelegate.openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
-        } else if let account = tableAccount?.account, account != appDelegate.account {
-            appDelegate.changeAccount(account, userProfile: nil) { }
+    override func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) {
+        let currentAccount = session.account
+        if database.getAllTableAccount().isEmpty {
+            appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
+        } else if let account = tableAccount?.account, account != currentAccount {
+            NCAccount().changeAccount(account, userProfile: nil, controller: controller) { }
         } else if isRoot {
             titleCurrentFolder = getNavigationTitle()
             navigationItem.title = titleCurrentFolder

+ 3 - 4
iOSClient/GUI/ComponentView.swift

@@ -24,7 +24,6 @@
 import SwiftUI
 
 struct TextFieldClearButton: ViewModifier {
-
     @Binding var text: String
 
     func body(content: Content) -> some View {
@@ -44,15 +43,15 @@ struct TextFieldClearButton: ViewModifier {
 }
 
 struct ButtonRounded: ButtonStyle {
-
     var disabled = false
+    var account = ""
 
     func makeBody(configuration: Configuration) -> some View {
         configuration.label
             .padding(.horizontal, 40)
             .padding(.vertical, 10)
-            .background(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.brandElement))
-            .foregroundColor(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.brandText))
+            .background(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.getElement(account: account)))
+            .foregroundColor(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.getText(account: account)))
             .clipShape(Capsule())
             .opacity(configuration.isPressed ? 0.5 : 1.0)
     }

+ 7 - 11
iOSClient/GUI/HUDView.swift → iOSClient/GUI/NCHUDView.swift

@@ -23,11 +23,11 @@
 
 import SwiftUI
 
-struct HUDView: View {
-
+struct NCHUDView: View {
     @Binding var showHUD: Bool
     @State var textLabel: String
     @State var image: String
+    @State var color: UIColor
 
     var body: some View {
         Button(action: {
@@ -40,7 +40,7 @@ struct HUDView: View {
                 .padding(.horizontal, 10)
                 .padding(14)
                 .background(
-                    Blur(style: .regular)
+                    Blur(style: .regular, color: color)
                         .clipShape(Capsule())
                         .shadow(color: Color(.black).opacity(0.22), radius: 12, x: 0, y: 5)
                     )
@@ -49,12 +49,12 @@ struct HUDView: View {
 }
 
 struct Blur: UIViewRepresentable {
-
     var style: UIBlurEffect.Style
+    var color: UIColor
 
     func makeUIView(context: Context) -> UIVisualEffectView {
         let effectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
-        effectView.backgroundColor = NCBrandColor.shared.brandElement
+        effectView.backgroundColor = color
         return effectView
     }
 
@@ -64,8 +64,8 @@ struct Blur: UIViewRepresentable {
 }
 
 struct ContentView: View {
-
     @State private var showHUD = false
+    @State var color: UIColor
     @Namespace var hudAnimation
 
     func dismissHUDAfterTime() {
@@ -83,14 +83,10 @@ struct ContentView: View {
                     }
                     .navigationTitle("Content View")
                 }
-                HUDView(showHUD: $showHUD, textLabel: NSLocalizedString("_wait_", comment: ""), image: "doc.badge.arrow.up")
+                NCHUDView(showHUD: $showHUD, textLabel: NSLocalizedString("_wait_", comment: ""), image: "doc.badge.arrow.up", color: color)
                     .offset(y: showHUD ? (geo.size.height / 2) : -200)
                     .animation(.easeOut, value: showHUD)
             }
         }
     }
 }
-
-#Preview {
-    ContentView()
-}

+ 131 - 0
iOSClient/GUI/NCHud.swift

@@ -0,0 +1,131 @@
+//
+//  NCHud.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 04/09/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+import JGProgressHUD
+
+class NCHud: NSObject {
+    private let hud = JGProgressHUD()
+    private var view: UIView?
+
+    public init(_ view: UIView? = nil) {
+        if let view {
+            self.view = view
+        }
+        super.init()
+    }
+
+    func initHud(view: UIView? = nil, text: String? = nil, detailText: String? = nil) {
+        DispatchQueue.main.async {
+            if let view {
+                self.view = view
+            }
+
+            self.hud.indicatorView = JGProgressHUDIndicatorView()
+
+            self.hud.textLabel.text = text
+            self.hud.textLabel.textColor = NCBrandColor.shared.iconImageColor
+
+            self.hud.detailTextLabel.text = detailText
+            self.hud.detailTextLabel.textColor = NCBrandColor.shared.iconImageColor2
+
+            if let view = self.view {
+                self.hud.show(in: view)
+            }
+        }
+    }
+
+    func initHudRing(view: UIView? = nil, text: String? = nil, detailText: String? = nil, tapToCancelDetailText: Bool = false, tapOperation: (() -> Void)? = nil) {
+        DispatchQueue.main.async {
+            self.hud.tapOnHUDViewBlock = { hud in
+                if let tapOperation {
+                    tapOperation()
+                    hud.dismiss()
+                }
+            }
+
+            if let view {
+                self.view = view
+            }
+
+            self.hud.indicatorView = JGProgressHUDRingIndicatorView()
+            self.hud.progress = 0.0
+
+            let indicatorView = self.hud.indicatorView as? JGProgressHUDRingIndicatorView
+            indicatorView?.ringWidth = 1.5
+            indicatorView?.ringColor = NCBrandColor.shared.iconImageColor
+
+            self.hud.textLabel.text = text
+            self.hud.textLabel.textColor = NCBrandColor.shared.iconImageColor
+
+            if tapToCancelDetailText {
+                self.hud.detailTextLabel.text = NSLocalizedString("_tap_to_cancel_", comment: "")
+            } else {
+                self.hud.detailTextLabel.text = detailText
+            }
+            self.hud.detailTextLabel.textColor = NCBrandColor.shared.iconImageColor2
+
+            if let view = self.view {
+                self.hud.show(in: view)
+            }
+        }
+    }
+
+    func dismiss() {
+        DispatchQueue.main.async {
+            self.hud.dismiss()
+        }
+    }
+
+    func show() {
+        DispatchQueue.main.async {
+            if let view = self.view {
+                self.hud.show(in: view)
+            }
+        }
+    }
+
+    func progress(num: Float, total: Float) {
+        DispatchQueue.main.async {
+            self.hud.progress = num / total
+        }
+    }
+
+    func progress(_ progress: Double) {
+        DispatchQueue.main.async {
+            self.hud.progress = Float(progress)
+        }
+    }
+
+    func success() {
+        DispatchQueue.main.async {
+            self.hud.indicatorView = JGProgressHUDSuccessIndicatorView()
+            self.hud.indicatorView?.tintColor = .green
+            self.hud.textLabel.text = NSLocalizedString("_success_", comment: "")
+            self.hud.detailTextLabel.text = nil
+            self.hud.dismiss(afterDelay: 1.0)
+        }
+    }
+
+    func error(text: String?) {
+        DispatchQueue.main.async {
+            self.hud.indicatorView = JGProgressHUDErrorIndicatorView()
+            self.hud.indicatorView?.tintColor = .red
+            self.hud.textLabel.text = text
+            self.hud.dismiss(afterDelay: 2.0)
+        }
+    }
+
+    func setText(text: String?, detailText: String? = nil) {
+        DispatchQueue.main.async {
+            self.hud.textLabel.text = text
+            self.hud.detailTextLabel.text = detailText
+        }
+    }
+}

+ 31 - 29
iOSClient/Groupfolders/NCGroupfolders.swift

@@ -23,6 +23,7 @@
 
 import UIKit
 import NextcloudKit
+import RealmSwift
 
 class NCGroupfolders: NCCollectionViewCommon {
 
@@ -33,7 +34,7 @@ class NCGroupfolders: NCCollectionViewCommon {
         layoutKey = NCGlobal.shared.layoutViewGroupfolders
         enableSearchBar = false
         headerRichWorkspaceDisable = true
-        emptyImage = utility.loadImage(named: "folder_group", colors: [NCBrandColor.shared.brandElement])
+        emptyImageName = "folder_group"
         emptyTitle = "_files_no_files_"
         emptyDescription = "_tutorial_groupfolders_view_"
     }
@@ -43,58 +44,59 @@ class NCGroupfolders: NCCollectionViewCommon {
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
 
-        if dataSource.metadatas.isEmpty {
-            reloadDataSource()
-        }
-        reloadDataSourceNetwork()
+        reloadDataSource()
     }
 
-    // MARK: - DataSource + NC Endpoint
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        getServerData()
+    }
 
-    override func queryDB() {
-        super.queryDB()
+    // MARK: - DataSource
 
-        var metadatas: [tableMetadata] = []
+    override func reloadDataSource() {
+        var results: Results<tableMetadata>?
 
         if self.serverUrl.isEmpty {
-            metadatas = NCManageDatabase.shared.getMetadatasFromGroupfolders(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
+            results = database.getResultsMetadatasFromGroupfolders(session: session)
         } else {
-            metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+            results = self.database.getResultsMetadatasPredicate(self.defaultPredicate, layoutForView: layoutForView)
         }
 
-        self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account, layoutForView: layoutForView, providers: self.providers, searchResults: self.searchResults)
-    }
+        self.dataSource = NCCollectionViewDataSource(results: results, layoutForView: layoutForView)
 
-    override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
-        super.reloadDataSourceNetwork()
+        super.reloadDataSource()
+    }
 
-        let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
+    override func getServerData() {
+        let homeServerUrl = utilityFileSystem.getHomeServer(session: session)
 
-        NextcloudKit.shared.getGroupfolders(account: self.appDelegate.account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { task in
+        NextcloudKit.shared.getGroupfolders(account: session.account) { task in
             self.dataSourceTask = task
-            self.collectionView.reloadData()
+            if self.dataSource.isEmpty() {
+                self.collectionView.reloadData()
+            }
         } completion: { account, results, _, error in
             if error == .success, let groupfolders = results {
-                NCManageDatabase.shared.addGroupfolders(account: account, groupfolders: groupfolders)
+                self.database.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 NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: account)
-                            if results.error == .success, let file = results.files.first {
-                                let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file)
-                                let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
-                                NCManageDatabase.shared.addMetadata(metadata)
-                                NCManageDatabase.shared.addDirectory(e2eEncrypted: isDirectoryE2EE, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
-                            }
+                        let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: account)
+
+                        if results.error == .success, let file = results.files?.first {
+                            let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file)
+                            let metadata = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
+                            self.database.addMetadata(metadata)
+                            self.database.addDirectory(e2eEncrypted: isDirectoryE2EE, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
                         }
                     }
                     self.reloadDataSource()
                 }
-            } else {
-                self.reloadDataSource(withQueryDB: withQueryDB)
             }
+            self.refreshControl.endRefreshing()
         }
     }
 }

+ 60 - 25
iOSClient/Login/NCLogin.storyboard

@@ -1,10 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina6_1" orientation="portrait" appearance="light"/>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_9" orientation="portrait" appearance="dark"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
+        <capability name="Image references" minToolsVersion="12.0"/>
         <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"/>
     </dependencies>
     <scenes>
@@ -13,18 +15,18 @@
             <objects>
                 <viewController storyboardIdentifier="NCLogin" id="yj9-jo-WIn" customClass="NCLogin" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
                     <view key="view" contentMode="scaleToFill" id="Bv6-g3-l0M">
-                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                        <rect key="frame" x="0.0" y="0.0" width="440" height="956"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <subviews>
                             <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="BpI-xK-1SU">
-                                <rect key="frame" x="87" y="226.5" width="240" height="120"/>
+                                <rect key="frame" x="100" y="241.66666666666663" width="240" height="120"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="120" id="E9d-5O-bto"/>
                                     <constraint firstAttribute="width" constant="240" id="xwH-mh-yDU"/>
                                 </constraints>
                             </imageView>
                             <textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" layoutMarginsFollowReadableWidth="YES" insetsLayoutMarginsFromSafeArea="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="  Server address https://…" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="szn-G7-5sK">
-                                <rect key="frame" x="50" y="376.5" width="314" height="44"/>
+                                <rect key="frame" x="50" y="391.66666666666669" width="340" height="44"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="44" id="I2v-Zr-IWf"/>
                                 </constraints>
@@ -33,7 +35,7 @@
                                 <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL" returnKeyType="done"/>
                             </textField>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HQd-pF-3cE">
-                                <rect key="frame" x="324" y="378.5" width="40" height="40"/>
+                                <rect key="frame" x="350" y="393.66666666666669" width="40" height="40"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="40" id="3yh-bo-Bzr"/>
@@ -42,36 +44,35 @@
                                 <color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                 <state key="normal">
                                     <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <imageReference key="image" image="arrow.right" catalog="system" symbolScale="large"/>
                                 </state>
                                 <connections>
                                     <action selector="actionButtonLogin:" destination="yj9-jo-WIn" eventType="touchUpInside" id="vFP-SJ-rFq"/>
                                 </connections>
                             </button>
-                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Al0-LA-Ndt">
-                                <rect key="frame" x="334" y="388.5" width="20" height="20"/>
-                                <constraints>
-                                    <constraint firstAttribute="height" constant="20" id="IQA-ga-jx5"/>
-                                    <constraint firstAttribute="width" constant="20" id="hVA-N5-u88"/>
-                                </constraints>
-                            </imageView>
                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="The link to your Nextcloud web interface when you open it in the browser." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PJH-5i-Tkf">
-                                <rect key="frame" x="60" y="438.5" width="294" height="29"/>
+                                <rect key="frame" x="60" y="453.66666666666669" width="320" height="28.666666666666686"/>
                                 <fontDescription key="fontDescription" type="system" pointSize="12"/>
                                 <nil key="textColor"/>
                                 <nil key="highlightedColor"/>
                             </label>
-                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tp-bh-Z9k" userLabel="QRCode">
-                                <rect key="frame" x="182" y="567.5" width="50" height="50"/>
+                            <button contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tp-bh-Z9k" userLabel="QRCode">
+                                <rect key="frame" x="192.66666666666663" y="582.33333333333337" width="55.000000000000028" height="50"/>
                                 <constraints>
-                                    <constraint firstAttribute="width" constant="50" id="3hb-Ez-hOz"/>
+                                    <constraint firstAttribute="width" constant="55" id="3hb-Ez-hOz"/>
                                     <constraint firstAttribute="height" constant="50" id="OLT-tb-4Qb"/>
                                 </constraints>
+                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <state key="normal">
+                                    <imageReference key="image" image="qrcode.viewfinder" catalog="system" symbolScale="default"/>
+                                    <preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="50" scale="large"/>
+                                </state>
                                 <connections>
                                     <action selector="actionQRCode:" destination="yj9-jo-WIn" eventType="touchUpInside" id="qwL-rG-ead"/>
                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5lM-dt-8fM">
-                                <rect key="frame" x="182" y="667.5" width="50" height="50"/>
+                                <rect key="frame" x="195" y="682.33333333333337" width="50" height="50"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="50" id="0RD-Gi-CTv"/>
                                     <constraint firstAttribute="width" constant="50" id="NuK-Yo-LoT"/>
@@ -80,9 +81,29 @@
                                     <action selector="actionCertificate:" destination="yj9-jo-WIn" eventType="touchUpInside" id="Ibx-wC-iEY"/>
                                 </connections>
                             </button>
+                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IXM-eK-wKA" userLabel="Choose Server">
+                                <rect key="frame" x="50" y="391.66666666666669" width="290" height="44"/>
+                                <color key="backgroundColor" white="1" alpha="0.40478847789115646" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="44" id="k9B-my-RAo"/>
+                                </constraints>
+                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="35" maxY="0.0"/>
+                                <inset key="titleEdgeInsets" minX="10" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <state key="normal" title="Name 1"/>
+                            </button>
+                            <imageView hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrowtriangle.down.circle" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="rza-UC-nsA">
+                                <rect key="frame" x="310" y="403.66666666666669" width="20" height="19.666666666666686"/>
+                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="20" id="Y46-VP-7nh"/>
+                                    <constraint firstAttribute="height" constant="21" id="boa-1i-a6E"/>
+                                </constraints>
+                            </imageView>
                         </subviews>
                         <viewLayoutGuide key="safeArea" id="8lf-3Y-f5R"/>
-                        <color key="backgroundColor" white="0.9023259132753424" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <color key="backgroundColor" systemColor="tintColor"/>
                         <constraints>
                             <constraint firstItem="8lf-3Y-f5R" firstAttribute="trailing" secondItem="szn-G7-5sK" secondAttribute="trailing" constant="50" id="24L-p4-zOF"/>
                             <constraint firstItem="PJH-5i-Tkf" firstAttribute="leading" secondItem="8lf-3Y-f5R" secondAttribute="leading" constant="60" id="3aW-cF-rdF"/>
@@ -90,27 +111,33 @@
                             <constraint firstItem="6tp-bh-Z9k" firstAttribute="centerX" secondItem="8lf-3Y-f5R" secondAttribute="centerX" id="Apc-1U-CwU"/>
                             <constraint firstItem="BpI-xK-1SU" firstAttribute="top" secondItem="Bv6-g3-l0M" secondAttribute="centerY" multiplier="0.5" constant="2.5" id="Gkg-up-7eW"/>
                             <constraint firstItem="8lf-3Y-f5R" firstAttribute="centerX" secondItem="BpI-xK-1SU" secondAttribute="centerX" id="IxG-UI-0vq"/>
+                            <constraint firstItem="rza-UC-nsA" firstAttribute="top" secondItem="BpI-xK-1SU" secondAttribute="bottom" constant="41.5" id="MS5-cH-8Ea"/>
                             <constraint firstItem="6tp-bh-Z9k" firstAttribute="top" secondItem="PJH-5i-Tkf" secondAttribute="bottom" constant="100" id="MZQ-GT-XSM"/>
+                            <constraint firstItem="IXM-eK-wKA" firstAttribute="leading" secondItem="8lf-3Y-f5R" secondAttribute="leading" constant="50" id="SE6-52-VoG"/>
                             <constraint firstItem="PJH-5i-Tkf" firstAttribute="top" secondItem="szn-G7-5sK" secondAttribute="bottom" constant="18" id="Vfj-lG-7wT"/>
-                            <constraint firstItem="Al0-LA-Ndt" firstAttribute="centerX" secondItem="HQd-pF-3cE" secondAttribute="centerX" id="Yll-3L-vuj"/>
                             <constraint firstItem="HQd-pF-3cE" firstAttribute="centerY" secondItem="szn-G7-5sK" secondAttribute="centerY" id="cG8-kL-pdu"/>
-                            <constraint firstItem="Al0-LA-Ndt" firstAttribute="centerY" secondItem="HQd-pF-3cE" secondAttribute="centerY" id="eOF-06-PmZ"/>
                             <constraint firstItem="5lM-dt-8fM" firstAttribute="top" secondItem="6tp-bh-Z9k" secondAttribute="bottom" constant="50" id="i5K-Jt-epF"/>
                             <constraint firstItem="szn-G7-5sK" firstAttribute="top" secondItem="BpI-xK-1SU" secondAttribute="bottom" constant="30" id="lWz-Yy-NCO"/>
                             <constraint firstItem="5lM-dt-8fM" firstAttribute="centerX" secondItem="8lf-3Y-f5R" secondAttribute="centerX" id="mKe-Nn-9dd"/>
+                            <constraint firstItem="HQd-pF-3cE" firstAttribute="leading" secondItem="IXM-eK-wKA" secondAttribute="trailing" constant="30" id="p5X-FY-AQg">
+                                <variation key="widthClass=compact" constant="10"/>
+                            </constraint>
+                            <constraint firstItem="IXM-eK-wKA" firstAttribute="top" secondItem="BpI-xK-1SU" secondAttribute="bottom" constant="30" id="sBQ-Q7-GKc"/>
                             <constraint firstItem="szn-G7-5sK" firstAttribute="leading" secondItem="8lf-3Y-f5R" secondAttribute="leading" constant="50" id="uQw-dy-Ga4"/>
+                            <constraint firstItem="IXM-eK-wKA" firstAttribute="trailing" secondItem="rza-UC-nsA" secondAttribute="trailing" constant="10" id="zD5-gc-sHh"/>
                             <constraint firstItem="8lf-3Y-f5R" firstAttribute="trailing" secondItem="PJH-5i-Tkf" secondAttribute="trailing" constant="60" id="zzV-UD-gRV"/>
                         </constraints>
                     </view>
                     <navigationItem key="navigationItem" id="6v9-Gy-jiv"/>
                     <connections>
-                        <outlet property="baseUrl" destination="szn-G7-5sK" id="rpO-mh-09O"/>
+                        <outlet property="baseUrlTextField" destination="szn-G7-5sK" id="rpO-mh-09O"/>
                         <outlet property="certificate" destination="5lM-dt-8fM" id="vw6-cH-njm"/>
+                        <outlet property="enforceServersButton" destination="IXM-eK-wKA" id="Ezu-3y-iDa"/>
+                        <outlet property="enforceServersDropdownImage" destination="rza-UC-nsA" id="eWk-Ww-vJM"/>
                         <outlet property="imageBrand" destination="BpI-xK-1SU" id="0tB-69-RNs"/>
                         <outlet property="imageBrandConstraintY" destination="Gkg-up-7eW" id="RKw-pM-dZr"/>
                         <outlet property="loginAddressDetail" destination="PJH-5i-Tkf" id="wBQ-5P-HD4"/>
                         <outlet property="loginButton" destination="HQd-pF-3cE" id="XOc-dS-QZ8"/>
-                        <outlet property="loginImage" destination="Al0-LA-Ndt" id="dT5-vu-5eD"/>
                         <outlet property="qrCode" destination="6tp-bh-Z9k" id="Tw3-op-BgR"/>
                     </connections>
                 </viewController>
@@ -123,7 +150,7 @@
             <objects>
                 <viewController storyboardIdentifier="NCLoginProvider" id="yEb-Ky-35s" customClass="NCLoginProvider" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
                     <view key="view" contentMode="scaleToFill" id="UX5-cJ-bY6">
-                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                        <rect key="frame" x="0.0" y="0.0" width="440" height="956"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <viewLayoutGuide key="safeArea" id="vqz-4v-cZu"/>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -134,4 +161,12 @@
             <point key="canvasLocation" x="5421.739130434783" y="-1210.0446428571429"/>
         </scene>
     </scenes>
+    <resources>
+        <image name="arrow.right" catalog="system" width="128" height="95"/>
+        <image name="arrowtriangle.down.circle" catalog="system" width="128" height="123"/>
+        <image name="qrcode.viewfinder" catalog="system" width="128" height="115"/>
+        <systemColor name="tintColor">
+            <color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
 </document>

+ 61 - 42
iOSClient/Login/NCLogin.swift

@@ -30,12 +30,13 @@ import SwiftUI
 class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     @IBOutlet weak var imageBrand: UIImageView!
     @IBOutlet weak var imageBrandConstraintY: NSLayoutConstraint!
-    @IBOutlet weak var baseUrl: UITextField!
+    @IBOutlet weak var baseUrlTextField: UITextField!
     @IBOutlet weak var loginAddressDetail: UILabel!
     @IBOutlet weak var loginButton: UIButton!
-    @IBOutlet weak var loginImage: UIImageView!
     @IBOutlet weak var qrCode: UIButton!
     @IBOutlet weak var certificate: UIButton!
+    @IBOutlet weak var enforceServersButton: UIButton!
+    @IBOutlet weak var enforceServersDropdownImage: UIImageView!
 
     private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     private var textColor: UIColor = .white
@@ -82,37 +83,31 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         imageBrand.image = UIImage(named: "logo")
 
         // Url
-        baseUrl.textColor = textColor
-        baseUrl.tintColor = textColor
-        baseUrl.layer.cornerRadius = 10
-        baseUrl.layer.borderWidth = 1
-        baseUrl.layer.borderColor = textColor.cgColor
-        baseUrl.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: baseUrl.frame.height))
-        baseUrl.leftViewMode = .always
-        baseUrl.rightView = UIView(frame: CGRect(x: 0, y: 0, width: 35, height: baseUrl.frame.height))
-        baseUrl.rightViewMode = .always
-        baseUrl.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("_login_url_", comment: ""), attributes: [NSAttributedString.Key.foregroundColor: textColor.withAlphaComponent(0.5)])
-        baseUrl.delegate = self
-
-        baseUrl.isEnabled = !NCBrandOptions.shared.disable_request_login_url
+        baseUrlTextField.textColor = textColor
+        baseUrlTextField.tintColor = textColor
+        baseUrlTextField.layer.cornerRadius = 10
+        baseUrlTextField.layer.borderWidth = 1
+        baseUrlTextField.layer.borderColor = textColor.cgColor
+        baseUrlTextField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: baseUrlTextField.frame.height))
+        baseUrlTextField.leftViewMode = .always
+        baseUrlTextField.rightView = UIView(frame: CGRect(x: 0, y: 0, width: 35, height: baseUrlTextField.frame.height))
+        baseUrlTextField.rightViewMode = .always
+        baseUrlTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("_login_url_", comment: ""), attributes: [NSAttributedString.Key.foregroundColor: textColor.withAlphaComponent(0.5)])
+        baseUrlTextField.delegate = self
+
+        baseUrlTextField.isEnabled = !NCBrandOptions.shared.disable_request_login_url
 
         // Login button
         loginAddressDetail.textColor = textColor
         loginAddressDetail.text = String.localizedStringWithFormat(NSLocalizedString("_login_address_detail_", comment: ""), NCBrandOptions.shared.brand)
 
-        // Login Image
-        loginImage.image = UIImage(named: "arrow.right")?.image(color: textColor, size: 100)
-
         // brand
         if NCBrandOptions.shared.disable_request_login_url {
-            baseUrl.isEnabled = false
-            baseUrl.isUserInteractionEnabled = false
-            baseUrl.alpha = 0.5
+            baseUrlTextField.isEnabled = false
+            baseUrlTextField.isUserInteractionEnabled = false
+            baseUrlTextField.alpha = 0.5
         }
 
-        // qrcode
-        qrCode.setImage(UIImage(systemName: "qrcode.viewfinder")?.image(color: textColor, size: 100), for: .normal)
-
         // certificate
         certificate.setImage(UIImage(named: "certificate")?.image(color: textColor, size: 100), for: .normal)
         certificate.isHidden = true
@@ -129,7 +124,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         self.navigationController?.view.backgroundColor = NCBrandColor.shared.customer
         self.navigationController?.navigationBar.tintColor = textColor
 
-        if !NCManageDatabase.shared.getAllAccount().isEmpty {
+        if !NCManageDatabase.shared.getAllTableAccount().isEmpty {
             let navigationItemCancel = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(self.actionCancel))
             navigationItemCancel.tintColor = textColor
             navigationItem.leftBarButtonItem = navigationItemCancel
@@ -137,14 +132,14 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
 
         if let dirGroupApps = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroupApps) {
             // Nextcloud update share accounts
-            if let error = appDelegate.updateShareAccounts() {
+            if let error = NCAccount().updateAppsShareAccounts() {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Create share accounts \(error.localizedDescription)")
             }
             // Nextcloud get share accounts
             if let shareAccounts = NKShareAccounts().getShareAccount(at: dirGroupApps, application: UIApplication.shared) {
                 var accountTemp = [NKShareAccounts.DataAccounts]()
                 for shareAccount in shareAccounts {
-                    if NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "urlBase == %@ AND user == %@", shareAccount.url, shareAccount.user)) == nil {
+                    if NCManageDatabase.shared.getTableAccount(predicate: NSPredicate(format: "urlBase == %@ AND user == %@", shareAccount.url, shareAccount.user)) == nil {
                         accountTemp.append(shareAccount)
                     }
                 }
@@ -165,14 +160,40 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
 
         handleLoginWithAppConfig()
-        baseUrl.text = urlBase
+        baseUrlTextField.text = urlBase
+
+        enforceServersButton.setTitle(NSLocalizedString("_select_server_", comment: ""), for: .normal)
+
+        let enforceServers = NCBrandOptions.shared.enforce_servers
+
+        if !enforceServers.isEmpty {
+            baseUrlTextField.isHidden = true
+            enforceServersDropdownImage.isHidden = false
+            enforceServersButton.isHidden = false
+
+            let actions = enforceServers.map { server in
+                UIAction(title: server.name, handler: { [self] _ in
+                    enforceServersButton.setTitle(server.name, for: .normal)
+                    baseUrlTextField.text = server.url
+                })
+            }
+
+            enforceServersButton.layer.cornerRadius = 10
+            enforceServersButton.menu = .init(title: NSLocalizedString("_servers_", comment: ""), children: actions)
+            enforceServersButton.showsMenuAsPrimaryAction = true
+            enforceServersButton.configuration?.titleTextAttributesTransformer =
+               UIConfigurationTextAttributesTransformer { incoming in
+                 var outgoing = incoming
+                 outgoing.font = UIFont.systemFont(ofSize: 13)
+                 return outgoing
+             }
+        }
     }
 
     override func viewDidAppear(_ animated: Bool) {
         super.viewDidAppear(animated)
-        appDelegate.timerErrorNetworkingDisabled = true
 
-        if self.shareAccounts != nil, let image = UIImage(systemName: "person.badge.plus")?.withTintColor(.white, renderingMode: .alwaysOriginal), let backgroundColor = NCBrandColor.shared.brandElement.lighter(by: 10) {
+        if self.shareAccounts != nil, let image = UIImage(systemName: "person.badge.plus")?.withTintColor(.white, renderingMode: .alwaysOriginal), let backgroundColor = NCBrandColor.shared.customer.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().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
@@ -183,11 +204,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         }
     }
 
-    override func viewDidDisappear(_ animated: Bool) {
-        super.viewDidDisappear(animated)
-        appDelegate.timerErrorNetworkingDisabled = false
-    }
-
     private func handleLoginWithAppConfig() {
         let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0
 
@@ -297,20 +313,20 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     // MARK: - Login
 
     private func login() {
-        guard var url = baseUrl.text?.trimmingCharacters(in: .whitespacesAndNewlines) else { return }
+        guard var url = baseUrlTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) else { return }
         if url.hasSuffix("/") { url = String(url.dropLast()) }
         if url.isEmpty { return }
         // Check whether baseUrl contain protocol. If not add https:// by default.
         if url.hasPrefix("https") == false && url.hasPrefix("http") == false {
             url = "https://" + url
         }
-        self.baseUrl.text = url
+        self.baseUrlTextField.text = url
         isUrlValid(url: url)
     }
 
     func isUrlValid(url: String, user: String? = nil) {
         loginButton.isEnabled = false
-        NextcloudKit.shared.getServerStatus(serverUrl: url) { serverInfoResult in
+        NextcloudKit.shared.getServerStatus(serverUrl: url) { _, serverInfoResult in
             switch serverInfoResult {
             case .success(let serverInfo):
                 if let host = URL(string: url)?.host {
@@ -369,9 +385,9 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                 let user = valueArray[0].replacingOccurrences(of: "user:", with: "")
                 let password = valueArray[1].replacingOccurrences(of: "password:", with: "")
                 let urlBase = valueArray[2].replacingOccurrences(of: "server:", with: "")
-                let serverUrl = urlBase + "/" + NextcloudKit.shared.nkCommonInstance.dav
+                let serverUrl = urlBase + "/remote.php/dav"
                 loginButton.isEnabled = false
-                NextcloudKit.shared.checkServer(serverUrl: serverUrl) { error in
+                NextcloudKit.shared.checkServer(serverUrl: serverUrl) { _, error in
                     self.loginButton.isEnabled = true
                     if error == .success {
                         self.createAccount(urlBase: urlBase, user: user, password: password)
@@ -397,16 +413,19 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     }
 
     private func createAccount(urlBase: String, user: String, password: String) {
+        let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
         if let host = URL(string: urlBase)?.host {
             NCNetworking.shared.writeCertificate(host: host)
         }
-        self.appDelegate.createAccount(urlBase: urlBase, user: user, password: password) { error in
+        NCAccount().createAccount(urlBase: urlBase, user: user, password: password, controller: controller) { account, error in
             if error == .success {
                 let window = UIApplication.shared.firstWindow
-                if window?.rootViewController is NCMainTabBarController {
+                if let controller = window?.rootViewController as? NCMainTabBarController {
+                    controller.account = account
                     self.dismiss(animated: true)
                 } else {
                     if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+                        controller.account = account
                         controller.modalPresentationStyle = .fullScreen
                         controller.view.alpha = 0
                         window?.rootViewController = controller

+ 41 - 24
iOSClient/Login/NCLoginPoll.swift

@@ -67,17 +67,18 @@ struct NCLoginPoll: View {
         .onChange(of: loginManager.pollFinished) { value in
             if value {
                 let window = UIApplication.shared.firstWindow
-
-                if window?.rootViewController is NCMainTabBarController {
-                    window?.rootViewController?.dismiss(animated: true, completion: nil)
+                if let controller = window?.rootViewController as? NCMainTabBarController {
+                    controller.account = loginManager.account
+                    controller.dismiss(animated: true, completion: nil)
                 } else {
-                    if let mainTabBarController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
-                        mainTabBarController.modalPresentationStyle = .fullScreen
-                        mainTabBarController.view.alpha = 0
-                        window?.rootViewController = mainTabBarController
+                    if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+                        controller.account = loginManager.account
+                        controller.modalPresentationStyle = .fullScreen
+                        controller.view.alpha = 0
+                        window?.rootViewController = controller
                         window?.makeKeyAndVisible()
                         UIView.animate(withDuration: 0.5) {
-                            mainTabBarController.view.alpha = 1
+                            controller.view.alpha = 1
                         }
                     }
                 }
@@ -91,6 +92,9 @@ struct NCLoginPoll: View {
                 loginManager.openLoginInBrowser()
             }
         }
+        .onDisappear {
+            loginManager.onDisappear()
+        }
         .interactiveDismissDisabled()
     }
 }
@@ -100,40 +104,53 @@ struct NCLoginPoll: View {
 }
 
 private class LoginManager: ObservableObject {
-    private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
-
     var loginFlowV2Token = ""
     var loginFlowV2Endpoint = ""
     var loginFlowV2Login = ""
 
     @Published var pollFinished = false
     @Published var isLoading = false
+    @Published var account = ""
 
-    init() {
-        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
-    }
-
-    @objc func applicationDidBecomeActive(_ notification: NSNotification) {
-        poll()
-    }
+    var timer: DispatchSourceTimer?
 
     func configure(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) {
         self.loginFlowV2Token = loginFlowV2Token
         self.loginFlowV2Endpoint = loginFlowV2Endpoint
         self.loginFlowV2Login = loginFlowV2Login
+
+        poll()
     }
 
     func poll() {
-        NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in
-            if error == .success, let urlBase = server, let user = loginName, let appPassword {
-                self.isLoading = true
-                self.appDelegate.createAccount(urlBase: urlBase, user: user, password: appPassword) { error in
-                    if error == .success {
-                        self.pollFinished = true
+        let queue = DispatchQueue.global(qos: .background)
+        timer = DispatchSource.makeTimerSource(queue: queue)
+
+        guard let timer = timer else { return }
+
+        timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(1))
+        timer.setEventHandler(handler: {
+            DispatchQueue.main.async {
+                let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+                NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in
+                    if error == .success, let urlBase = server, let user = loginName, let appPassword {
+                        self.isLoading = true
+                        NCAccount().createAccount(urlBase: urlBase, user: user, password: appPassword, controller: controller) { account, error in
+                            if error == .success {
+                                self.account = account
+                                self.pollFinished = true
+                            }
+                        }
                     }
                 }
             }
-        }
+        })
+
+        timer.resume()
+    }
+
+    func onDisappear() {
+        timer?.cancel()
     }
 
     func openLoginInBrowser() {

+ 18 - 16
iOSClient/Login/NCLoginProvider.swift

@@ -22,13 +22,12 @@
 //
 
 import UIKit
-import WebKit
+@preconcurrency import WebKit
 import NextcloudKit
 import FloatingPanel
 
 class NCLoginProvider: UIViewController {
     var webView: WKWebView?
-    let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     let utility = NCUtility()
     var titleView: String = ""
     var urlBase = ""
@@ -58,24 +57,17 @@ class NCLoginProvider: UIViewController {
 
         if let host = URL(string: urlBase)?.host {
             titleView = host
-            if let account = NCManageDatabase.shared.getActiveAccount(), NCKeychain().getPassword(account: account.account).isEmpty {
-                titleView = NSLocalizedString("_user_", comment: "") + " " + account.userId + " " + NSLocalizedString("_in_", comment: "") + " " + host
+            if let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(), NCKeychain().getPassword(account: activeTableAccount.account).isEmpty {
+                titleView = NSLocalizedString("_user_", comment: "") + " " + activeTableAccount.userId + " " + NSLocalizedString("_in_", comment: "") + " " + host
             }
         }
 
         self.title = titleView
     }
 
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        // Stop timer error network
-        appDelegate.timerErrorNetworkingDisabled = true
-    }
-
     override func viewDidDisappear(_ animated: Bool) {
         super.viewDidDisappear(animated)
         NCActivityIndicator.shared.stop()
-        appDelegate.timerErrorNetworkingDisabled = false
     }
 
     func loadWebPage(webView: WKWebView, url: URL) {
@@ -166,17 +158,27 @@ extension NCLoginProvider: WKNavigationDelegate {
         let account: String = "\(username) \(urlBase)"
         let user = username
 
-        NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
-        NextcloudKit.shared.getUserProfile(account: account) { _, userProfile, _, error in
+        NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in
             if error == .success, let userProfile {
-                NCManageDatabase.shared.deleteAccount(account)
+                NextcloudKit.shared.appendSession(account: account,
+                                                  urlBase: urlBase,
+                                                  user: user,
+                                                  userId: user,
+                                                  password: password,
+                                                  userAgent: userAgent,
+                                                  nextcloudVersion: NCCapabilities.shared.getCapabilities(account: account).capabilityServerVersionMajor,
+                                                  groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
+                NCSession.shared.appendSession(account: account, urlBase: urlBase, user: user, userId: userProfile.userId)
                 NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
-                self.appDelegate.changeAccount(account, userProfile: userProfile) { }
+                NCAccount().changeAccount(account, userProfile: userProfile, controller: nil) { }
+
                 let window = UIApplication.shared.firstWindow
-                if window?.rootViewController is NCMainTabBarController {
+                if let controller = window?.rootViewController as? NCMainTabBarController {
+                    controller.account = account
                     self.dismiss(animated: true)
                 } else {
                     if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+                        controller.account = account
                         controller.modalPresentationStyle = .fullScreen
                         controller.view.alpha = 0
                         window?.rootViewController = controller

+ 26 - 14
iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift

@@ -25,26 +25,32 @@ import UIKit
 
 protocol NCCellProtocol {
     var fileAvatarImageView: UIImageView? { get }
-    var fileObjectId: String? { get set }
+    var fileAccount: String? { get set }
+    var fileOcId: String? { get set }
+    var fileOcIdTransfer: String? { get set }
     var filePreviewImageView: UIImageView? { get set }
-    var filePreviewImageBottom: NSLayoutConstraint? { get set }
     var fileUser: String? { get set }
     var fileTitleLabel: UILabel? { get set }
     var fileInfoLabel: UILabel? { get set }
     var fileSubinfoLabel: UILabel? { get set }
-    var fileProgressView: UIProgressView? { get set }
     var fileStatusImage: UIImageView? { get set }
     var fileLocalImage: UIImageView? { get set }
     var fileFavoriteImage: UIImageView? { get set }
     var fileSharedImage: UIImageView? { get set }
     var fileMoreImage: UIImageView? { get set }
     var cellSeparatorView: UIView? { get set }
-    var indexPath: IndexPath { get set }
 
     func titleInfoTrailingDefault()
     func titleInfoTrailingFull()
     func writeInfoDateSize(date: NSDate, size: Int64)
-    func setButtonMore(named: String, image: UIImage)
+    func setButtonMore(image: UIImage)
+    func hideImageItem(_ status: Bool)
+    func hideImageFavorite(_ status: Bool)
+    func hideImageStatus(_ status: Bool)
+    func hideImageLocal(_ status: Bool)
+    func hideLabelTitle(_ status: Bool)
+    func hideLabelInfo(_ status: Bool)
+    func hideLabelSubinfo(_ status: Bool)
     func hideButtonShare(_ status: Bool)
     func hideButtonMore(_ status: Bool)
     func selected(_ status: Bool, isEditMode: Bool)
@@ -54,19 +60,22 @@ protocol NCCellProtocol {
 }
 
 extension NCCellProtocol {
-
     var fileAvatarImageView: UIImageView? {
         return nil
     }
-    var fileObjectId: String? {
+    var fileAccount: String? {
         get { return nil }
         set {}
     }
-    var filePreviewImageView: UIImageView? {
+    var fileOcId: String? {
         get { return nil }
         set {}
     }
-    var filePreviewImageBottom: NSLayoutConstraint? {
+    var fileOcIdTransfer: String? {
+        get { return nil }
+        set {}
+    }
+    var filePreviewImageView: UIImageView? {
         get { return nil }
         set {}
     }
@@ -82,10 +91,6 @@ extension NCCellProtocol {
         get { return nil }
         set { }
     }
-    var fileProgressView: UIProgressView? {
-        get { return nil }
-        set {}
-    }
     var fileStatusImage: UIImageView? {
         get { return nil }
         set {}
@@ -114,7 +119,14 @@ extension NCCellProtocol {
     func titleInfoTrailingDefault() {}
     func titleInfoTrailingFull() {}
     func writeInfoDateSize(date: NSDate, size: Int64) {}
-    func setButtonMore(named: String, image: UIImage) {}
+    func setButtonMore(image: UIImage) {}
+    func hideImageItem(_ status: Bool) {}
+    func hideImageFavorite(_ status: Bool) {}
+    func hideImageStatus(_ status: Bool) {}
+    func hideImageLocal(_ status: Bool) {}
+    func hideLabelTitle(_ status: Bool) {}
+    func hideLabelInfo(_ status: Bool) {}
+    func hideLabelSubinfo(_ status: Bool) {}
     func hideButtonShare(_ status: Bool) {}
     func hideButtonMore(_ status: Bool) {}
     func selected(_ status: Bool, isEditMode: Bool) {}

+ 55 - 24
iOSClient/Main/Collection Common/Cell/NCGridCell.swift

@@ -35,16 +35,20 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     @IBOutlet weak var buttonMore: UIButton!
     @IBOutlet weak var imageVisualEffect: UIVisualEffectView!
 
-    var objectId = ""
-    var indexPath = IndexPath()
-    private var user = ""
+    var ocId = ""
+    var ocIdTransfer = ""
+    var account = ""
+    var user = ""
 
     weak var gridCellDelegate: NCGridCellDelegate?
-    var namedButtonMore = ""
 
-    var fileObjectId: String? {
-        get { return objectId }
-        set { objectId = newValue ?? "" }
+    var fileOcId: String? {
+        get { return ocId }
+        set { ocId = newValue ?? "" }
+    }
+    var fileOcIdTransfer: String? {
+        get { return ocIdTransfer }
+        set { ocIdTransfer = newValue ?? "" }
     }
     var filePreviewImageView: UIImageView? {
         get { return imageItem }
@@ -95,25 +99,26 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         accessibilityValue = nil
         isAccessibilityElement = true
 
+        imageItem.image = nil
         imageItem.layer.cornerRadius = 6
         imageItem.layer.masksToBounds = true
-
+        imageSelect.isHidden = true
+        imageSelect.image = NCImageCache.shared.getImageCheckedYes()
+        imageStatus.image = nil
+        imageFavorite.image = nil
+        imageLocal.image = nil
+        labelTitle.text = ""
+        labelInfo.text = ""
+        labelSubinfo.text = ""
         imageVisualEffect.layer.cornerRadius = 6
         imageVisualEffect.clipsToBounds = true
         imageVisualEffect.alpha = 0.5
 
-        imageSelect.isHidden = true
-        imageSelect.image = NCImageCache.images.checkedYes
-
         let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:)))
         longPressedGesture.minimumPressDuration = 0.5
         longPressedGesture.delegate = self
         longPressedGesture.delaysTouchesBegan = true
         self.addGestureRecognizer(longPressedGesture)
-
-        labelTitle.text = ""
-        labelInfo.text = ""
-        labelSubinfo.text = ""
     }
 
     override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
@@ -121,30 +126,55 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     @IBAction func touchUpInsideMore(_ sender: Any) {
-        gridCellDelegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
+        gridCellDelegate?.tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: imageItem.image, sender: sender)
     }
 
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
-        gridCellDelegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
+        gridCellDelegate?.longPressGridItem(with: ocId, ocIdTransfer: ocIdTransfer, gestureRecognizer: gestureRecognizer)
     }
 
     fileprivate func setA11yActions() {
-        let moreName = namedButtonMore == NCGlobal.shared.buttonMoreStop ? "_cancel_" : "_more_"
-
         self.accessibilityCustomActions = [
             UIAccessibilityCustomAction(
-                name: NSLocalizedString(moreName, comment: ""),
+                name: NSLocalizedString("_more_", comment: ""),
                 target: self,
                 selector: #selector(touchUpInsideMore))
         ]
     }
 
-    func setButtonMore(named: String, image: UIImage) {
-        namedButtonMore = named
+    func setButtonMore(image: UIImage) {
         buttonMore.setImage(image, for: .normal)
         setA11yActions()
     }
 
+    func hideImageItem(_ status: Bool) {
+        imageItem.isHidden = status
+    }
+
+    func hideImageFavorite(_ status: Bool) {
+        imageFavorite.isHidden = status
+    }
+
+    func hideImageStatus(_ status: Bool) {
+        imageStatus.isHidden = status
+    }
+
+    func hideImageLocal(_ status: Bool) {
+        imageLocal.isHidden = status
+    }
+
+    func hideLabelTitle(_ status: Bool) {
+        labelTitle.isHidden = status
+    }
+
+    func hideLabelInfo(_ status: Bool) {
+        labelInfo.isHidden = status
+    }
+
+    func hideLabelSubinfo(_ status: Bool) {
+        labelSubinfo.isHidden = status
+    }
+
     func hideButtonMore(_ status: Bool) {
         buttonMore.isHidden = status
     }
@@ -159,6 +189,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         }
         if status {
             imageSelect.isHidden = false
+            imageSelect.image = NCImageCache.shared.getImageCheckedYes()
             imageVisualEffect.isHidden = false
         } else {
             imageSelect.isHidden = true
@@ -191,8 +222,8 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 }
 
 protocol NCGridCellDelegate: AnyObject {
-    func tapMoreGridItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any)
-    func longPressGridItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer)
+    func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any)
+    func longPressGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer)
 }
 
 // MARK: - Grid Layout

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно