Browse Source

Version 5.3.0 (#2930)

---------

Signed-off-by: Marino Faggiana <marino@marinofaggiana.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
Signed-off-by: Milen Pivchev <jtodorov_kostadinov@tu-sofia.bg>
Signed-off-by: Marco Ambrosini <marcoambrosini@proton.me>
Signed-off-by: Claudio Cambra <developer@claudiocambra.com>
Marino Faggiana 11 months ago
parent
commit
ffefb25d14
100 changed files with 1732 additions and 1320 deletions
  1. 12 13
      .github/workflows/additional-targets.yml
  2. 5 1
      .github/workflows/lint.yml
  3. 0 72
      .github/workflows/xcode.xxx
  4. 129 0
      .github/workflows/xcode.yml
  5. 0 3
      .slather.yml
  6. 2 1
      .swiftlint.yml
  7. 1 1
      Brand/Database.swift
  8. 8 4
      Brand/Intro/NCIntroViewController.swift
  9. 26 1
      Brand/NCBrand.swift
  10. 4 0
      Brand/Notification_Service_Extension.entitlements
  11. 4 0
      Brand/Share.entitlements
  12. 8 4
      Brand/Widget.entitlements
  13. 4 0
      Brand/WidgetDashboardIntentHandler.entitlements
  14. 12 0
      Brand/iOSClient.entitlements
  15. 17 2
      Brand/iOSClient.plist
  16. 1 1
      File Provider Extension UI/DocumentActionViewController.swift
  17. 0 2
      File Provider Extension/FileProviderData.swift
  18. 4 8
      File Provider Extension/FileProviderEnumerator.swift
  19. 1 1
      File Provider Extension/FileProviderExtension.swift
  20. 0 3
      Gemfile
  21. 314 123
      Nextcloud.xcodeproj/project.pbxproj
  22. 23 3
      Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme
  23. 1 1
      Share/NCShareCell.swift
  24. 3 3
      Share/NCShareExtension+DataSource.swift
  25. 2 0
      Share/NCShareExtension+NCDelegate.swift
  26. 7 4
      Share/NCShareExtension.swift
  27. 0 18
      Sourcery/EnvVars.stencil
  28. BIN
      Sourcery/bin/sourcery
  29. 37 0
      Tests/Common/BaseXCTestCase.swift
  30. 30 0
      Tests/Common/TestConstants.swift
  31. 2 11
      Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift
  32. 13 12
      Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift
  33. 0 33
      Tests/NextcloudSnapshotTests/Extensions/SwiftUIView+Extensions.swift
  34. 0 37
      Tests/NextcloudSnapshotTests/NextcloudSnapshotTests.swift
  35. BIN
      Tests/NextcloudSnapshotTests/__Snapshots__/NextcloudSnapshotTests/test_CapalitiesView.DefaultPreviewConfiguration.heic
  36. BIN
      Tests/NextcloudSnapshotTests/__Snapshots__/NextcloudSnapshotTests/test_HUDView.DefaultPreviewConfiguration.heic
  37. 3 5
      Tests/NextcloudUITests/BaseUIXCTestCase.swift
  38. 18 20
      Tests/NextcloudUITests/LoginUITests.swift
  39. 6 7
      Widget/Dashboard/DashboardData.swift
  40. 9 7
      Widget/Dashboard/DashboardWidgetView.swift
  41. 19 34
      Widget/Files/FilesData.swift
  42. 30 27
      Widget/Files/FilesWidgetView.swift
  43. 4 4
      Widget/Lockscreen/LockscreenWidgetView.swift
  44. 8 7
      Widget/Toolbar/ToolbarWidgetView.swift
  45. 13 0
      Widget/Widget.swift
  46. 11 43
      create-docker-test-server.sh
  47. 6 25
      iOSClient/Account Request/NCAccountRequest.swift
  48. 23 14
      iOSClient/Activity/NCActivity.swift
  49. 2 2
      iOSClient/Activity/NCActivityCommentView.swift
  50. 6 4
      iOSClient/Activity/NCActivityTableViewCell.swift
  51. 94 430
      iOSClient/AppDelegate.swift
  52. 76 0
      iOSClient/Assistant/Create Task/NCAssistantCreateNewTask.swift
  53. 186 0
      iOSClient/Assistant/Models/NCAssistantTask.swift
  54. 195 0
      iOSClient/Assistant/NCAssistant.swift
  55. 37 0
      iOSClient/Assistant/NCAssistantEmptyView.swift
  56. 102 0
      iOSClient/Assistant/Task Detail/NCAssistantTaskDetail.swift
  57. 3 2
      iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
  58. 1 1
      iOSClient/BrowserWeb/NCBrowserWeb.swift
  59. 1 1
      iOSClient/Color/NCColorPicker.swift
  60. 2 6
      iOSClient/Data/NCManageDatabase+Account.swift
  61. 0 7
      iOSClient/Data/NCManageDatabase+Avatar.swift
  62. 12 3
      iOSClient/Data/NCManageDatabase+Capabilities.swift
  63. 8 4
      iOSClient/Data/NCManageDatabase+Directory.swift
  64. 4 1
      iOSClient/Data/NCManageDatabase+Metadata+Session.swift
  65. 46 44
      iOSClient/Data/NCManageDatabase+Metadata.swift
  66. 2 6
      iOSClient/Data/NCManageDatabase+Trash.swift
  67. 48 66
      iOSClient/Diagnostics/NCCapabilitiesView.swift
  68. 2 2
      iOSClient/Extensions/UIAlertController+Extension.swift
  69. 28 0
      iOSClient/Extensions/UIApplication+Extension.swift
  70. 2 2
      iOSClient/Extensions/UIColor+Extension.swift
  71. 4 0
      iOSClient/Extensions/UIImage+Extension.swift
  72. 6 6
      iOSClient/Extensions/UINavigationController+Extension.swift
  73. 13 0
      iOSClient/Extensions/View+Extension.swift
  74. 4 4
      iOSClient/Favorites/NCFavorite.swift
  75. 20 23
      iOSClient/Files/NCFiles.swift
  76. 1 1
      iOSClient/GUI/ComponentView.swift
  77. 3 16
      iOSClient/GUI/HUDView.swift
  78. 3 3
      iOSClient/Groupfolders/NCGroupfolders.swift
  79. 1 1
      iOSClient/Images.xcassets/InfoNetwork.imageset/Contents.json
  80. 0 0
      iOSClient/Images.xcassets/InfoNetwork.imageset/InfoNetwork.pdf
  81. 0 26
      iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/Contents.json
  82. BIN
      iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic.png
  83. BIN
      iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic@3x-1.png
  84. BIN
      iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic@3x.png
  85. 0 26
      iOSClient/Images.xcassets/MenuGroupByDate.imageset/Contents.json
  86. BIN
      iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate.png
  87. BIN
      iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate@2x.png
  88. BIN
      iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate@3x.png
  89. 0 26
      iOSClient/Images.xcassets/MenuGroupByFile.imageset/Contents.json
  90. BIN
      iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile.png
  91. BIN
      iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile@2x.png
  92. BIN
      iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile@3x.png
  93. 0 26
      iOSClient/Images.xcassets/MenuOrderByFileName.imageset/Contents.json
  94. BIN
      iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName.png
  95. BIN
      iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName@2x.png
  96. BIN
      iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName@3x.png
  97. 0 26
      iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/Contents.json
  98. BIN
      iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate.png
  99. BIN
      iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate@2x.png
  100. BIN
      iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate@3x.png

+ 12 - 13
.github/workflows/additional-targets.yml

@@ -11,23 +11,22 @@ on:
       - master
       - master
       - develop
       - develop
 
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
 jobs:
 jobs:
   build-and-test:
   build-and-test:
     name: Build and Test
     name: Build and Test
-    runs-on: macos-latest
+    runs-on: macos-14
     if: github.event.pull_request.draft == false
     if: github.event.pull_request.draft == false
     env:
     env:
       PROJECT: Nextcloud.xcodeproj
       PROJECT: Nextcloud.xcodeproj
-      DESTINATION: platform=iOS Simulator,name=iPhone 14
+      DESTINATION: platform=iOS Simulator,name=iPhone 15
     steps:
     steps:
     - name: Set env var
     - name: Set env var
       run: echo "DEVELOPER_DIR=$(xcode-select --print-path)" >> $GITHUB_ENV
       run: echo "DEVELOPER_DIR=$(xcode-select --print-path)" >> $GITHUB_ENV
-    - uses: actions/checkout@v3
-    - name: Setup Bundler and Install Gems
-      run: |
-        gem install bundler
-        bundle install
-        bundle update
+    - uses: actions/checkout@v4
     - name: Restore Carhage Cache
     - name: Restore Carhage Cache
       uses: actions/cache@v3
       uses: actions/cache@v3
       id: carthage-cache
       id: carthage-cache
@@ -43,26 +42,26 @@ jobs:
       run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
       run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
     - name: Build iOS Share
     - name: Build iOS Share
       run: |
       run: |
-        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
       env:
       env:
           SCHEME: Share
           SCHEME: Share
     - name: Build iOS File Extension
     - name: Build iOS File Extension
       run: |
       run: |
-        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
       env:
       env:
           SCHEME: File Provider Extension
           SCHEME: File Provider Extension
     - name: Build iOS Notification Extension
     - name: Build iOS Notification Extension
       run: |
       run: |
-        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
       env:
       env:
           SCHEME: Notification Service Extension
           SCHEME: Notification Service Extension
     - name: Build iOS Widget
     - name: Build iOS Widget
       run: |
       run: |
-        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
       env:
       env:
           SCHEME: Widget
           SCHEME: Widget
     - name: Build iOS Widget Dashboard IntentHandler
     - name: Build iOS Widget Dashboard IntentHandler
       run: |
       run: |
-        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
+        xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
       env:
       env:
           SCHEME: WidgetDashboardIntentHandler
           SCHEME: WidgetDashboardIntentHandler

+ 5 - 1
.github/workflows/lint.yml

@@ -14,13 +14,17 @@ on:
       - master
       - master
       - develop
       - develop
 
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
 jobs:
 jobs:
   Lint:
   Lint:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     if: github.event.pull_request.draft == false
     if: github.event.pull_request.draft == false
 
 
     steps:
     steps:
-     - uses: actions/checkout@v3
+     - uses: actions/checkout@v4
 
 
      - name: GitHub Action for SwiftLint
      - name: GitHub Action for SwiftLint
        uses: norio-nomura/action-swiftlint@3.2.1
        uses: norio-nomura/action-swiftlint@3.2.1

+ 0 - 72
.github/workflows/xcode.xxx

@@ -1,72 +0,0 @@
-name: Build main target
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-  pull_request:
-    types: [synchronize, opened, reopened, ready_for_review]
-    branches:
-      - master
-      - develop
-
-jobs:
-  build-and-test:
-    name: Build and Test
-    runs-on: macos-latest
-    if: github.event.pull_request.draft == false
-    env:
-      PROJECT: Nextcloud.xcodeproj
-      DESTINATION: platform=iOS Simulator,name=iPhone 14
-      SCHEME: Nextcloud
-    steps:
-    - name: Set env var
-      run: echo "DEVELOPER_DIR=$(xcode-select --print-path)" >> $GITHUB_ENV
-    - uses: actions/checkout@v3
-    - name: Setup Bundler and Install Gems
-      run: |
-        gem install bundler
-        bundle install
-        bundle update
-    - name: Restore Carhage Cache
-      uses: actions/cache@v3
-      id: carthage-cache
-      with:
-        path: Carthage
-        key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }}
-        restore-keys: |
-          ${{ runner.os }}-carthage-
-    - name: Carthage
-      if: steps.carthage-cache.outputs.cache-hit != 'true'
-      run: carthage bootstrap --use-xcframeworks --platform iOS
-    - name: Download GoogleService-Info.plist
-      run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
-    - name: Install docker
-      run: |
-        brew install colima
-        brew install docker
-        colima start
-    - name: Create docker test server and export enviroment variables
-      run: |
-        source ./create-docker-test-server.sh
-        if [ ! -f ".env-vars" ]; then
-            touch .env-vars
-            echo "export TEST_SERVER_URL=$TEST_SERVER_URL" >> .env-vars
-            echo "export TEST_USER=$TEST_USER" >> .env-vars
-            echo "export TEST_APP_PASSWORD=$TEST_APP_PASSWORD" >> .env-vars
-        fi
-    - name: Build & Test Nextcloud iOS
-      run: |
-        set -o pipefail && xcodebuild test -project $PROJECT \
-        -scheme "$SCHEME" \
-        -destination "$DESTINATION" \
-        -enableCodeCoverage YES \
-        -test-iterations 3 \
-        -retry-tests-on-failure \
-        | xcpretty
-    - name: Upload coverage to codecov
-      run: |
-        bundle exec slather
-        bash <(curl -s https://codecov.io/bash) -f ./cobertura.xml -X coveragepy -X gcov -X xcode -t ${{ secrets.CODECOV_TOKEN }}
-

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

@@ -0,0 +1,129 @@
+name: Build and test main target
+
+on:
+  push:
+    branches:
+      - master
+      - develop
+  pull_request:
+    types: [synchronize, opened, reopened, ready_for_review]
+    branches:
+      - master
+      - develop
+
+env:
+  PROJECT: Nextcloud.xcodeproj
+  DESTINATION: platform=iOS Simulator,name=iPhone 15,OS=17.2
+  SCHEME: Nextcloud
+  SERVER_BRANCH: stable28
+  PHP_VERSION: 8.2
+
+jobs:
+  build:
+    name: Build
+    runs-on: macos-13
+    if: github.event.pull_request.draft == false
+
+    steps:
+    - uses: actions/checkout@v4
+
+    - name: Restore Carhage Cache
+      uses: actions/cache@v3
+      id: carthage-cache
+      with:
+        path: Carthage
+        key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }}
+        restore-keys: |
+          ${{ runner.os }}-carthage-
+
+    - name: Carthage
+      if: steps.carthage-cache.outputs.cache-hit != 'true'
+      run: carthage bootstrap --use-xcframeworks --platform iOS
+
+    - name: Download GoogleService-Info.plist
+      run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
+
+    - name: Build Nextcloud iOS
+      run: |
+        set -o pipefail && \
+        xcodebuild build-for-testing \
+        -scheme "${{ env.SCHEME }}" \
+        -destination "${{ env.DESTINATION }}" \
+        -derivedDataPath "DerivedData" \
+        | xcbeautify --quieter
+
+    - name: Upload test build
+      uses: actions/upload-artifact@v4
+      with:
+        name: Nextcloud iOS
+        path: DerivedData/Build/Products
+        retention-days: 4
+
+  test:
+    name: Test
+    runs-on: macos-13
+    needs: [build]
+
+    if: github.event.pull_request.draft == false
+
+    steps:
+    - uses: actions/checkout@v4
+
+    - name: Set up php ${{ env.PHP_VERSION }}
+      uses: shivammathur/setup-php@8872c784b04a1420e81191df5d64fbd59d3d3033 # v2.30.0
+      with:
+        php-version: ${{ env.PHP_VERSION }}
+        # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
+        extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql
+        coverage: none
+        ini-file: development
+        # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on
+        ini-values: apc.enable_cli=on, disable_functions=
+
+    - name: Checkout server
+      uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+      with:
+        submodules: true
+        repository: nextcloud/server
+        path: server
+        ref: ${{ env.SERVER_BRANCH }}
+
+    - name: Set up Nextcloud
+      run: |
+        mkdir server/data
+        ./server/occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
+        ./server/occ config:system:set hashing_default_password --value=true --type=boolean
+        ./server/occ config:system:set auth.bruteforce.protection.enabled --value false --type bool
+        ./server/occ config:system:set ratelimit.protection.enabled --value false --type bool
+        ./server/occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu"
+        ./server/occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu"
+        ./server/occ background:cron
+        PHP_CLI_SERVER_WORKERS=5 php -S localhost:8080 -t server/ &
+
+    - name: Download test build
+      uses: actions/download-artifact@v4
+      with:
+        name: Nextcloud iOS
+
+    - name: Check server status
+      run: curl -s --retry 5 --retry-delay 60 --retry-all-errors http://localhost:8080/status.php || true
+
+    - name: Test Nextcloud iOS
+      run: |
+        set -o pipefail && \
+        xcodebuild test-without-building \
+        -xctestrun $(find . -type f -name "*.xctestrun") \
+        -destination "${{ env.DESTINATION }}" \
+        -derivedDataPath "DerivedData" \
+        -test-iterations 3 \
+        -retry-tests-on-failure \
+        -resultBundlePath "TestResult.xcresult" \
+        | xcbeautify --quieter
+
+    - name: Upload test results
+      uses: actions/upload-artifact@v4
+      if: ${{ !cancelled() }}
+      with:
+        name: TestResult.xcresult
+        path: "TestResult.xcresult"
+

+ 0 - 3
.slather.yml

@@ -1,3 +0,0 @@
-coverage_service: cobertura_xml
-xcodeproj: Nextcloud.xcodeproj
-scheme: Nextcloud

+ 2 - 1
.swiftlint.yml

@@ -44,5 +44,6 @@ excluded:
   - Brand/NCBrand.swift
   - Brand/NCBrand.swift
   - iOSClient/NCGlobal.swift
   - iOSClient/NCGlobal.swift
   - iOSClient/Utility/NCLivePhoto.swift
   - iOSClient/Utility/NCLivePhoto.swift
-
+  - DerivedData
+  
 reporter: "xcode"
 reporter: "xcode"

+ 1 - 1
Brand/Database.swift

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

+ 8 - 4
Brand/Intro/NCIntroViewController.swift

@@ -34,6 +34,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
     @IBOutlet weak var pageControl: UIPageControl!
     @IBOutlet weak var pageControl: UIPageControl!
 
 
     @objc weak var delegate: NCIntroViewController?
     @objc weak var delegate: NCIntroViewController?
+
     private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     private let titles = [NSLocalizedString("_intro_1_title_", comment: ""), NSLocalizedString("_intro_2_title_", comment: ""), NSLocalizedString("_intro_3_title_", comment: ""), NSLocalizedString("_intro_4_title_", comment: "")]
     private let titles = [NSLocalizedString("_intro_1_title_", comment: ""), NSLocalizedString("_intro_2_title_", comment: ""), NSLocalizedString("_intro_3_title_", comment: ""), NSLocalizedString("_intro_4_title_", comment: "")]
     private let images = [UIImage(named: "intro1"), UIImage(named: "intro2"), UIImage(named: "intro3"), UIImage(named: "intro4")]
     private let images = [UIImage(named: "intro1"), UIImage(named: "intro2"), UIImage(named: "intro3"), UIImage(named: "intro4")]
@@ -113,8 +114,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
     }
     }
 
 
     override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
     override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
-        pageControl.currentPage = 0
-        introCollectionView.collectionViewLayout.invalidateLayout()
+        super.viewWillTransition(to: size, with: coordinator)
+        coordinator.animate(alongsideTransition: nil) { _ in
+            self.pageControl?.currentPage = 0
+            self.introCollectionView?.collectionViewLayout.invalidateLayout()
+        }
     }
     }
 
 
     @objc func autoScroll() {
     @objc func autoScroll() {
@@ -158,11 +162,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
     }
     }
 
 
     @IBAction func login(_ sender: Any) {
     @IBAction func login(_ sender: Any) {
-        appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
+        appDelegate.openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
     }
     }
 
 
     @IBAction func signup(_ sender: Any) {
     @IBAction func signup(_ sender: Any) {
-        appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introSignup, openLoginWeb: false)
+        appDelegate.openLogin(selector: NCGlobal.shared.introSignup, openLoginWeb: false)
     }
     }
 
 
     @IBAction func host(_ sender: Any) {
     @IBAction func host(_ sender: Any) {

+ 26 - 1
Brand/NCBrand.swift

@@ -149,7 +149,7 @@ class NCBrandColor: NSObject {
     @objc public let customer: UIColor = UIColor(red: 0.0 / 255.0, green: 130.0 / 255.0, blue: 201.0 / 255.0, alpha: 1.0)         // BLU NC : #0082c9
     @objc public let customer: UIColor = UIColor(red: 0.0 / 255.0, green: 130.0 / 255.0, blue: 201.0 / 255.0, alpha: 1.0)         // BLU NC : #0082c9
     @objc public var customerText: UIColor = .white
     @objc public var customerText: UIColor = .white
 
 
-    @objc public var brand: UIColor                                                                                         // don't touch me
+    @objc private var brand: UIColor                                                                                         // don't touch me
     @objc public var brandElement: UIColor                                                                                  // don't touch me
     @objc public var brandElement: UIColor                                                                                  // don't touch me
     @objc public var brandText: UIColor                                                                                     // don't touch me
     @objc public var brandText: UIColor                                                                                     // don't touch me
 
 
@@ -161,12 +161,37 @@ class NCBrandColor: NSObject {
     public var themingColorElement: String = ""
     public var themingColorElement: String = ""
     public var themingColorText: String = ""
     public var themingColorText: String = ""
 
 
+    @objc public let iconImageColor: UIColor = .label
+    @objc public let iconImageColor2: UIColor = .secondaryLabel
+    @objc public let iconImageMultiColors: [UIColor] = [.secondaryLabel, .label]
+
+    @objc public let textColor: UIColor = .label
+    @objc public let textColor2: UIColor = .secondaryLabel
+
     @objc public var systemMint: UIColor {
     @objc public var systemMint: UIColor {
         get {
         get {
             return UIColor(red: 0.0 / 255.0, green: 199.0 / 255.0, blue: 190.0 / 255.0, alpha: 1.0)
             return UIColor(red: 0.0 / 255.0, green: 199.0 / 255.0, blue: 190.0 / 255.0, alpha: 1.0)
         }
         }
     }
     }
 
 
+    @objc public var documentIconColor: UIColor {
+        get {
+            return UIColor(hex: "#49abe9")!
+        }
+    }
+
+    @objc public var spreadsheetIconColor: UIColor {
+        get {
+            return UIColor(hex: "#9abd4e")!
+        }
+    }
+
+    @objc public var presentationIconColor: UIColor {
+        get {
+            return UIColor(hex: "#f0965f")!
+        }
+    }
+
     override init() {
     override init() {
         brand = customer
         brand = customer
         brandElement = customer
         brandElement = customer

+ 4 - 0
Brand/Notification_Service_Extension.entitlements

@@ -2,10 +2,14 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <plist version="1.0">
 <dict>
 <dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
 	<key>com.apple.security.application-groups</key>
 	<key>com.apple.security.application-groups</key>
 	<array>
 	<array>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 	</array>
 	</array>
+	<key>com.apple.security.network.client</key>
+	<true/>
 	<key>keychain-access-groups</key>
 	<key>keychain-access-groups</key>
 	<array>
 	<array>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>

+ 4 - 0
Brand/Share.entitlements

@@ -2,10 +2,14 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <plist version="1.0">
 <dict>
 <dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
 	<key>com.apple.security.application-groups</key>
 	<key>com.apple.security.application-groups</key>
 	<array>
 	<array>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 	</array>
 	</array>
+	<key>com.apple.security.network.client</key>
+	<true/>
 	<key>keychain-access-groups</key>
 	<key>keychain-access-groups</key>
 	<array>
 	<array>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>

+ 8 - 4
Brand/Widget.entitlements

@@ -2,13 +2,17 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <plist version="1.0">
 <dict>
 <dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
 	<key>com.apple.security.application-groups</key>
 	<key>com.apple.security.application-groups</key>
 	<array>
 	<array>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 	</array>
 	</array>
-    <key>keychain-access-groups</key>
-    <array>
-        <string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
-    </array>
+	<key>com.apple.security.network.client</key>
+	<true/>
+	<key>keychain-access-groups</key>
+	<array>
+		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
+	</array>
 </dict>
 </dict>
 </plist>
 </plist>

+ 4 - 0
Brand/WidgetDashboardIntentHandler.entitlements

@@ -2,10 +2,14 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <plist version="1.0">
 <dict>
 <dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
 	<key>com.apple.security.application-groups</key>
 	<key>com.apple.security.application-groups</key>
 	<array>
 	<array>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 	</array>
 	</array>
+	<key>com.apple.security.network.client</key>
+	<true/>
 	<key>keychain-access-groups</key>
 	<key>keychain-access-groups</key>
 	<array>
 	<array>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>

+ 12 - 0
Brand/iOSClient.entitlements

@@ -4,11 +4,23 @@
 <dict>
 <dict>
 	<key>aps-environment</key>
 	<key>aps-environment</key>
 	<string>development</string>
 	<string>development</string>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
 	<key>com.apple.security.application-groups</key>
 	<key>com.apple.security.application-groups</key>
 	<array>
 	<array>
 		<string>group.com.nextcloud.apps</string>
 		<string>group.com.nextcloud.apps</string>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 		<string>group.it.twsweb.Crypto-Cloud</string>
 	</array>
 	</array>
+	<key>com.apple.security.device.audio-input</key>
+	<true/>
+	<key>com.apple.security.device.camera</key>
+	<true/>
+	<key>com.apple.security.network.client</key>
+	<true/>
+	<key>com.apple.security.personal-information.location</key>
+	<true/>
+	<key>com.apple.security.personal-information.photos-library</key>
+	<true/>
 	<key>keychain-access-groups</key>
 	<key>keychain-access-groups</key>
 	<array>
 	<array>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
 		<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>

+ 17 - 2
Brand/iOSClient.plist

@@ -104,8 +104,6 @@
 	<true/>
 	<true/>
 	<key>UILaunchStoryboardName</key>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
 	<string>LaunchScreen</string>
-	<key>UIMainStoryboardFile</key>
-	<string>Main</string>
 	<key>UIRequiredDeviceCapabilities</key>
 	<key>UIRequiredDeviceCapabilities</key>
 	<array>
 	<array>
 		<string>armv7</string>
 		<string>armv7</string>
@@ -176,5 +174,22 @@
 			</dict>
 			</dict>
 		</dict>
 		</dict>
 	</array>
 	</array>
+	<key>UIApplicationSceneManifest</key>
+    	<dict>
+        	<key>UIApplicationSupportsMultipleScenes</key>
+        	<true/>
+        	<key>UISceneConfigurations</key>
+        	<dict>
+            		<key>UIWindowSceneSessionRoleApplication</key>
+            		<array>
+               			<dict>
+                  			<key>UISceneConfigurationName</key>
+                    			<string>Default Configuration</string>
+                    			<key>UISceneDelegateClassName</key>
+                    			<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
+               			</dict>
+            		</array>
+       		</dict>
+    </dict>
 </dict>
 </dict>
 </plist>
 </plist>

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

@@ -32,7 +32,7 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
     override func loadView() {
     override func loadView() {
         super.loadView()
         super.loadView()
 
 
-        view.backgroundColor = NCBrandColor.shared.brand
+        view.backgroundColor = NCBrandColor.shared.brandElement
         titleError.textColor = NCBrandColor.shared.brandText
         titleError.textColor = NCBrandColor.shared.brandText
         cancelButton.setTitleColor(NCBrandColor.shared.brandText, for: .normal)
         cancelButton.setTitleColor(NCBrandColor.shared.brandText, for: .normal)
 
 

+ 0 - 2
File Provider Extension/FileProviderData.swift

@@ -93,7 +93,6 @@ class fileProviderData: NSObject {
             NCManageDatabase.shared.setCapabilities(account: account)
             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)
             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)
             return tableAccount.init(value: activeAccount)
         }
         }
@@ -117,7 +116,6 @@ class fileProviderData: NSObject {
                 NCManageDatabase.shared.setCapabilities(account: account)
                 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)
                 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)
                 return tableAccount.init(value: activeAccount)
             }
             }

+ 4 - 8
File Provider Extension/FileProviderEnumerator.swift

@@ -212,24 +212,20 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
 
 
                     if error == .success {
                     if error == .success {
                         DispatchQueue.global().async {
                         DispatchQueue.global().async {
-                            NCManageDatabase.shared.convertFilesToMetadatas(files, useMetadataFolder: true) { _, metadatasFolder, metadatas in
+                            NCManageDatabase.shared.convertFilesToMetadatas(files, useFirstAsMetadataFolder: true) { _, metadatas in
                                 let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal)
                                 let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal)
                                 NCManageDatabase.shared.updateMetadatas(metadatas, predicate: predicate)
                                 NCManageDatabase.shared.updateMetadatas(metadatas, predicate: predicate)
-                                for metadata in metadatasFolder {
-                                    let serverUrl = metadata.serverUrl + "/" + metadata.fileNameView
-                                    NCManageDatabase.shared.addDirectory(e2eEncrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, permissions: metadata.permissions, serverUrl: serverUrl, account: metadata.account)
-                                }
-                                let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
+                                let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
                                 completionHandler(metadatas)
                                 completionHandler(metadatas)
                             }
                             }
                         }
                         }
                     } else {
                     } else {
-                        let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
+                        let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
                         completionHandler(metadatas)
                         completionHandler(metadatas)
                     }
                     }
                 }
                 }
             } else {
             } else {
-                let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
+                let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", fileProviderData.shared.account, serverUrl), sorted: "fileName", ascending: true)
                 completionHandler(metadatas)
                 completionHandler(metadatas)
             }
             }
         }
         }

+ 1 - 1
File Provider Extension/FileProviderExtension.swift

@@ -51,7 +51,7 @@ import Alamofire
  
  
    -------------------------------------------------------------------------------------------------------------------------------------------- */
    -------------------------------------------------------------------------------------------------------------------------------------------- */
 
 
-class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
+class FileProviderExtension: NSFileProviderExtension {
 
 
     var outstandingSessionTasks: [URL: URLSessionTask] = [:]
     var outstandingSessionTasks: [URL: URLSessionTask] = [:]
     var outstandingOcIdTemp: [String: String] = [:]
     var outstandingOcIdTemp: [String: String] = [:]

+ 0 - 3
Gemfile

@@ -1,3 +0,0 @@
-source 'https://rubygems.org'
-gem 'slather'
-gem 'xcpretty' 

File diff suppressed because it is too large
+ 314 - 123
Nextcloud.xcodeproj/project.pbxproj


+ 23 - 3
Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme

@@ -21,7 +21,7 @@
             </BuildableReference>
             </BuildableReference>
          </BuildActionEntry>
          </BuildActionEntry>
          <BuildActionEntry
          <BuildActionEntry
-            buildForTesting = "NO"
+            buildForTesting = "YES"
             buildForRunning = "NO"
             buildForRunning = "NO"
             buildForProfiling = "NO"
             buildForProfiling = "NO"
             buildForArchiving = "NO"
             buildForArchiving = "NO"
@@ -35,7 +35,7 @@
             </BuildableReference>
             </BuildableReference>
          </BuildActionEntry>
          </BuildActionEntry>
          <BuildActionEntry
          <BuildActionEntry
-            buildForTesting = "NO"
+            buildForTesting = "YES"
             buildForRunning = "NO"
             buildForRunning = "NO"
             buildForProfiling = "NO"
             buildForProfiling = "NO"
             buildForArchiving = "NO"
             buildForArchiving = "NO"
@@ -49,7 +49,7 @@
             </BuildableReference>
             </BuildableReference>
          </BuildActionEntry>
          </BuildActionEntry>
          <BuildActionEntry
          <BuildActionEntry
-            buildForTesting = "NO"
+            buildForTesting = "YES"
             buildForRunning = "NO"
             buildForRunning = "NO"
             buildForProfiling = "NO"
             buildForProfiling = "NO"
             buildForArchiving = "NO"
             buildForArchiving = "NO"
@@ -92,6 +92,26 @@
                ReferencedContainer = "container:Nextcloud.xcodeproj">
                ReferencedContainer = "container:Nextcloud.xcodeproj">
             </BuildableReference>
             </BuildableReference>
          </TestableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "C04E2F1F2A17BB4D001BAD85"
+               BuildableName = "NextcloudIntegrationTests.xctest"
+               BlueprintName = "NextcloudIntegrationTests"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "C0046CD92A17B98400D87C9D"
+               BuildableName = "NextcloudUITests.xctest"
+               BlueprintName = "NextcloudUITests"
+               ReferencedContainer = "container:Nextcloud.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
       </Testables>
       </Testables>
    </TestAction>
    </TestAction>
    <LaunchAction
    <LaunchAction

+ 1 - 1
Share/NCShareCell.swift

@@ -63,7 +63,7 @@ class NCShareCell: UITableViewCell {
         let fileSize = utilityFileSystem.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
         let fileSize = utilityFileSystem.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
         sizeCell?.text = utilityFileSystem.transformedSize(fileSize)
         sizeCell?.text = utilityFileSystem.transformedSize(fileSize)
 
 
-        moreButton?.setImage(utility.loadImage(named: "more").image(color: .label, size: 15), for: .normal)
+        moreButton?.setImage(NCImageCache.images.buttonMore, for: .normal)
     }
     }
 
 
     @IBAction func buttonTapped(_ sender: Any) {
     @IBAction func buttonTapped(_ sender: Any) {

+ 3 - 3
Share/NCShareExtension+DataSource.swift

@@ -44,11 +44,11 @@ extension NCShareExtension: UICollectionViewDelegate {
         if kind == UICollectionView.elementKindSectionHeader {
         if kind == UICollectionView.elementKindSectionHeader {
             guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderEmptyData", for: indexPath) as? NCSectionHeaderEmptyData else { return NCSectionHeaderEmptyData() }
             guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderEmptyData", for: indexPath) as? NCSectionHeaderEmptyData else { return NCSectionHeaderEmptyData() }
             if self.dataSourceTask?.state == .running {
             if self.dataSourceTask?.state == .running {
-                header.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
+                header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.brandElement])
                 header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
                 header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
                 header.emptyDescription.text = ""
                 header.emptyDescription.text = ""
             } else {
             } else {
-                header.emptyImage.image = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
+                header.emptyImage.image = NCImageCache.images.folder
                 header.emptyTitle.text = NSLocalizedString("_files_no_folders_", comment: "")
                 header.emptyTitle.text = NSLocalizedString("_files_no_folders_", comment: "")
                 header.emptyDescription.text = ""
                 header.emptyDescription.text = ""
             }
             }
@@ -91,7 +91,7 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.indexPath = indexPath
         cell.indexPath = indexPath
         cell.fileUser = metadata.ownerId
         cell.fileUser = metadata.ownerId
         cell.labelTitle.text = metadata.fileNameView
         cell.labelTitle.text = metadata.fileNameView
-        cell.labelTitle.textColor = .label
+        cell.labelTitle.textColor = NCBrandColor.shared.textColor
 
 
         cell.imageSelect.image = nil
         cell.imageSelect.image = nil
         cell.imageStatus.image = nil
         cell.imageStatus.image = nil

+ 2 - 0
Share/NCShareExtension+NCDelegate.swift

@@ -55,6 +55,8 @@ extension NCShareExtension: NCAccountRequestDelegate {
         self.present(popup, animated: true)
         self.present(popup, animated: true)
     }
     }
 
 
+    func accountRequestAddAccount() { }
+
     func accountRequestChangeAccount(account: String) {
     func accountRequestChangeAccount(account: String) {
         guard let activeAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) else {
         guard let activeAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) else {
             cancel(with: NCShareExtensionError.noAccount)
             cancel(with: NCShareExtensionError.noAccount)

+ 7 - 4
Share/NCShareExtension.swift

@@ -100,14 +100,13 @@ class NCShareExtension: UIViewController {
         commandViewHeightConstraint.constant = heightCommandView
         commandViewHeightConstraint.constant = heightCommandView
 
 
         createFolderView.layer.cornerRadius = 10
         createFolderView.layer.cornerRadius = 10
-        createFolderImage.image = utility.loadImage(named: "folder.badge.plus", color: .label)
+        createFolderImage.image = utility.loadImage(named: "folder.badge.plus", colors: [NCBrandColor.shared.iconImageColor])
         createFolderLabel.text = NSLocalizedString("_create_folder_", comment: "")
         createFolderLabel.text = NSLocalizedString("_create_folder_", comment: "")
         let createFolderGesture = UITapGestureRecognizer(target: self, action: #selector(actionCreateFolder))
         let createFolderGesture = UITapGestureRecognizer(target: self, action: #selector(actionCreateFolder))
         createFolderView.addGestureRecognizer(createFolderGesture)
         createFolderView.addGestureRecognizer(createFolderGesture)
 
 
         uploadView.layer.cornerRadius = 10
         uploadView.layer.cornerRadius = 10
 
 
-        // uploadImage.image = utility).loadImage(named: "square.and.arrow.up", color: .label)
         uploadLabel.text = NSLocalizedString("_upload_", comment: "")
         uploadLabel.text = NSLocalizedString("_upload_", comment: "")
         uploadLabel.textColor = .systemBlue
         uploadLabel.textColor = .systemBlue
         let uploadGesture = UITapGestureRecognizer(target: self, action: #selector(actionUpload))
         let uploadGesture = UITapGestureRecognizer(target: self, action: #selector(actionUpload))
@@ -137,6 +136,7 @@ class NCShareExtension: UIViewController {
         hud.indicatorView = JGProgressHUDRingIndicatorView()
         hud.indicatorView = JGProgressHUDRingIndicatorView()
         if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
         if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
             indicatorView.ringWidth = 1.5
             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)
         NotificationCenter.default.addObserver(self, selector: #selector(didCreateFolder(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil)
@@ -160,8 +160,10 @@ class NCShareExtension: UIViewController {
             self.filesName = fileNames
             self.filesName = fileNames
             DispatchQueue.main.async { self.setCommandView() }
             DispatchQueue.main.async { self.setCommandView() }
         }
         }
-        NCPasscode.shared.presentPasscode(viewController: self, delegate: self) {
-            NCPasscode.shared.enableTouchFaceID()
+        if NCKeychain().presentPasscode {
+            NCPasscode.shared.presentPasscode(viewController: self, delegate: self) {
+                NCPasscode.shared.enableTouchFaceID()
+            }
         }
         }
     }
     }
 
 
@@ -383,6 +385,7 @@ extension NCShareExtension {
             }
             }
         } else {
         } else {
             hud.indicatorView = JGProgressHUDSuccessIndicatorView()
             hud.indicatorView = JGProgressHUDSuccessIndicatorView()
+            hud.indicatorView?.tintColor = NCBrandColor.shared.brandElement
             hud.textLabel.text = NSLocalizedString("_success_", comment: "")
             hud.textLabel.text = NSLocalizedString("_success_", comment: "")
             DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
             DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                 self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
                 self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)

+ 0 - 18
Sourcery/EnvVars.stencil

@@ -1,18 +0,0 @@
-//
-//  EnvVars.stencil.swift
-//  NextcloudIntegrationTests
-//
-//  Created by Milen on 31.05.23.
-//  Copyright © 2023 Marino Faggiana. All rights reserved.
-//
-
-import Foundation
-
-/**
-This is generated from the .env-vars file in the root directory. If there is an environment variable here that is needed and not filled, please look into this file.
- */
- public struct EnvVars {
-  static let testUser = "{{ argument.TEST_USER }}"
-  static let testAppPassword = "{{ argument.TEST_APP_PASSWORD }}"
-  static let testServerUrl = "{{ argument.TEST_SERVER_URL }}"
-}

BIN
Sourcery/bin/sourcery


+ 37 - 0
Tests/Common/BaseXCTestCase.swift

@@ -0,0 +1,37 @@
+//
+//  BaseXCTestCase.swift
+//  Nextcloud
+//
+//  Created by Milen on 20.03.24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import XCTest
+import Foundation
+import Alamofire
+import NextcloudKit
+@testable import Nextcloud
+
+class BaseXCTestCase: XCTestCase {
+    var appToken = ""
+
+    func setupAppToken() {
+        let expectation = expectation(description: "Should get app token")
+
+        NextcloudKit.shared.getAppPassword(serverUrl: TestConstants.server, username: TestConstants.username, password: TestConstants.password) { token, data, error in
+            XCTAssertEqual(error.errorCode, 0)
+            XCTAssertNotNil(token)
+
+            guard let token else { return XCTFail() }
+            
+            self.appToken = token
+            expectation.fulfill()
+        }
+        
+        waitForExpectations(timeout: TestConstants.timeoutLong)
+    }
+
+    override func setUpWithError() throws {
+        setupAppToken()
+    }
+}

+ 30 - 0
Tests/Common/TestConstants.swift

@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2023 Marcel Müller <marcel-mueller@gmx.de>
+//
+// Author Marcel Müller <marcel-mueller@gmx.de>
+//
+// GNU GPL version 3 or any later version
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+
+public class TestConstants {
+    static let timeoutLong: Double = 600
+    static let server = "http://localhost:8080"
+    static let username = "admin"
+    static let password = "admin"
+    static let account = "\(username) \(server)"
+}

+ 2 - 11
Tests/NextcloudIntegrationTests/BaseIntegrationXCTestCase.swift

@@ -20,17 +20,8 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 //
 
 
-import XCTest
-@testable import NextcloudKit
-
-class BaseIntegrationXCTestCase: XCTestCase {
-    internal let baseUrl = EnvVars.testServerUrl
-    internal let user = EnvVars.testUser
-    internal let userId = EnvVars.testUser
-    internal let password = EnvVars.testAppPassword
-    internal lazy var account = "\(userId) \(baseUrl)"
-
-    internal var randomInt: Int {
+class BaseIntegrationXCTestCase: BaseXCTestCase {
+    var randomInt: Int {
         get {
         get {
             return Int.random(in: 1000...Int.max)
             return Int.random(in: 1000...Int.max)
         }
         }

+ 13 - 12
Tests/NextcloudIntegrationTests/FilesIntegrationTests.swift

@@ -34,30 +34,30 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
         let expectation = expectation(description: "Should finish last callback")
         let expectation = expectation(description: "Should finish last callback")
 
 
         let folderName = "TestFolder\(randomInt)"
         let folderName = "TestFolder\(randomInt)"
-        let serverUrl = "\(baseUrl)/remote.php/dav/files/\(userId)"
+        let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)"
         let serverUrlFileName = "\(serverUrl)/\(folderName)"
         let serverUrlFileName = "\(serverUrl)/\(folderName)"
 
 
-        NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: baseUrl)
+        NextcloudKit.shared.setup(account: TestConstants.account, user: TestConstants.username, userId: TestConstants.username, password: appToken, urlBase: TestConstants.server)
 
 
         // Test creating folder
         // Test creating folder
-        NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, account: account, urlBase: baseUrl, userId: userId, withPush: true) { error in
+        NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, account: TestConstants.account, urlBase: TestConstants.server, userId: TestConstants.username, withPush: true, sceneIdentifier: nil) { error in
             XCTAssertEqual(NKError.success.errorCode, error.errorCode)
             XCTAssertEqual(NKError.success.errorCode, error.errorCode)
             XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
             XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
 
 
-            Thread.sleep(forTimeInterval: 0.2)
+            Thread.sleep(forTimeInterval: 1)
 
 
             // Test reading folder, should exist
             // Test reading folder, should exist
-            NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: self.user) { account, metadataFolder, metadatas, metadatasUpdate, metadatasLocalUpdate, metadatasDelete, error in
-                XCTAssertEqual(self.account, account)
+            NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: TestConstants.username) { account, metadataFolder, _, _, _, _ in
+                XCTAssertEqual(TestConstants.account, account)
                 XCTAssertEqual(NKError.success.errorCode, error.errorCode)
                 XCTAssertEqual(NKError.success.errorCode, error.errorCode)
                 XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
                 XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
                 XCTAssertEqual(metadataFolder?.fileName, folderName)
                 XCTAssertEqual(metadataFolder?.fileName, folderName)
-
+                
                 // Check Realm directory, should exist
                 // Check Realm directory, should exist
                 let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
                 let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
                 XCTAssertNotNil(directory)
                 XCTAssertNotNil(directory)
 
 
-                Thread.sleep(forTimeInterval: 0.2)
+                Thread.sleep(forTimeInterval: 1)
 
 
                 Task {
                 Task {
                     // Test deleting folder
                     // Test deleting folder
@@ -66,13 +66,14 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
                     XCTAssertEqual(NKError.success.errorCode, error.errorCode)
                     XCTAssertEqual(NKError.success.errorCode, error.errorCode)
                     XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
                     XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
 
 
-                    try await Task.sleep(for: .milliseconds(200))
+                    try await Task.sleep(for: .seconds(1))
 
 
                     // Test reading folder, should NOT exist
                     // Test reading folder, should NOT exist
-                    NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: self.user) { account, metadataFolder, metadatas, metadatasUpdate, metadatasLocalUpdate, metadatasDelete, error in
+                    NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: TestConstants.username) { account, metadataFolder, _, _, _, _ in
+
                         defer { expectation.fulfill() }
                         defer { expectation.fulfill() }
 
 
-                        XCTAssertEqual(404, error.errorCode)
+                        XCTAssertEqual(0, error.errorCode)
                         XCTAssertNil(metadataFolder?.fileName)
                         XCTAssertNil(metadataFolder?.fileName)
 
 
                         // Check Realm directory, should NOT exist
                         // Check Realm directory, should NOT exist
@@ -83,6 +84,6 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
             }
             }
         }
         }
         
         
-        waitForExpectations(timeout: 100)
+        waitForExpectations(timeout: TestConstants.timeoutLong)
     }
     }
 }
 }

+ 0 - 33
Tests/NextcloudSnapshotTests/Extensions/SwiftUIView+Extensions.swift

@@ -1,33 +0,0 @@
-//
-//  SwiftUIView+Extensions.swift
-//  Nextcloud
-//
-//  Created by Milen on 06.06.23.
-//  Copyright © 2023 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import Foundation
-import SwiftUI
-
-extension SwiftUI.View {
-    func toVC() -> UIViewController {
-        let vc = UIHostingController (rootView: self)
-        vc.view.frame = UIScreen.main.bounds
-        return vc
-    }
-}

+ 0 - 37
Tests/NextcloudSnapshotTests/NextcloudSnapshotTests.swift

@@ -1,37 +0,0 @@
-//
-//  NextcloudSnapshotTests.swift
-//  NextcloudSnapshotTests
-//
-//  Created by Milen on 06.06.23.
-//  Copyright © 2023 Marino Faggiana. All rights reserved.
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import XCTest
-import SnapshotTesting
-import SnapshotTestingHEIC
-import PreviewSnapshotsTesting
-import SwiftUI
-@testable import Nextcloud
-
-final class NextcloudSnapshotTests: XCTestCase {
-    func test_HUDView() {
-        HUDView_Previews.snapshots.assertSnapshots(as: .imageHEIC)
-    }
-
-    func test_CapalitiesView() {
-        NCCapabilitiesView_Previews.snapshots.assertSnapshots(as: .imageHEIC)
-    }
-}

BIN
Tests/NextcloudSnapshotTests/__Snapshots__/NextcloudSnapshotTests/test_CapalitiesView.DefaultPreviewConfiguration.heic


BIN
Tests/NextcloudSnapshotTests/__Snapshots__/NextcloudSnapshotTests/test_HUDView.DefaultPreviewConfiguration.heic


+ 3 - 5
Tests/NextcloudUITests/BaseUIXCTestCase.swift

@@ -21,9 +21,7 @@
 
 
 import XCTest
 import XCTest
 
 
-class BaseUIXCTestCase: XCTestCase {
-    let timeoutSeconds: Double = 100
-
+class BaseUIXCTestCase: BaseXCTestCase {
     override final class var runsForEachTargetApplicationUIConfiguration: Bool {
     override final class var runsForEachTargetApplicationUIConfiguration: Bool {
         false
         false
     }
     }
@@ -31,13 +29,13 @@ class BaseUIXCTestCase: XCTestCase {
     internal func waitForEnabled(object: Any?) {
     internal func waitForEnabled(object: Any?) {
         let predicate = NSPredicate(format: "enabled == true")
         let predicate = NSPredicate(format: "enabled == true")
         expectation(for: predicate, evaluatedWith: object, handler: nil)
         expectation(for: predicate, evaluatedWith: object, handler: nil)
-        waitForExpectations(timeout: timeoutSeconds, handler: nil)
+        waitForExpectations(timeout: TestConstants.timeoutLong, handler: nil)
     }
     }
 
 
     internal func waitForHittable(object: Any?) {
     internal func waitForHittable(object: Any?) {
         let predicate = NSPredicate(format: "hittable == true")
         let predicate = NSPredicate(format: "hittable == true")
         expectation(for: predicate, evaluatedWith: object, handler: nil)
         expectation(for: predicate, evaluatedWith: object, handler: nil)
-        waitForExpectations(timeout: timeoutSeconds, handler: nil)
+        waitForExpectations(timeout: TestConstants.timeoutLong, handler: nil)
     }
     }
 
 
     internal func waitForEnabledAndHittable(object: Any?) {
     internal func waitForEnabledAndHittable(object: Any?) {

+ 18 - 20
Tests/NextcloudUITests/LoginUITests.swift

@@ -20,14 +20,10 @@
 //
 //
 
 
 import XCTest
 import XCTest
+import NextcloudKit
+@testable import Nextcloud
 
 
 final class LoginUITests: BaseUIXCTestCase {
 final class LoginUITests: BaseUIXCTestCase {
-    private let baseUrl = EnvVars.testServerUrl
-    private let user = EnvVars.testUser
-    private let userId = EnvVars.testUser
-    private let password = EnvVars.testAppPassword
-    private lazy var account = "\(userId) \(baseUrl)"
-
     let app = XCUIApplication()
     let app = XCUIApplication()
 
 
     override func setUp() {
     override func setUp() {
@@ -38,40 +34,42 @@ final class LoginUITests: BaseUIXCTestCase {
         app.launch()
         app.launch()
 
 
         let loginButton = app.buttons["Log in"]
         let loginButton = app.buttons["Log in"]
-        XCTAssert(loginButton.waitForExistence(timeout: timeoutSeconds))
+        XCTAssert(loginButton.waitForExistence(timeout: TestConstants.timeoutLong))
         loginButton.tap()
         loginButton.tap()
 
 
         let serverAddressHttpsTextField = app.textFields["Server address https:// …"]
         let serverAddressHttpsTextField = app.textFields["Server address https:// …"]
         serverAddressHttpsTextField.tap()
         serverAddressHttpsTextField.tap()
-        serverAddressHttpsTextField.typeText(baseUrl)
-        let button = app.windows.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .button).element(boundBy: 0)
+        serverAddressHttpsTextField.typeText(TestConstants.server)
+        let button = app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .button).element(boundBy: 0)
         button.tap()
         button.tap()
 
 
         let webViewsQuery = app.webViews.webViews.webViews
         let webViewsQuery = app.webViews.webViews.webViews
         let loginButton2 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements.matching(identifier: \"Nextcloud\")",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
         let loginButton2 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements.matching(identifier: \"Nextcloud\")",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
-        XCTAssert(loginButton2.waitForExistence(timeout: timeoutSeconds))
+        XCTAssert(loginButton2.waitForExistence(timeout: TestConstants.timeoutLong))
         waitForEnabledAndHittable(object: loginButton2)
         waitForEnabledAndHittable(object: loginButton2)
         loginButton2.tap()
         loginButton2.tap()
 
 
-        let element = webViewsQuery/*@START_MENU_TOKEN@*/.otherElements["main"]/*[[".otherElements[\"Login – Nextcloud\"].otherElements[\"main\"]",".otherElements[\"main\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.children(matching: .other).element(boundBy: 1)
-        let usernameTextField = element.children(matching: .other).element(boundBy: 2).children(matching: .textField).element
-        XCTAssert(usernameTextField.waitForExistence(timeout: timeoutSeconds))
+        let usernameTextField = webViewsQuery.textFields["Login with username or email"]
+        XCTAssert(usernameTextField.waitForExistence(timeout: TestConstants.timeoutLong))
         usernameTextField.tap()
         usernameTextField.tap()
-        usernameTextField.typeText(user)
-        let passwordTextField = element.children(matching: .other).element(boundBy: 4).children(matching: .secureTextField).element
+        usernameTextField.typeText(TestConstants.username)
+
+        let passwordTextField = webViewsQuery/*@START_MENU_TOKEN@*/.secureTextFields["Password"]/*[[".otherElements[\"Login – Nextcloud\"]",".otherElements[\"main\"].secureTextFields[\"Password\"]",".secureTextFields[\"Password\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
+        XCTAssert(passwordTextField.waitForExistence(timeout: TestConstants.timeoutLong))
         passwordTextField.tap()
         passwordTextField.tap()
-        passwordTextField.typeText(password)
+        passwordTextField.typeText(TestConstants.username)
+
         let loginButton3 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements[\"Login – Nextcloud\"]",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
         let loginButton3 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements[\"Login – Nextcloud\"]",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
-        XCTAssert(loginButton3.waitForExistence(timeout: timeoutSeconds))
+        XCTAssert(loginButton3.waitForExistence(timeout: TestConstants.timeoutLong))
         loginButton3.tap()
         loginButton3.tap()
 
 
         let grantAccessButton = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Grant access"]/*[[".otherElements.matching(identifier: \"Nextcloud\")",".otherElements[\"main\"].buttons[\"Grant access\"]",".buttons[\"Grant access\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
         let grantAccessButton = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Grant access"]/*[[".otherElements.matching(identifier: \"Nextcloud\")",".otherElements[\"main\"].buttons[\"Grant access\"]",".buttons[\"Grant access\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
-        XCTAssert(grantAccessButton.waitForExistence(timeout: timeoutSeconds))
+        XCTAssert(grantAccessButton.waitForExistence(timeout: TestConstants.timeoutLong))
         waitForEnabledAndHittable(object: grantAccessButton)
         waitForEnabledAndHittable(object: grantAccessButton)
         grantAccessButton.tap()
         grantAccessButton.tap()
 
 
         // Check if we are in the home screen
         // Check if we are in the home screen
-        XCTAssert(app.navigationBars["Nextcloud"].waitForExistence(timeout: timeoutSeconds))
-        XCTAssert(app.tabBars["Tab Bar"].waitForExistence(timeout: timeoutSeconds))
+        XCTAssert(app.navigationBars["Nextcloud"].waitForExistence(timeout: TestConstants.timeoutLong))
+        XCTAssert(app.tabBars["Tab Bar"].waitForExistence(timeout: TestConstants.timeoutLong))
     }
     }
 }
 }

+ 6 - 7
Widget/Dashboard/DashboardData.swift

@@ -65,13 +65,12 @@ let dashboardDatasTest: [DashboardData] = [
 ]
 ]
 
 
 func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
 func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
-
     if withButton {
     if withButton {
-        let height = Int((displaySize.height - 85) / 50)
-        return height
+        let items = Int((displaySize.height - 90) / 55)
+        return items
     } else {
     } else {
-        let height = Int((displaySize.height - 60) / 50)
-        return height
+        let items = Int((displaySize.height - 50) / 55)
+        return items
     }
     }
 }
 }
 
 
@@ -164,7 +163,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
     if let fileName = tableDashboard?.iconClass {
     if let fileName = tableDashboard?.iconClass {
         let fileNamePath: String = utilityFileSystem.directoryUserData + "/" + fileName + ".png"
         let fileNamePath: String = utilityFileSystem.directoryUserData + "/" + fileName + ".png"
         if let image = UIImage(contentsOfFile: fileNamePath) {
         if let image = UIImage(contentsOfFile: fileNamePath) {
-            imagetmp = image.withTintColor(.label, renderingMode: .alwaysOriginal)
+            imagetmp = image.withTintColor(NCBrandColor.shared.iconImageColor, renderingMode: .alwaysOriginal)
         }
         }
     }
     }
     let titleImage = imagetmp
     let titleImage = imagetmp
@@ -222,7 +221,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
                                     let path = (urlComponents.path as NSString)
                                     let path = (urlComponents.path as NSString)
                                     let colorString = ((path.lastPathComponent) as NSString).deletingPathExtension
                                     let colorString = ((path.lastPathComponent) as NSString).deletingPathExtension
                                     imageColor = UIColor(hex: colorString)
                                     imageColor = UIColor(hex: colorString)
-                                    icon = UIImage(systemName: "circle.fill")!
+                                    icon = utility.loadImage(named: "circle.fill")
                                 } else if let fileName = iconFileName {
                                 } else if let fileName = iconFileName {
                                     let fileNamePath: String = utilityFileSystem.directoryUserData + "/" + fileName + ".png"
                                     let fileNamePath: String = utilityFileSystem.directoryUserData + "/" + fileName + ".png"
                                     if FileManager().fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
                                     if FileManager().fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {

+ 9 - 7
Widget/Dashboard/DashboardWidgetView.swift

@@ -37,6 +37,7 @@ struct DashboardWidgetView: View {
                     Image(systemName: "checkmark")
                     Image(systemName: "checkmark")
                         .resizable()
                         .resizable()
                         .scaledToFit()
                         .scaledToFit()
+                        .font(Font.system(.body).weight(.light))
                         .frame(width: 50, height: 50)
                         .frame(width: 50, height: 50)
                     Text(NSLocalizedString("_no_items_", comment: ""))
                     Text(NSLocalizedString("_no_items_", comment: ""))
                         .font(.system(size: 25))
                         .font(.system(size: 25))
@@ -48,9 +49,7 @@ struct DashboardWidgetView: View {
             }
             }
 
 
             ZStack(alignment: .topLeading) {
             ZStack(alignment: .topLeading) {
-
                 HStack {
                 HStack {
-
                     Image(uiImage: entry.titleImage)
                     Image(uiImage: entry.titleImage)
                         .renderingMode(.template)
                         .renderingMode(.template)
                         .resizable()
                         .resizable()
@@ -143,8 +142,10 @@ struct DashboardWidgetView: View {
                                     .padding(.leading, 10)
                                     .padding(.leading, 10)
                                     .frame(height: 50)
                                     .frame(height: 50)
                                 }
                                 }
-                                Divider()
-                                    .padding(.leading, 54)
+                                if element != entry.datas.last {
+                                    Divider()
+                                        .padding(.leading, 54)
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
@@ -156,7 +157,7 @@ struct DashboardWidgetView: View {
 
 
                     HStack(spacing: 10) {
                     HStack(spacing: 10) {
 
 
-                        let brandColor = Color(NCBrandColor.shared.brand)
+                        let brandColor = Color(NCBrandColor.shared.brandElement)
                         let brandTextColor = Color(NCBrandColor.shared.brandText)
                         let brandTextColor = Color(NCBrandColor.shared.brandText)
 
 
                         ForEach(buttons, id: \.index) { element in
                         ForEach(buttons, id: \.index) { element in
@@ -181,12 +182,13 @@ struct DashboardWidgetView: View {
                         .resizable()
                         .resizable()
                         .scaledToFit()
                         .scaledToFit()
                         .frame(width: 15, height: 15)
                         .frame(width: 15, height: 15)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                        .font(Font.system(.body).weight(.light))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
 
 
                     Text(entry.footerText)
                     Text(entry.footerText)
                         .font(.caption2)
                         .font(.caption2)
                         .lineLimit(1)
                         .lineLimit(1)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                 }
                 }
                 .padding(.horizontal, 15.0)
                 .padding(.horizontal, 15.0)
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)

+ 19 - 34
Widget/Files/FilesData.swift

@@ -43,6 +43,7 @@ struct FilesData: Identifiable, Hashable {
     var title: String
     var title: String
     var subTitle: String
     var subTitle: String
     var url: URL
     var url: URL
+    var useTypeIconFile: Bool = false
 }
 }
 
 
 let filesDatasTest: [FilesData] = [
 let filesDatasTest: [FilesData] = [
@@ -79,9 +80,8 @@ func getTitleFilesWidget(account: tableAccount?) -> String {
 }
 }
 
 
 func getFilesItems(displaySize: CGSize) -> Int {
 func getFilesItems(displaySize: CGSize) -> Int {
-
-    let height = Int((displaySize.height - 100) / 50)
-    return height
+    let items = Int((displaySize.height - 90) / 55)
+    return items
 }
 }
 
 
 func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySize: CGSize, completion: @escaping (_ entry: FilesDataEntry) -> Void) {
 func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySize: CGSize, completion: @escaping (_ entry: FilesDataEntry) -> Void) {
@@ -107,24 +107,6 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
         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: "")))
         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: "")))
     }
     }
 
 
-    @Sendable func isLive(file: NKFile, files: [NKFile]) -> Bool {
-
-        let ext = (file.fileName as NSString).pathExtension.lowercased()
-        if ext != "mov" { return false }
-
-        let fileName = (file.fileName as NSString).deletingPathExtension.lowercased()
-        let fileNameViewMOV = fileName + ".mov"
-        let fileNameViewJPG = fileName + ".jpg"
-        let fileNameViewHEIC = fileName + ".heic"
-
-        let results = files.filter({ $0.fileName.lowercased() == fileNameViewJPG.lowercased() || $0.fileName.lowercased() == fileNameViewHEIC.lowercased() || $0.fileName.lowercased() == fileNameViewMOV.lowercased() })
-        if results.count == 2 {
-            return true
-        } else {
-            return false
-        }
-    }
-
     // NETWORKING
     // NETWORKING
     let password = NCKeychain().getPassword(account: account.account)
     let password = NCKeychain().getPassword(account: account.account)
     NextcloudKit.shared.setup(
     NextcloudKit.shared.setup(
@@ -181,7 +163,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
         </d:order>
         </d:order>
     </d:orderby>
     </d:orderby>
     <d:limit>
     <d:limit>
-        <d:nresults>50</d:nresults>
+        <d:nresults>25</d:nresults>
     </d:limit>
     </d:limit>
     </d:basicsearch>
     </d:basicsearch>
     </d:searchrequest>
     </d:searchrequest>
@@ -207,18 +189,20 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) widget session with level \(levelLog) " + versionNextcloudiOS)
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start \(NCBrandOptions.shared.brand) widget session with level \(levelLog) " + versionNextcloudiOS)
     }
     }
 
 
-    let options = NKRequestOptions(timeout: 90, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
+    let options = NKRequestOptions(timeout: 30, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
     NextcloudKit.shared.searchBodyRequest(serverUrl: account.urlBase, requestBody: requestBody, showHiddenFiles: NCKeychain().showHiddenFiles, options: options) { _, files, data, error in
     NextcloudKit.shared.searchBodyRequest(serverUrl: account.urlBase, requestBody: requestBody, showHiddenFiles: NCKeychain().showHiddenFiles, options: options) { _, files, data, error in
         Task {
         Task {
             var datas: [FilesData] = []
             var datas: [FilesData] = []
-            var imageRecent = UIImage(named: "file")!
             let title = getTitleFilesWidget(account: account)
             let title = getTitleFilesWidget(account: account)
             let files = files.sorted(by: { ($0.date as Date) > ($1.date as Date) })
             let files = files.sorted(by: { ($0.date as Date) > ($1.date as Date) })
 
 
             for file in files {
             for file in files {
+                var image: UIImage?
+                var useTypeIconFile = false
 
 
-                guard !file.directory else { continue }
-                guard !isLive(file: file, files: files) else { continue }
+                if file.directory || (!file.livePhotoFile.isEmpty && file.classFile == NKCommon.TypeClassFile.video.rawValue) {
+                    continue
+                }
 
 
                 // SUBTITLE
                 // SUBTITLE
                 let subTitle = utility.dateDiff(file.date as Date) + " · " + utilityFileSystem.transformedSize(file.size)
                 let subTitle = utility.dateDiff(file.date as Date) + " · " + utilityFileSystem.transformedSize(file.size)
@@ -232,27 +216,28 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 guard let url = URL(string: urlString) else { continue }
                 guard let url = URL(string: urlString) else { continue }
 
 
                 // IMAGE
                 // IMAGE
-                if !file.iconName.isEmpty {
-                    imageRecent = UIImage(named: file.iconName)!
-                }
-                if let image = utility.createFilePreviewImage(ocId: file.ocId, etag: file.etag, fileNameView: file.fileName, classFile: file.classFile, status: 0, createPreviewMedia: false) {
-                    imageRecent = image
+                if let result = utility.createFilePreviewImage(ocId: file.ocId, etag: file.etag, fileNameView: file.fileName, classFile: file.classFile, status: 0, createPreviewMedia: false) {
+                    image = result
                 } else if file.hasPreview {
                 } else if file.hasPreview {
                     let fileNamePathOrFileId = utilityFileSystem.getFileNamePath(file.fileName, serverUrl: file.serverUrl, urlBase: file.urlBase, userId: file.userId)
                     let fileNamePathOrFileId = utilityFileSystem.getFileNamePath(file.fileName, serverUrl: file.serverUrl, urlBase: file.urlBase, userId: file.userId)
                     let fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)
                     let fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(file.ocId, etag: file.etag)
                     let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)
                     let fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)
                     let sizePreview = NCUtility().getSizePreview(width: Int(file.width), height: Int(file.height))
                     let sizePreview = NCUtility().getSizePreview(width: Int(file.width), height: Int(file.height))
                     let (_, _, imageIcon, _, _, _) = await NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePathOrFileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, widthPreview: Int(sizePreview.width), heightPreview: Int(sizePreview.height), fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon, options: options)
                     let (_, _, imageIcon, _, _, _) = await NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePathOrFileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, widthPreview: Int(sizePreview.width), heightPreview: Int(sizePreview.height), fileNameIconLocalPath: fileNameIconLocalPath, sizeIcon: NCGlobal.shared.sizeIcon, options: options)
-                    if let image = imageIcon {
-                        imageRecent = image
+                    if let result = imageIcon {
+                         image = result
                     }
                     }
                 }
                 }
+                if image == nil {
+                    image = utility.loadImage(named: file.iconName, useTypeIconFile: true)
+                    useTypeIconFile = true
+                }
 
 
                 let isDirectoryE2EE = utilityFileSystem.isDirectoryE2EE(file: file)
                 let isDirectoryE2EE = utilityFileSystem.isDirectoryE2EE(file: file)
                 let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
                 let metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
 
 
                 // DATA
                 // DATA
-                let data = FilesData(id: metadata.ocId, image: imageRecent, title: metadata.fileNameView, subTitle: subTitle, url: url)
+                let data = FilesData(id: metadata.ocId, image: image!, title: metadata.fileNameView, subTitle: subTitle, url: url, useTypeIconFile: useTypeIconFile)
                 datas.append(data)
                 datas.append(data)
                 if datas.count == filesItems { break}
                 if datas.count == filesItems { break}
             }
             }

+ 30 - 27
Widget/Files/FilesWidgetView.swift

@@ -38,12 +38,12 @@ struct FilesWidgetView: View {
         let linkActionVoiceMemo: URL = URL(string: NCGlobal.shared.widgetActionVoiceMemo + parameterLink) != nil ? URL(string: NCGlobal.shared.widgetActionVoiceMemo + parameterLink)! : URL(string: NCGlobal.shared.widgetActionVoiceMemo)!
         let linkActionVoiceMemo: URL = URL(string: NCGlobal.shared.widgetActionVoiceMemo + parameterLink) != nil ? URL(string: NCGlobal.shared.widgetActionVoiceMemo + parameterLink)! : URL(string: NCGlobal.shared.widgetActionVoiceMemo)!
 
 
         GeometryReader { geo in
         GeometryReader { geo in
-
             if entry.isEmpty {
             if entry.isEmpty {
                 VStack(alignment: .center) {
                 VStack(alignment: .center) {
                     Image(systemName: "checkmark")
                     Image(systemName: "checkmark")
                         .resizable()
                         .resizable()
                         .scaledToFit()
                         .scaledToFit()
+                        .font(Font.system(.body).weight(.light))
                         .frame(width: 50, height: 50)
                         .frame(width: 50, height: 50)
                     Text(NSLocalizedString("_no_items_", comment: ""))
                     Text(NSLocalizedString("_no_items_", comment: ""))
                         .font(.system(size: 25))
                         .font(.system(size: 25))
@@ -57,7 +57,6 @@ struct FilesWidgetView: View {
             ZStack(alignment: .topLeading) {
             ZStack(alignment: .topLeading) {
 
 
                 HStack {
                 HStack {
-
                     Text(entry.tile)
                     Text(entry.tile)
                         .font(.system(size: 12))
                         .font(.system(size: 12))
                         .fontWeight(.bold)
                         .fontWeight(.bold)
@@ -70,39 +69,43 @@ struct FilesWidgetView: View {
 
 
                 if !entry.isEmpty {
                 if !entry.isEmpty {
                     VStack(alignment: .leading) {
                     VStack(alignment: .leading) {
-
                         VStack(spacing: 0) {
                         VStack(spacing: 0) {
-
                             ForEach(entry.datas, id: \.id) { element in
                             ForEach(entry.datas, id: \.id) { element in
-
                                 Link(destination: element.url) {
                                 Link(destination: element.url) {
-
                                     HStack {
                                     HStack {
-
-                                        Image(uiImage: element.image)
-                                            .resizable()
-                                            .scaledToFill()
-                                            .frame(width: 35, height: 35)
-                                            .clipped()
-                                            .cornerRadius(5)
+                                        if element.useTypeIconFile {
+                                            Image(uiImage: element.image)
+                                                .resizable()
+                                                .renderingMode(.template)
+                                                .foregroundColor(Color(NCBrandColor.shared.iconImageColor2))
+                                                .scaledToFit()
+                                                .frame(width: 35, height: 35)
+                                        } else {
+                                            Image(uiImage: element.image)
+                                                .resizable()
+                                                .scaledToFill()
+                                                .frame(width: 35, height: 35)
+                                                .clipped()
+                                                .cornerRadius(5)
+                                        }
 
 
                                         VStack(alignment: .leading, spacing: 2) {
                                         VStack(alignment: .leading, spacing: 2) {
-
                                             Text(element.title)
                                             Text(element.title)
                                                 .font(.system(size: 12))
                                                 .font(.system(size: 12))
                                                 .fontWeight(.regular)
                                                 .fontWeight(.regular)
-
                                             Text(element.subTitle)
                                             Text(element.subTitle)
                                                 .font(.system(size: CGFloat(10)))
                                                 .font(.system(size: CGFloat(10)))
-                                                .foregroundColor(Color(.systemGray))
+                                                .foregroundColor(Color(NCBrandColor.shared.iconImageColor2))
                                         }
                                         }
                                         Spacer()
                                         Spacer()
                                     }
                                     }
                                     .padding(.leading, 10)
                                     .padding(.leading, 10)
                                     .frame(height: 50)
                                     .frame(height: 50)
                                 }
                                 }
-                                Divider()
-                                    .padding(.leading, 54)
+                                if element != entry.datas.last {
+                                    Divider()
+                                        .padding(.leading, 54)
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
@@ -111,7 +114,6 @@ struct FilesWidgetView: View {
                 }
                 }
 
 
                 HStack(spacing: 0) {
                 HStack(spacing: 0) {
-
                     let sizeButton: CGFloat = 40
                     let sizeButton: CGFloat = 40
 
 
                     Link(destination: entry.isPlaceholder ? linkNoAction : linkActionUploadAsset, label: {
                     Link(destination: entry.isPlaceholder ? linkNoAction : linkActionUploadAsset, label: {
@@ -120,7 +122,7 @@ struct FilesWidgetView: View {
                             .renderingMode(.template)
                             .renderingMode(.template)
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding(11)
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -132,9 +134,10 @@ struct FilesWidgetView: View {
                             .renderingMode(.template)
                             .renderingMode(.template)
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding(11)
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
+                            .font(Font.system(.body).weight(.light))
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
                     })
                     })
 
 
@@ -144,7 +147,7 @@ struct FilesWidgetView: View {
                             .renderingMode(.template)
                             .renderingMode(.template)
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding(11)
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -156,27 +159,27 @@ struct FilesWidgetView: View {
                             .renderingMode(.template)
                             .renderingMode(.template)
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding(11)
                             .padding(11)
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
                     })
                     })
                 }
                 }
-                .frame(width: geo.size.width, height: geo.size.height - 25, alignment: .bottomTrailing)
+                .frame(width: geo.size.width, height: geo.size.height - 22, alignment: .bottomTrailing)
                 .redacted(reason: entry.isPlaceholder ? .placeholder : [])
                 .redacted(reason: entry.isPlaceholder ? .placeholder : [])
 
 
                 HStack {
                 HStack {
-
                     Image(systemName: entry.footerImage)
                     Image(systemName: entry.footerImage)
                         .resizable()
                         .resizable()
                         .scaledToFit()
                         .scaledToFit()
                         .frame(width: 15, height: 15)
                         .frame(width: 15, height: 15)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                        .font(Font.system(.body).weight(.light))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
 
 
                     Text(entry.footerText)
                     Text(entry.footerText)
                         .font(.caption2)
                         .font(.caption2)
                         .lineLimit(1)
                         .lineLimit(1)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                 }
                 }
                 .padding(.horizontal, 15.0)
                 .padding(.horizontal, 15.0)
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
                 .frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)

+ 4 - 4
Widget/Lockscreen/LockscreenWidgetView.swift

@@ -39,7 +39,7 @@ struct LockscreenWidgetView: View {
                     label: {},
                     label: {},
                     currentValueLabel: {
                     currentValueLabel: {
                         Image(systemName: "xmark.icloud")
                         Image(systemName: "xmark.icloud")
-                            .font(.system(size: 25.0))
+                            .font(.system(size: 25.0).weight(.light))
                     }
                     }
                 )
                 )
                 .gaugeStyle(.accessoryCircularCapacity)
                 .gaugeStyle(.accessoryCircularCapacity)
@@ -66,17 +66,17 @@ struct LockscreenWidgetView: View {
                         .renderingMode(.template)
                         .renderingMode(.template)
                         .resizable()
                         .resizable()
                         .scaledToFill()
                         .scaledToFill()
-                        .foregroundColor(.gray)
+                        .foregroundColor(Color(NCBrandColor.shared.textColor2))
                         .frame(width: 11, height: 11)
                         .frame(width: 11, height: 11)
                     Text(NSLocalizedString("_recent_activity_", comment: ""))
                     Text(NSLocalizedString("_recent_activity_", comment: ""))
                         .font(.system(size: 11))
                         .font(.system(size: 11))
                         .fontWeight(.heavy)
                         .fontWeight(.heavy)
-                        .foregroundColor(.gray)
+                        .foregroundColor(Color(NCBrandColor.shared.textColor2))
                 }
                 }
                 if entry.error {
                 if entry.error {
                     VStack(spacing: 1) {
                     VStack(spacing: 1) {
                         Image(systemName: "xmark.icloud")
                         Image(systemName: "xmark.icloud")
-                            .font(.system(size: 25.0))
+                            .font(Font.system(size: 25.0).weight(.light))
                             .frame(maxWidth: .infinity, alignment: .center)
                             .frame(maxWidth: .infinity, alignment: .center)
                     }.padding(8)
                     }.padding(8)
                 } else {
                 } else {

+ 8 - 7
Widget/Toolbar/ToolbarWidgetView.swift

@@ -51,7 +51,7 @@ struct ToolbarWidgetView: View {
                             .renderingMode(.template)
                             .renderingMode(.template)
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding()
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -61,9 +61,10 @@ struct ToolbarWidgetView: View {
                         Image(systemName: "doc.text.viewfinder")
                         Image(systemName: "doc.text.viewfinder")
                             .resizable()
                             .resizable()
                             .renderingMode(.template)
                             .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.brandText))
                             .padding()
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -75,7 +76,7 @@ struct ToolbarWidgetView: View {
                             .renderingMode(.template)
                             .renderingMode(.template)
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding()
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -86,7 +87,7 @@ struct ToolbarWidgetView: View {
                             .resizable()
                             .resizable()
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
                             .padding()
                             .padding()
-                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                            .background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                             .clipShape(Circle())
                             .clipShape(Circle())
                             .scaledToFit()
                             .scaledToFit()
                             .frame(width: geo.size.width / 4, height: sizeButton)
                             .frame(width: geo.size.width / 4, height: sizeButton)
@@ -96,17 +97,17 @@ struct ToolbarWidgetView: View {
                 .redacted(reason: entry.isPlaceholder ? .placeholder : [])
                 .redacted(reason: entry.isPlaceholder ? .placeholder : [])
 
 
                 HStack {
                 HStack {
-
                     Image(systemName: entry.footerImage)
                     Image(systemName: entry.footerImage)
                         .resizable()
                         .resizable()
+                        .font(Font.system(.body).weight(.light))
                         .scaledToFit()
                         .scaledToFit()
                         .frame(width: 15, height: 15)
                         .frame(width: 15, height: 15)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
 
 
                     Text(entry.footerText)
                     Text(entry.footerText)
                         .font(.caption2)
                         .font(.caption2)
                         .padding(.trailing, 13.0)
                         .padding(.trailing, 13.0)
-                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
+                        .foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
                 }
                 }
                 .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
                 .frame(maxWidth: geo.size.width - 5, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
             }
             }

+ 13 - 0
Widget/Widget.swift

@@ -47,7 +47,9 @@ struct DashboardWidget: Widget {
         .supportedFamilies([.systemLarge])
         .supportedFamilies([.systemLarge])
         .configurationDisplayName("Dashboard")
         .configurationDisplayName("Dashboard")
         .description(NSLocalizedString("_description_dashboardwidget_", comment: ""))
         .description(NSLocalizedString("_description_dashboardwidget_", comment: ""))
+#if !targetEnvironment(simulator)
         .contentMarginsDisabled()
         .contentMarginsDisabled()
+#endif
     }
     }
 }
 }
 
 
@@ -61,7 +63,9 @@ struct FilesWidget: Widget {
         .supportedFamilies([.systemLarge])
         .supportedFamilies([.systemLarge])
         .configurationDisplayName("Files")
         .configurationDisplayName("Files")
         .description(NSLocalizedString("_description_fileswidget_", comment: ""))
         .description(NSLocalizedString("_description_fileswidget_", comment: ""))
+#if !targetEnvironment(simulator)
         .contentMarginsDisabled()
         .contentMarginsDisabled()
+#endif
     }
     }
 }
 }
 
 
@@ -75,7 +79,9 @@ struct ToolbarWidget: Widget {
         .supportedFamilies([.systemMedium])
         .supportedFamilies([.systemMedium])
         .configurationDisplayName("Toolbar")
         .configurationDisplayName("Toolbar")
         .description(NSLocalizedString("_description_toolbarwidget_", comment: ""))
         .description(NSLocalizedString("_description_toolbarwidget_", comment: ""))
+#if !targetEnvironment(simulator)
         .contentMarginsDisabled()
         .contentMarginsDisabled()
+#endif
     }
     }
 }
 }
 
 
@@ -90,7 +96,9 @@ struct LockscreenWidget: Widget {
             .supportedFamilies([.accessoryRectangular, .accessoryCircular])
             .supportedFamilies([.accessoryRectangular, .accessoryCircular])
             .configurationDisplayName(NSLocalizedString("_title_lockscreenwidget_", comment: ""))
             .configurationDisplayName(NSLocalizedString("_title_lockscreenwidget_", comment: ""))
             .description(NSLocalizedString("_description_lockscreenwidget_", comment: ""))
             .description(NSLocalizedString("_description_lockscreenwidget_", comment: ""))
+#if !targetEnvironment(simulator)
             .contentMarginsDisabled()
             .contentMarginsDisabled()
+#endif
         } else {
         } else {
             return EmptyWidgetConfiguration()
             return EmptyWidgetConfiguration()
         }
         }
@@ -99,6 +107,7 @@ struct LockscreenWidget: Widget {
 
 
 extension View {
 extension View {
     func widgetBackground(_ backgroundView: some View) -> some View {
     func widgetBackground(_ backgroundView: some View) -> some View {
+#if !targetEnvironment(simulator)
         if #available(iOSApplicationExtension 17.0, *) {
         if #available(iOSApplicationExtension 17.0, *) {
             return containerBackground(for: .widget) {
             return containerBackground(for: .widget) {
                 backgroundView
                 backgroundView
@@ -106,5 +115,9 @@ extension View {
         } else {
         } else {
             return background(backgroundView)
             return background(backgroundView)
         }
         }
+#else
+        return background(backgroundView)
+#endif
+
     }
     }
 }
 }

+ 11 - 43
create-docker-test-server.sh

@@ -1,49 +1,17 @@
 #!/usr/bin/env bash
 #!/usr/bin/env bash
 #This script creates a testable Docker enviroment of the Nextcloud server, and is used by the CI for tests.
 #This script creates a testable Docker enviroment of the Nextcloud server, and is used by the CI for tests.
 
 
-container_name="nextcloud_test"
-port=8080
-server_url="http://localhost:${port}"
-user="admin"
+CONTAINER_NAME=nextcloud_test
+SERVER_PORT=8080
+TEST_BRANCH=stable28
+SERVER_URL="http://localhost:${SERVER_PORT}"
+USER="admin"
 
 
-docker run --rm -d --name $container_name -p $port:80 ghcr.io/juliushaertl/nextcloud-dev-php80:latest
+docker run --rm -d \
+    --name $CONTAINER_NAME \
+    -e SERVER_BRANCH=$TEST_BRANCH \
+    -p $SERVER_PORT:80 \
+    ghcr.io/juliushaertl/nextcloud-dev-php80:latest
 
 
-timeout=300
-elapsed=0
 
 
-echo "Waiting for server..."
-
-sleep 2
-
-while true; do
-    content=$(curl -s $server_url/status.php)
-
-    if [[ $content == *"installed\":true"* ]]; then
-        break
-    fi
-
-    elapsed=$((elapsed + 1))
-
-    if [ $elapsed -ge $timeout ]; then
-        echo "No success after $timeout seconds."
-        exit 1
-    fi
-
-    sleep 1
-done
-
-echo "Server is installed."
-echo "Exporting env vars..."
-
-sleep 2
-
-password=$(docker exec -e NC_PASS=$user $container_name sudo -E -u www-data php /var/www/html/occ user:add-app-password $user --password-from-env | tail -1)
-
-export TEST_APP_PASSWORD=$password
-export TEST_SERVER_URL=$server_url
-export TEST_USER=$user
-
-echo "TEST_SERVER_URL: ${TEST_SERVER_URL}"
-echo "TEST_USER: ${TEST_USER}"
-echo "TEST_APP_PASSWORD: ${TEST_APP_PASSWORD}"
-echo "Env vars exported."
+source ./wait-for-server.sh

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

@@ -29,14 +29,7 @@ public protocol NCAccountRequestDelegate: AnyObject {
     func accountRequestChangeAccount(account: String)
     func accountRequestChangeAccount(account: String)
 }
 }
 
 
-// optional func
-public extension NCAccountRequestDelegate {
-    func accountRequestAddAccount() {}
-    func accountRequestChangeAccount(account: String) {}
-}
-
 class NCAccountRequest: UIViewController {
 class NCAccountRequest: UIViewController {
-
     @IBOutlet weak var titleLabel: UILabel!
     @IBOutlet weak var titleLabel: UILabel!
     @IBOutlet weak var tableView: UITableView!
     @IBOutlet weak var tableView: UITableView!
     @IBOutlet weak var progressView: UIProgressView!
     @IBOutlet weak var progressView: UIProgressView!
@@ -73,8 +66,8 @@ class NCAccountRequest: UIViewController {
             progressView.isHidden = true
             progressView.isHidden = true
         }
         }
 
 
-        NotificationCenter.default.addObserver(self, selector: #selector(startTimer), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(startTimer), name: UIApplication.didBecomeActiveNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
     }
     }
 
 
     override func viewDidAppear(_ animated: Bool) {
     override func viewDidAppear(_ animated: Bool) {
@@ -97,7 +90,6 @@ class NCAccountRequest: UIViewController {
     // MARK: - NotificationCenter
     // MARK: - NotificationCenter
 
 
     @objc func applicationDidEnterBackground() {
     @objc func applicationDidEnterBackground() {
-
         if dismissDidEnterBackground {
         if dismissDidEnterBackground {
             dismiss(animated: false)
             dismiss(animated: false)
         }
         }
@@ -106,19 +98,17 @@ class NCAccountRequest: UIViewController {
     // MARK: - Progress
     // MARK: - Progress
 
 
     @objc func startTimer() {
     @objc func startTimer() {
-
         if enableTimerProgress {
         if enableTimerProgress {
             time = 0
             time = 0
             timer?.invalidate()
             timer?.invalidate()
             timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
             timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
-            progressView.isHidden = false
+            progressView?.isHidden = false
         } else {
         } else {
-            progressView.isHidden = true
+            progressView?.isHidden = true
         }
         }
     }
     }
 
 
     @objc func updateProgress() {
     @objc func updateProgress() {
-
         time += 0.1
         time += 0.1
         if time >= secondsAutoDismiss {
         if time >= secondsAutoDismiss {
             dismiss(animated: true)
             dismiss(animated: true)
@@ -129,9 +119,7 @@ class NCAccountRequest: UIViewController {
 }
 }
 
 
 extension NCAccountRequest: UITableViewDelegate {
 extension NCAccountRequest: UITableViewDelegate {
-
     func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
     func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
-
         timer?.invalidate()
         timer?.invalidate()
         progressView.progress = 0
         progressView.progress = 0
     }
     }
@@ -141,14 +129,10 @@ extension NCAccountRequest: UITableViewDelegate {
     }
     }
 
 
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-
         if indexPath.row == accounts.count {
         if indexPath.row == accounts.count {
-
             dismiss(animated: true)
             dismiss(animated: true)
             delegate?.accountRequestAddAccount()
             delegate?.accountRequestAddAccount()
-
         } else {
         } else {
-
             let account = accounts[indexPath.row]
             let account = accounts[indexPath.row]
             if account.account != activeAccount?.account {
             if account.account != activeAccount?.account {
                 dismiss(animated: true) {
                 dismiss(animated: true) {
@@ -162,7 +146,6 @@ extension NCAccountRequest: UITableViewDelegate {
 }
 }
 
 
 extension NCAccountRequest: UITableViewDataSource {
 extension NCAccountRequest: UITableViewDataSource {
-
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         if enableAddAccount {
         if enableAddAccount {
             return accounts.count + 1
             return accounts.count + 1
@@ -172,10 +155,8 @@ extension NCAccountRequest: UITableViewDataSource {
     }
     }
 
 
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-
         let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
         let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
         cell.backgroundColor = tableView.backgroundColor
         cell.backgroundColor = tableView.backgroundColor
-
         let avatarImage = cell.viewWithTag(10) as? UIImageView
         let avatarImage = cell.viewWithTag(10) as? UIImageView
         let userLabel = cell.viewWithTag(20) as? UILabel
         let userLabel = cell.viewWithTag(20) as? UILabel
         let urlLabel = cell.viewWithTag(30) as? UILabel
         let urlLabel = cell.viewWithTag(30) as? UILabel
@@ -186,7 +167,7 @@ extension NCAccountRequest: UITableViewDataSource {
 
 
         if indexPath.row == accounts.count {
         if indexPath.row == accounts.count {
 
 
-            avatarImage?.image = utility.loadImage(named: "plus").image(color: .systemBlue, size: 15)
+            avatarImage?.image = utility.loadImage(named: "plus", colors: [.systemBlue])
             avatarImage?.contentMode = .center
             avatarImage?.contentMode = .center
             userLabel?.text = NSLocalizedString("_add_account_", comment: "")
             userLabel?.text = NSLocalizedString("_add_account_", comment: "")
             userLabel?.textColor = .systemBlue
             userLabel?.textColor = .systemBlue
@@ -209,7 +190,7 @@ extension NCAccountRequest: UITableViewDataSource {
             }
             }
 
 
             if account.active {
             if account.active {
-                activeImage?.image = utility.loadImage(named: "checkmark").image(color: .systemBlue, size: 30)
+                activeImage?.image = utility.loadImage(named: "checkmark", colors: [.systemBlue])
             } else {
             } else {
                 activeImage?.image = nil
                 activeImage?.image = nil
             }
             }

+ 23 - 14
iOSClient/Activity/NCActivity.swift

@@ -44,6 +44,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
     let utility = NCUtility()
     let utility = NCUtility()
     var allItems: [DateCompareable] = []
     var allItems: [DateCompareable] = []
     var sectionDates: [Date] = []
     var sectionDates: [Date] = []
+    var dataSourceTask: URLSessionTask?
 
 
     var insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
     var insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
     var didSelectItemEnable: Bool = true
     var didSelectItemEnable: Bool = true
@@ -102,9 +103,12 @@ class NCActivity: UIViewController, NCSharePagingContent {
         fetchAll(isInitial: true)
         fetchAll(isInitial: true)
     }
     }
 
 
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        appDelegate.activeViewController = self
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+
+        // Cancel Queue & Retrieves Properties
+        NCNetworking.shared.downloadThumbnailActivityQueue.cancelAll()
+        dataSourceTask?.cancel()
     }
     }
 
 
     override func viewWillLayoutSubviews() {
     override func viewWillLayoutSubviews() {
@@ -123,7 +127,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
 
 
         let label = UILabel()
         let label = UILabel()
         label.font = UIFont.systemFont(ofSize: 15)
         label.font = UIFont.systemFont(ofSize: 15)
-        label.textColor = UIColor.systemGray
+        label.textColor = NCBrandColor.shared.textColor2
         label.textAlignment = .center
         label.textAlignment = .center
         label.text = NSLocalizedString("_no_activity_footer_", comment: "")
         label.text = NSLocalizedString("_no_activity_footer_", comment: "")
         label.frame = CGRect(x: 0, y: 10, width: tableView.frame.width, height: 60)
         label.frame = CGRect(x: 0, y: 10, width: tableView.frame.width, height: 60)
@@ -156,7 +160,7 @@ extension NCActivity: UITableViewDelegate {
 
 
         let label = UILabel()
         let label = UILabel()
         label.font = UIFont.boldSystemFont(ofSize: 13)
         label.font = UIFont.boldSystemFont(ofSize: 13)
-        label.textColor = .label
+        label.textColor = NCBrandColor.shared.textColor
         label.text = utility.getTitleFromDate(sectionDates[section])
         label.text = utility.getTitleFromDate(sectionDates[section])
         label.textAlignment = .center
         label.textAlignment = .center
         label.layer.cornerRadius = 11
         label.layer.cornerRadius = 11
@@ -209,16 +213,16 @@ extension NCActivity: UITableViewDataSource {
 
 
         // Image
         // Image
         let fileName = appDelegate.userBaseUrl + "-" + comment.actorId + ".png"
         let fileName = appDelegate.userBaseUrl + "-" + comment.actorId + ".png"
-        NCNetworking.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
+        NCNetworking.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView)
         // Username
         // Username
         cell.labelUser.text = comment.actorDisplayName
         cell.labelUser.text = comment.actorDisplayName
-        cell.labelUser.textColor = .label
+        cell.labelUser.textColor = NCBrandColor.shared.textColor
         // Date
         // Date
         cell.labelDate.text = utility.dateDiff(comment.creationDateTime as Date)
         cell.labelDate.text = utility.dateDiff(comment.creationDateTime as Date)
         cell.labelDate.textColor = .systemGray4
         cell.labelDate.textColor = .systemGray4
         // Message
         // Message
         cell.labelMessage.text = comment.message
         cell.labelMessage.text = comment.message
-        cell.labelMessage.textColor = .label
+        cell.labelMessage.textColor = NCBrandColor.shared.textColor
         // Button Menu
         // Button Menu
         if comment.actorId == appDelegate.userId {
         if comment.actorId == appDelegate.userId {
             cell.buttonMenu.isHidden = false
             cell.buttonMenu.isHidden = false
@@ -242,7 +246,7 @@ extension NCActivity: UITableViewDataSource {
         cell.avatar.isHidden = true
         cell.avatar.isHidden = true
         cell.subjectTrailingConstraint.constant = 10
         cell.subjectTrailingConstraint.constant = 10
         cell.didSelectItemEnable = self.didSelectItemEnable
         cell.didSelectItemEnable = self.didSelectItemEnable
-        cell.subject.textColor = .label
+        cell.subject.textColor = NCBrandColor.shared.textColor
         cell.viewController = self
         cell.viewController = self
 
 
         // icon
         // icon
@@ -276,7 +280,7 @@ extension NCActivity: UITableViewDataSource {
 
 
             let fileName = appDelegate.userBaseUrl + "-" + activity.user + ".png"
             let fileName = appDelegate.userBaseUrl + "-" + activity.user + ".png"
 
 
-            NCNetworking.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
+            NCNetworking.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView)
         }
         }
 
 
         // subject
         // subject
@@ -426,7 +430,9 @@ extension NCActivity {
             limit: 1,
             limit: 1,
             objectId: nil,
             objectId: nil,
             objectType: objectType,
             objectType: objectType,
-            previews: true) { account, _, activityFirstKnown, activityLastGiven, _, error in
+            previews: true) { task in
+                self.dataSourceTask = task
+            } completion: { account, _, activityFirstKnown, activityLastGiven, _, error in
                 defer { disptachGroup.leave() }
                 defer { disptachGroup.leave() }
 
 
                 let largestActivityId = max(activityFirstKnown, activityLastGiven)
                 let largestActivityId = max(activityFirstKnown, activityLastGiven)
@@ -453,7 +459,9 @@ extension NCActivity {
             limit: min(limit, 200),
             limit: min(limit, 200),
             objectId: metadata?.fileId,
             objectId: metadata?.fileId,
             objectType: objectType,
             objectType: objectType,
-            previews: true) { account, activities, activityFirstKnown, activityLastGiven, _, error in
+            previews: true) { task in
+                self.dataSourceTask = task
+            } completion: { account, activities, activityFirstKnown, activityLastGiven, _, error in
                 defer { disptachGroup.leave() }
                 defer { disptachGroup.leave() }
                 guard error == .success,
                 guard error == .success,
                       account == self.appDelegate.account,
                       account == self.appDelegate.account,
@@ -494,7 +502,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
         actions.append(
         actions.append(
             NCMenuAction(
             NCMenuAction(
                 title: NSLocalizedString("_edit_comment_", comment: ""),
                 title: NSLocalizedString("_edit_comment_", comment: ""),
-                icon: UIImage(named: "pencil")!.image(color: UIColor.systemGray, size: 50),
+                icon: utility.loadImage(named: "pencil", colors: [NCBrandColor.shared.iconImageColor]),
                 action: { _ in
                 action: { _ in
                     guard let metadata = self.metadata, let tableComments = tableComments else { return }
                     guard let metadata = self.metadata, let tableComments = tableComments else { return }
 
 
@@ -525,7 +533,8 @@ extension NCActivity: NCShareCommentsCellDelegate {
         actions.append(
         actions.append(
             NCMenuAction(
             NCMenuAction(
                 title: NSLocalizedString("_delete_comment_", comment: ""),
                 title: NSLocalizedString("_delete_comment_", comment: ""),
-                icon: utility.loadImage(named: "trash"),
+                destructive: true,
+                icon: utility.loadImage(named: "trash", colors: [.red]),
                 action: { _ in
                 action: { _ in
                     guard let metadata = self.metadata, let tableComments = tableComments else { return }
                     guard let metadata = self.metadata, let tableComments = tableComments else { return }
 
 

+ 2 - 2
iOSClient/Activity/NCActivityCommentView.swift

@@ -40,7 +40,7 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
         if let image = UIImage(contentsOfFile: fileNameLocalPath) {
         if let image = UIImage(contentsOfFile: fileNameLocalPath) {
             imageItem.image = image
             imageItem.image = image
         } else {
         } else {
-            imageItem.image = UIImage(named: "avatar")
+            imageItem.image = NCUtility().loadImage(named: "person.crop.circle", colors: [NCBrandColor.shared.iconImageColor])
         }
         }
 
 
         if account.displayName.isEmpty {
         if account.displayName.isEmpty {
@@ -48,7 +48,7 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
         } else {
         } else {
             labelUser.text = account.displayName
             labelUser.text = account.displayName
         }
         }
-        labelUser.textColor = .label
+        labelUser.textColor = NCBrandColor.shared.textColor
     }
     }
 
 
     func textFieldShouldReturn(_ textField: UITextField) -> Bool {
     func textFieldShouldReturn(_ textField: UITextField) -> Bool {

+ 6 - 4
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -166,7 +166,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                 if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
                 if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
                     cell.imageView.image = image
                     cell.imageView.image = image
                 } else {
                 } else {
-                    cell.imageView.image = UIImage(named: "file")
+                    cell.imageView.image = NCImageCache.images.file
                 }
                 }
             }
             }
 
 
@@ -176,11 +176,11 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
 
 
                 let source = activityPreview.source
                 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: 150, rewrite: false, account: appDelegate.account, id: idActivity) { imageNamePath, id in
                     if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
                     if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
                         cell.imageView.image = image
                         cell.imageView.image = image
                     } else {
                     } else {
-                        cell.imageView.image = UIImage(named: "file")
+                        cell.imageView.image = NCImageCache.images.file
                     }
                     }
                 }
                 }
 
 
@@ -192,8 +192,10 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
 
 
                     if FileManager.default.fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
                     if FileManager.default.fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
                         cell.imageView.image = image
                         cell.imageView.image = image
+                        cell.imageView?.contentMode = .scaleAspectFill
                     } else {
                     } else {
-                        cell.imageView?.image = UIImage(named: "file_photo")
+                        cell.imageView?.image = utility.loadImage(named: "doc", colors: [NCBrandColor.shared.iconImageColor])
+                        cell.imageView?.contentMode = .scaleAspectFit
                         cell.fileId = fileId
                         cell.fileId = fileId
                         if !FileManager.default.fileExists(atPath: fileNamePath) {
                         if !FileManager.default.fileExists(atPath: fileNamePath) {
                             if NCNetworking.shared.downloadThumbnailActivityQueue.operations.filter({ ($0 as? NCOperationDownloadThumbnailActivity)?.fileId == fileId }).isEmpty {
                             if NCNetworking.shared.downloadThumbnailActivityQueue.operations.filter({ ($0 as? NCOperationDownloadThumbnailActivity)?.fileId == fileId }).isEmpty {

+ 94 - 430
iOSClient/AppDelegate.swift

@@ -24,41 +24,32 @@
 import UIKit
 import UIKit
 import BackgroundTasks
 import BackgroundTasks
 import NextcloudKit
 import NextcloudKit
-import TOPasscodeViewController
 import LocalAuthentication
 import LocalAuthentication
 import Firebase
 import Firebase
 import WidgetKit
 import WidgetKit
 import Queuer
 import Queuer
+import EasyTipView
 
 
 @UIApplicationMain
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, NCUserBaseUrl {
 class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, NCUserBaseUrl {
 
 
-    var backgroundSessionCompletionHandler: (() -> Void)?
-    var window: UIWindow?
-
     @objc var account: String = ""
     @objc var account: String = ""
     @objc var urlBase: String = ""
     @objc var urlBase: String = ""
     @objc var user: String = ""
     @objc var user: String = ""
     @objc var userId: String = ""
     @objc var userId: String = ""
     @objc var password: String = ""
     @objc var password: String = ""
 
 
+    var tipView: EasyTipView?
+    var backgroundSessionCompletionHandler: (() -> Void)?
     var activeLogin: NCLogin?
     var activeLogin: NCLogin?
     var activeLoginWeb: NCLoginWeb?
     var activeLoginWeb: NCLoginWeb?
-    var activeServerUrl: String = ""
-    @objc var activeViewController: UIViewController?
-    var mainTabBar: NCMainTabBar?
-    var activeMetadata: tableMetadata?
-    let listFilesVC = ThreadSafeDictionary<String, NCFiles>()
-
-    var disableSharesView: Bool = false
-    var documentPickerViewController: NCDocumentPickerViewController?
     var timerErrorNetworking: Timer?
     var timerErrorNetworking: Timer?
-    var isAppRefresh: Bool = false
-    var isProcessingTask: Bool = false
-
+    var timerErrorNetworkingDisabled: Bool = false
+    var taskAutoUploadDate: Date = Date()
     var isUiTestingEnabled: Bool {
     var isUiTestingEnabled: Bool {
         return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
         return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
     }
     }
+    var notificationSettings: UNNotificationSettings?
 
 
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         if isUiTestingEnabled {
         if isUiTestingEnabled {
@@ -87,8 +78,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NextcloudKit.shared.setup(delegate: NCNetworking.shared)
         NextcloudKit.shared.setup(delegate: NCNetworking.shared)
         NextcloudKit.shared.setup(userAgent: userAgent)
         NextcloudKit.shared.setup(userAgent: userAgent)
 
 
-        startTimerErrorNetworking()
-
         var levelLog = 0
         var levelLog = 0
         NextcloudKit.shared.nkCommonInstance.pathLog = utilityFileSystem.directoryGroup
         NextcloudKit.shared.nkCommonInstance.pathLog = utilityFileSystem.directoryGroup
 
 
@@ -105,14 +94,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(levelLog) " + versionNextcloudiOS)
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(levelLog) " + versionNextcloudiOS)
         }
         }
 
 
-        if let account = NCManageDatabase.shared.getActiveAccount() {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Account active \(account.account)")
-            if NCKeychain().getPassword(account: account.account).isEmpty {
-                NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] PASSWORD NOT FOUND for \(account.account)")
-            }
-        }
-
         if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
         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
             account = activeAccount.account
             urlBase = activeAccount.urlBase
             urlBase = activeAccount.urlBase
@@ -140,6 +126,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NCImageCache.shared.createImagesCache()
         NCImageCache.shared.createImagesCache()
 
 
         // Push Notification & display notification
         // Push Notification & display notification
+        UNUserNotificationCenter.current().getNotificationSettings { settings in
+            self.notificationSettings = settings
+        }
         application.registerForRemoteNotifications()
         application.registerForRemoteNotifications()
         UNUserNotificationCenter.current().delegate = self
         UNUserNotificationCenter.current().delegate = self
         UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
         UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
@@ -158,127 +147,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             self.handleProcessingTask(task)
             self.handleProcessingTask(task)
         }
         }
 
 
-        if account.isEmpty {
-            if NCBrandOptions.shared.disable_intro {
-                openLogin(viewController: nil, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
-            } else {
-                if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() {
-                    let navigationController = NCLoginNavigationController(rootViewController: viewController)
-                    window?.rootViewController = navigationController
-                    window?.makeKeyAndVisible()
-                }
-            }
-        } else {
-            NCPasscode.shared.presentPasscode(delegate: self) {
-                NCPasscode.shared.enableTouchFaceID()
-            }
-        }
-
         return true
         return true
     }
     }
 
 
-    // MARK: - Life Cycle
-
-    // L' applicazione entrerà in attivo (sempre)
-    func applicationDidBecomeActive(_ application: UIApplication) {
-
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application did become active")
-
-        NCSettingsBundleHelper.setVersionAndBuildNumber()
-        NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0.5)
-
-        // START TIMER UPLOAD PROCESS
-        NCNetworkingProcess.shared.startTimer()
-
-        if !NCAskAuthorization().isRequesting {
-            NCPasscode.shared.hidePrivacyProtectionWindow()
-        }
-
-        NCService().startRequestServicesServer()
-
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
-        }
-
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidBecomeActive)
-    }
-
-    // L' applicazione si dimetterà dallo stato di attivo
-    func applicationWillResignActive(_ application: UIApplication) {
-
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application will resign active")
-
-        guard !account.isEmpty else { return }
-
-        // STOP TIMER UPLOAD PROCESS
-        NCNetworkingProcess.shared.stopTimer()
-
-        if NCKeychain().privacyScreenEnabled {
-            NCPasscode.shared.showPrivacyProtectionWindow()
-        }
-
-        // Reload Widget
-        WidgetCenter.shared.reloadAllTimelines()
-
-        // Clear older files
-        let days = NCKeychain().cleanUpDay
-        let utilityFileSystem = NCUtilityFileSystem()
-        utilityFileSystem.cleanUp(directory: utilityFileSystem.directoryProviderStorage, days: TimeInterval(days))
-
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillResignActive)
-    }
-
-    // L' applicazione entrerà in primo piano (dopo il background)
-    func applicationWillEnterForeground(_ application: UIApplication) {
-
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application will enter in foreground")
-
-        guard !account.isEmpty else { return }
-
-        NCPasscode.shared.enableTouchFaceID()
-
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillEnterForeground)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetwork, second: 2)
-    }
-
-    // L' applicazione è entrata nello sfondo
-    func applicationDidEnterBackground(_ application: UIApplication) {
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Application did enter in background")
-        guard !account.isEmpty else { return }
-
-        let activeAccount = NCManageDatabase.shared.getActiveAccount()
-
-        if let autoUpload = activeAccount?.autoUpload, autoUpload {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload: true")
-            if UIApplication.shared.backgroundRefreshStatus == .available {
-                NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload in background: true")
-            } else {
-                NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload in background: false")
-            }
-        } else {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Auto upload: false")
-        }
-
-        if let error = updateShareAccounts() {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Create share accounts \(error.localizedDescription)")
-        }
-
-        scheduleAppRefresh()
-        scheduleAppProcessing()
-        NCNetworking.shared.cancelAllQueue()
-        NCNetworking.shared.cancelDownloadTasks()
-        NCNetworking.shared.cancelUploadTasks()
-        NCPasscode.shared.presentPasscode(delegate: self) { }
-
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidEnterBackground)
-    }
-
-    // L'applicazione terminerà
     func applicationWillTerminate(_ application: UIApplication) {
     func applicationWillTerminate(_ application: UIApplication) {
-
-        if UIApplication.shared.backgroundRefreshStatus == .available {
-
+        if self.notificationSettings?.authorizationStatus != .denied && UIApplication.shared.backgroundRefreshStatus == .available {
             let content = UNMutableNotificationContent()
             let content = UNMutableNotificationContent()
             content.title = NCBrandOptions.shared.brand
             content.title = NCBrandOptions.shared.brand
             content.body = NSLocalizedString("_keep_running_", comment: "")
             content.body = NSLocalizedString("_keep_running_", comment: "")
@@ -290,11 +163,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] bye bye")
         NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] bye bye")
     }
     }
 
 
+    // MARK: - UISceneSession Lifecycle
+
+    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+        // Called when a new scene session is being created.
+        // Use this method to select a configuration to create the new scene with.
+        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
+    }
+
+    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
+        // Called when the user discards a scene session.
+        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
+        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
+    }
+
     // MARK: - Background Task
     // MARK: - Background Task
 
 
     /*
     /*
     @discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage.
     @discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage.
-     < MAX 30 seconds >
      */
      */
     func scheduleAppRefresh() {
     func scheduleAppRefresh() {
 
 
@@ -302,7 +188,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds.
         request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds.
         do {
         do {
             try BGTaskScheduler.shared.submit(request)
             try BGTaskScheduler.shared.submit(request)
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Refresh task: ok")
         } catch {
         } catch {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)")
         }
         }
@@ -310,7 +195,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
 
     /*
     /*
      @discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week.
      @discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week.
-     < MAX over 1 minute >
      */
      */
     func scheduleAppProcessing() {
     func scheduleAppProcessing() {
 
 
@@ -320,7 +204,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         request.requiresExternalPower = false
         request.requiresExternalPower = false
         do {
         do {
             try BGTaskScheduler.shared.submit(request)
             try BGTaskScheduler.shared.submit(request)
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Processing task: ok")
         } catch {
         } catch {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)")
         }
         }
@@ -329,59 +212,57 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     func handleAppRefresh(_ task: BGTask) {
     func handleAppRefresh(_ task: BGTask) {
         scheduleAppRefresh()
         scheduleAppRefresh()
 
 
-        if isProcessingTask {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] ProcessingTask already in progress, abort.")
-            return task.setTaskCompleted(success: true)
-        }
-        isAppRefresh = true
-
         handleAppRefreshProcessingTask(taskText: "AppRefresh") {
         handleAppRefreshProcessingTask(taskText: "AppRefresh") {
             task.setTaskCompleted(success: true)
             task.setTaskCompleted(success: true)
-            self.isAppRefresh = false
         }
         }
     }
     }
 
 
     func handleProcessingTask(_ task: BGTask) {
     func handleProcessingTask(_ task: BGTask) {
         scheduleAppProcessing()
         scheduleAppProcessing()
 
 
-        if isAppRefresh {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] AppRefresh already in progress, abort.")
-            return task.setTaskCompleted(success: true)
-        }
-        isProcessingTask = true
-
         handleAppRefreshProcessingTask(taskText: "ProcessingTask") {
         handleAppRefreshProcessingTask(taskText: "ProcessingTask") {
             task.setTaskCompleted(success: true)
             task.setTaskCompleted(success: true)
-            self.isProcessingTask = false
         }
         }
     }
     }
 
 
     func handleAppRefreshProcessingTask(taskText: String, completion: @escaping () -> Void = {}) {
     func handleAppRefreshProcessingTask(taskText: String, completion: @escaping () -> Void = {}) {
         Task {
         Task {
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) start handle")
-            let items = await NCAutoUpload.shared.initAutoUpload()
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) auto upload with \(items) uploads")
-            let results = await NCNetworkingProcess.shared.start()
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) networking process with download: \(results.counterDownloading) upload: \(results.counterUploading)")
+            var itemsAutoUpload = 0
+
+            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")
+            } else {
+                NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) disabled auto upload")
+            }
+
+            let results = await NCNetworkingProcess.shared.start(scene: nil)
+            NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) networking process with download: \(results.counterDownloading) upload: \(results.counterUploading)")
 
 
             if taskText == "ProcessingTask",
             if taskText == "ProcessingTask",
-               items == 0, results.counterDownloading == 0, results.counterUploading == 0,
+               itemsAutoUpload == 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", self.account), sorted: "offlineDate", ascending: true) {
                 for directory: tableDirectory in directories {
                 for directory: tableDirectory in directories {
-                    // only 3 time for day
+                    // test only 3 time for day (every 8 h.)
                     if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() {
                     if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() {
-                        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)")
+                        NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)")
                         continue
                         continue
                     }
                     }
                     let results = await NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false)
                     let results = await NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false)
-                    NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(results.errorCode), item: \(results.items)")
+                    NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(results.errorCode), item: \(results.items)")
                 }
                 }
             }
             }
 
 
             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", self.account, NCNetworking.shared.sessionDownloadBackground, NCNetworking.shared.sessionUploadBackground, NCGlobal.shared.metadataStatusNormal))?.count ?? 0
             UIApplication.shared.applicationIconBadgeNumber = counter
             UIApplication.shared.applicationIconBadgeNumber = counter
 
 
-            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) completion handle")
+            NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) completion handle")
             completion()
             completion()
         }
         }
     }
     }
@@ -390,7 +271,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
 
     func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
     func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
 
 
-        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start handle Events For Background URLSession: \(identifier)")
+        NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] Start handle Events For Background URLSession: \(identifier)")
         WidgetCenter.shared.reloadAllTimelines()
         WidgetCenter.shared.reloadAllTimelines()
         backgroundSessionCompletionHandler = completionHandler
         backgroundSessionCompletionHandler = completionHandler
     }
     }
@@ -413,7 +294,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
     }
 
 
     func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
     func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
-        NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: self.window?.rootViewController) { error in
+        NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: UIApplication.shared.firstWindow?.rootViewController) { error in
             if error == .success {
             if error == .success {
                 NCPushNotification.shared().registerForRemoteNotifications(withDeviceToken: deviceToken)
                 NCPushNotification.shared().registerForRemoteNotifications(withDeviceToken: deviceToken)
             }
             }
@@ -446,35 +327,51 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                 DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                     let navigationController = UINavigationController(rootViewController: viewController)
                     let navigationController = UINavigationController(rootViewController: viewController)
                     navigationController.modalPresentationStyle = .fullScreen
                     navigationController.modalPresentationStyle = .fullScreen
-                    self.window?.rootViewController?.present(navigationController, animated: true)
+                    UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true)
                 }
                 }
             } else if !findAccount {
             } else if !findAccount {
                 let message = NSLocalizedString("_the_account_", comment: "") + " " + accountPush + " " + NSLocalizedString("_does_not_exist_", comment: "")
                 let message = NSLocalizedString("_the_account_", comment: "") + " " + accountPush + " " + NSLocalizedString("_does_not_exist_", comment: "")
                 let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
                 let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
                 alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
                 alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-                self.window?.rootViewController?.present(alertController, animated: true, completion: { })
+                UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true, completion: { })
             }
             }
         }
         }
     }
     }
 
 
-    // MARK: - Login & checkErrorNetworking
+    // MARK: - Login
 
 
-    @objc func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) {
+    @objc func openLogin(selector: Int, openLoginWeb: Bool, windowForRootViewController: UIWindow? = nil) {
+        func showLoginViewController(_ viewController: UIViewController?) {
+            guard let viewController else { return }
+            let navigationController = NCLoginNavigationController(rootViewController: viewController)
+
+            navigationController.modalPresentationStyle = .fullScreen
+            navigationController.navigationBar.barStyle = .black
+            navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
+            navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
+            navigationController.navigationBar.isTranslucent = false
+
+            if let window = windowForRootViewController {
+                window.rootViewController = navigationController
+                window.makeKeyAndVisible()
+            } else {
+                UIApplication.shared.allSceneSessionDestructionExceptFirst()
+                UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true)
+            }
+        }
 
 
         // [WEBPersonalized] [AppConfig]
         // [WEBPersonalized] [AppConfig]
         if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig {
         if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig {
-
             if activeLoginWeb?.view.window == nil {
             if activeLoginWeb?.view.window == nil {
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
                 activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
-                showLoginViewController(activeLoginWeb, contextViewController: viewController)
+                showLoginViewController(activeLoginWeb)
             }
             }
             return
             return
         }
         }
 
 
         // Nextcloud standard login
         // Nextcloud standard login
         if selector == NCGlobal.shared.introSignup {
         if selector == NCGlobal.shared.introSignup {
-
             if activeLoginWeb?.view.window == nil {
             if activeLoginWeb?.view.window == nil {
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 if selector == NCGlobal.shared.introSignup {
                 if selector == NCGlobal.shared.introSignup {
@@ -482,72 +379,45 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 } else {
                 } else {
                     activeLoginWeb?.urlBase = self.urlBase
                     activeLoginWeb?.urlBase = self.urlBase
                 }
                 }
-                showLoginViewController(activeLoginWeb, contextViewController: viewController)
+                showLoginViewController(activeLoginWeb)
             }
             }
 
 
         } else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url {
         } else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url {
-
             if activeLoginWeb?.view.window == nil {
             if activeLoginWeb?.view.window == nil {
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
                 activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
-                showLoginViewController(activeLoginWeb, contextViewController: viewController)
+                showLoginViewController(activeLoginWeb)
             }
             }
 
 
         } else if openLoginWeb {
         } else if openLoginWeb {
-
             // Used also for reinsert the account (change passwd)
             // Used also for reinsert the account (change passwd)
             if activeLoginWeb?.view.window == nil {
             if activeLoginWeb?.view.window == nil {
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                 activeLoginWeb?.urlBase = urlBase
                 activeLoginWeb?.urlBase = urlBase
                 activeLoginWeb?.user = user
                 activeLoginWeb?.user = user
-                showLoginViewController(activeLoginWeb, contextViewController: viewController)
+                showLoginViewController(activeLoginWeb)
             }
             }
 
 
         } else {
         } else {
-
             if activeLogin?.view.window == nil {
             if activeLogin?.view.window == nil {
                 activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
                 activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
-                showLoginViewController(activeLogin, contextViewController: viewController)
+                showLoginViewController(activeLogin)
             }
             }
         }
         }
     }
     }
 
 
-    func showLoginViewController(_ viewController: UIViewController?, contextViewController: UIViewController?) {
-
-        if contextViewController == nil {
-            if let viewController = viewController {
-                let navigationController = NCLoginNavigationController(rootViewController: viewController)
-                navigationController.navigationBar.barStyle = .black
-                navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
-                navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
-                navigationController.navigationBar.isTranslucent = false
-                window?.rootViewController = navigationController
-                window?.makeKeyAndVisible()
-            }
-        } else if contextViewController is UINavigationController {
-            if let contextViewController = contextViewController, let viewController = viewController {
-                (contextViewController as? UINavigationController)?.pushViewController(viewController, animated: true)
-            }
-        } else {
-            if let viewController = viewController, let contextViewController = contextViewController {
-                let navigationController = NCLoginNavigationController(rootViewController: viewController)
-                navigationController.modalPresentationStyle = .fullScreen
-                navigationController.navigationBar.barStyle = .black
-                navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
-                navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
-                navigationController.navigationBar.isTranslucent = false
-                contextViewController.present(navigationController, animated: true) { }
-            }
-        }
-    }
+    // MARK: - Error Networking
 
 
-    @objc func startTimerErrorNetworking() {
-        timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking), userInfo: nil, repeats: true)
+    @objc func startTimerErrorNetworking(scene: UIScene) {
+        timerErrorNetworkingDisabled = false
+        timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking(_:)), userInfo: nil, repeats: true)
     }
     }
 
 
-    @objc private func checkErrorNetworking() {
-        guard !account.isEmpty, NCKeychain().getPassword(account: account).isEmpty else { return }
-        openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true)
+    @objc private func checkErrorNetworking(_ notification: NSNotification) {
+        guard !self.timerErrorNetworkingDisabled,
+              !account.isEmpty,
+              NCKeychain().getPassword(account: account).isEmpty else { return }
+        openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: true)
     }
     }
 
 
     func trustCertificateError(host: String) {
     func trustCertificateError(host: String) {
@@ -578,11 +448,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                let viewController = navigationController.topViewController as? NCViewCertificateDetails {
                let viewController = navigationController.topViewController as? NCViewCertificateDetails {
                 viewController.delegate = self
                 viewController.delegate = self
                 viewController.host = host
                 viewController.host = host
-                self.window?.rootViewController?.present(navigationController, animated: true)
+                UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true)
             }
             }
         }))
         }))
 
 
-        window?.rootViewController?.present(alertController, animated: true)
+        UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true)
     }
     }
 
 
     // MARK: - Account
     // MARK: - Account
@@ -632,6 +502,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
 
     @objc func deleteAccount(_ account: String, wipe: Bool) {
     @objc func deleteAccount(_ account: String, wipe: Bool) {
 
 
+        UIApplication.shared.allSceneSessionDestructionExceptFirst()
+
         if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
         if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
             NCPushNotification.shared().unsubscribingNextcloudServerPushNotification(account.account, urlBase: account.urlBase, user: account.user, withSubscribing: false)
             NCPushNotification.shared().unsubscribingNextcloudServerPushNotification(account.account, urlBase: account.urlBase, user: account.user, withSubscribing: false)
         }
         }
@@ -664,7 +536,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                     self.changeAccount(newAccount, userProfile: nil)
                     self.changeAccount(newAccount, userProfile: nil)
                 }
                 }
             } else {
             } else {
-                openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
+                openLogin(selector: NCGlobal.shared.introLogin, openLoginWeb: false)
             }
             }
         }
         }
     }
     }
@@ -719,175 +591,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         let applicationHandle = NCApplicationHandle()
         let applicationHandle = NCApplicationHandle()
         return applicationHandle.applicationOpenUserActivity(userActivity)
         return applicationHandle.applicationOpenUserActivity(userActivity)
     }
     }
-
-    // MARK: - Scheme URL
-
-    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
-
-        let scheme = url.scheme
-        let action = url.host
-        var fileName: String = ""
-        var serverUrl: String = ""
-
-        /*
-         Example: nextcloud://open-action?action=create-voice-memo&&user=marinofaggiana&url=https://cloud.nextcloud.com
-         */
-
-        if !account.isEmpty && scheme == NCGlobal.shared.appScheme && action == "open-action" {
-
-            if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
-
-                let queryItems = urlComponents.queryItems
-                guard let actionScheme = queryItems?.filter({ $0.name == "action" }).first?.value,
-                      let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value,
-                      let urlScheme = queryItems?.filter({ $0.name == "url" }).first?.value,
-                      let rootViewController = window?.rootViewController else { return false }
-                if getMatchedAccount(userId: userScheme, url: urlScheme) == nil {
-                    let message = NSLocalizedString("_the_account_", comment: "") + " " + userScheme + NSLocalizedString("_of_", comment: "") + " " + urlScheme + " " + 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 }))
-
-                    window?.rootViewController?.present(alertController, animated: true, completion: { })
-                    return false
-                }
-
-                switch actionScheme {
-                case NCGlobal.shared.actionUploadAsset:
-
-                    NCAskAuthorization().askAuthorizationPhotoLibrary(viewController: rootViewController) { hasPermission in
-                        if hasPermission {
-                            NCPhotosPickerViewController(viewController: rootViewController, maxSelectedAssets: 0, singleSelectedMode: false)
-                        }
-                    }
-
-                case NCGlobal.shared.actionScanDocument:
-
-                    NCDocumentCamera.shared.openScannerDocument(viewController: rootViewController)
-
-                case NCGlobal.shared.actionTextDocument:
-
-                    guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController(),
-                          let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account),
-                          let directEditingCreator = directEditingCreators.first(where: { $0.editor == NCGlobal.shared.editorText}),
-                          let viewController = (navigationController as? UINavigationController)?.topViewController as? NCCreateFormUploadDocuments else { return false }
-
-                    navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
-
-                    viewController.editorId = NCGlobal.shared.editorText
-                    viewController.creatorId = directEditingCreator.identifier
-                    viewController.typeTemplate = NCGlobal.shared.templateDocument
-                    viewController.serverUrl = activeServerUrl
-                    viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")
-
-                    rootViewController.present(navigationController, animated: true, completion: nil)
-
-                case NCGlobal.shared.actionVoiceMemo:
-
-                    NCAskAuthorization().askAuthorizationAudioRecord(viewController: rootViewController) { hasPermission in
-                        if hasPermission {
-                            if let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController {
-                                viewController.modalTransitionStyle = .crossDissolve
-                                viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
-                                rootViewController.present(viewController, animated: true, completion: nil)
-                            }
-                        }
-                    }
-
-                default:
-                    print("No action")
-                }
-            }
-            return true
-        }
-
-        /*
-         Example: nextcloud://open-file?path=Talk/IMG_0000123.jpg&user=marinofaggiana&link=https://cloud.nextcloud.com/f/123
-         */
-
-        else if !account.isEmpty && scheme == NCGlobal.shared.appScheme && action == "open-file" {
-
-            if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
-
-                let queryItems = urlComponents.queryItems
-                guard let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value,
-                      let pathScheme = queryItems?.filter({ $0.name == "path" }).first?.value,
-                      let linkScheme = queryItems?.filter({ $0.name == "link" }).first?.value else { return false}
-
-                guard let matchedAccount = getMatchedAccount(userId: userScheme, url: linkScheme) else {
-                    guard let domain = URL(string: linkScheme)?.host else { return true }
-                    fileName = (pathScheme as NSString).lastPathComponent
-                    let message = String(format: NSLocalizedString("_account_not_available_", comment: ""), userScheme, domain, fileName)
-                    let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-
-                    window?.rootViewController?.present(alertController, animated: true, completion: { })
-                    return false
-                }
-
-                let davFiles = NextcloudKit.shared.nkCommonInstance.dav + "/files/" + self.userId
-                if pathScheme.contains("/") {
-                    fileName = (pathScheme as NSString).lastPathComponent
-                    serverUrl = matchedAccount.urlBase + "/" + davFiles + "/" + (pathScheme as NSString).deletingLastPathComponent
-                } else {
-                    fileName = pathScheme
-                    serverUrl = matchedAccount.urlBase + "/" + davFiles
-                }
-
-                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                    NCActionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: nil, fileNameOpen: fileName)
-                }
-            }
-            return true
-
-        /*
-         Example: nextcloud://open-and-switch-account?user=marinofaggiana&url=https://cloud.nextcloud.com
-         */
-
-        } else if !account.isEmpty && scheme == NCGlobal.shared.appScheme && action == "open-and-switch-account" {
-            guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
-            let queryItems = urlComponents.queryItems
-            guard let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value,
-                  let urlScheme = queryItems?.filter({ $0.name == "url" }).first?.value else { return false}
-            // If the account doesn't exist, return false which will open the app without switching
-            if getMatchedAccount(userId: userScheme, url: urlScheme) == nil {
-                return false
-            }
-            // Otherwise open the app and switch accounts
-            return true
-        } else {
-            let applicationHandle = NCApplicationHandle()
-            let isHandled = applicationHandle.applicationOpenURL(url)
-            if isHandled {
-                return true
-            } else {
-                app.open(url)
-                return true
-            }
-        }
-    }
-
-    func getMatchedAccount(userId: String, url: String) -> tableAccount? {
-
-        if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
-            let urlBase = URL(string: activeAccount.urlBase)
-            if url.contains(urlBase?.host ?? "") && userId == activeAccount.userId {
-               return activeAccount
-            } else {
-                let accounts = NCManageDatabase.shared.getAllAccount()
-                for account in accounts {
-                    let urlBase = URL(string: account.urlBase)
-                    if url.contains(urlBase?.host ?? "") && userId == account.userId {
-                        changeAccount(account.account, userProfile: nil)
-                        return account
-                    }
-                }
-            }
-        }
-        return nil
-    }
 }
 }
 
 
-// MARK: -
+// MARK: - Extension
 
 
 extension AppDelegate: NCViewCertificateDetailsDelegate {
 extension AppDelegate: NCViewCertificateDetailsDelegate {
     func viewCertificateDetailsDismiss(host: String) {
     func viewCertificateDetailsDismiss(host: String) {
@@ -901,45 +607,3 @@ extension AppDelegate: NCCreateFormUploadConflictDelegate {
         NCNetworkingProcess.shared.createProcessUploads(metadatas: metadatas)
         NCNetworkingProcess.shared.createProcessUploads(metadatas: metadatas)
     }
     }
 }
 }
-
-extension AppDelegate: NCPasscodeDelegate {
-    func requestedAccount() {
-        guard !NCPasscode.shared.isPasscodePresented, NCKeychain().accountRequest else {
-            return
-        }
-
-        let accounts = NCManageDatabase.shared.getAllAccount()
-        if accounts.count > 1 {
-
-            if let viewController = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
-
-                viewController.activeAccount = NCManageDatabase.shared.getActiveAccount()
-                viewController.accounts = accounts
-                viewController.enableTimerProgress = true
-                viewController.enableAddAccount = false
-                viewController.dismissDidEnterBackground = false
-                viewController.delegate = self
-
-                let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height / 5)
-                let numberCell = accounts.count
-                let height = min(CGFloat(numberCell * Int(viewController.heightCell) + 45), screenHeighMax)
-
-                let popup = NCPopupViewController(contentController: viewController, popupWidth: 300, popupHeight: height + 20)
-                popup.backgroundAlpha = 0.8
-
-                window?.rootViewController?.present(popup, animated: true)
-                viewController.startTimer()
-            }
-        }
-    }
-
-    func passcodeReset(_ passcodeViewController: TOPasscodeViewController) {
-        resetApplication()
-    }
-}
-
-extension AppDelegate: NCAccountRequestDelegate {
-    func accountRequestChangeAccount(account: String) {
-        changeAccount(account, userProfile: nil)
-    }
-}

+ 76 - 0
iOSClient/Assistant/Create Task/NCAssistantCreateNewTask.swift

@@ -0,0 +1,76 @@
+//
+//  NCAssistantCreateNewTask.swift
+//  Nextcloud
+//
+//  Created by Milen on 09.04.24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import SwiftUI
+
+struct NCAssistantCreateNewTask: View {
+    @EnvironmentObject var model: NCAssistantTask
+    @State var text = ""
+    @FocusState private var inFocus: Bool
+    @Environment(\.presentationMode) var presentationMode
+
+    var body: some View {
+        VStack {
+            Text(model.selectedType?.description ?? "")
+                .frame(maxWidth: .infinity, alignment: .topLeading)
+
+            ZStack(alignment: .topLeading) {
+                if text.isEmpty {
+                    Text(NSLocalizedString("_input_", comment: ""))
+                        .padding(24)
+                        .foregroundStyle(.secondary)
+                }
+
+                TextEditor(text: $text)
+                    .frame(maxWidth: .infinity, alignment: .topLeading)
+                    .padding()
+                    .transparentScrolling()
+                    .background(Color(NCBrandColor.shared.textColor2).opacity(0.1))
+                    .focused($inFocus)
+            }
+            .background(Color(NCBrandColor.shared.textColor2).opacity(0.1))
+            .clipShape(.rect(cornerRadius: 8))
+        }
+        .toolbar {
+            Button(action: {
+                model.scheduleTask(input: text)
+                presentationMode.wrappedValue.dismiss()
+            }, label: {
+                Text(NSLocalizedString("_create_", comment: ""))
+            })
+            .disabled(text.isEmpty)
+        }
+        .navigationTitle(String(format: NSLocalizedString("_new_task_", comment: ""), model.selectedType?.name ?? ""))
+        .navigationBarTitleDisplayMode(.inline)
+        .padding()
+        .onAppear {
+            inFocus = true
+        }
+    }
+}
+
+#Preview {
+    let model = NCAssistantTask()
+
+    return NCAssistantCreateNewTask()
+        .environmentObject(model)
+        .onAppear {
+            model.loadDummyData()
+        }}
+
+private extension View {
+    func transparentScrolling() -> some View {
+        if #available(iOS 16.0, *) {
+            return scrollContentBackground(.hidden)
+        } else {
+            return onAppear {
+                UITextView.appearance().backgroundColor = .clear
+            }
+        }
+    }
+}

+ 186 - 0
iOSClient/Assistant/Models/NCAssistantTask.swift

@@ -0,0 +1,186 @@
+//
+//  NCAssistantModel.swift
+//  Nextcloud
+//
+//  Created by Milen on 08.04.24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import NextcloudKit
+import SwiftUI
+
+class NCAssistantTask: ObservableObject {
+    @Published var types: [NKTextProcessingTaskType] = []
+    @Published var filteredTasks: [NKTextProcessingTask] = []
+    @Published var selectedType: NKTextProcessingTaskType?
+    @Published var selectedTask: NKTextProcessingTask?
+    @Published var hasError: Bool = false
+    @Published var isLoading: Bool = false
+
+    private var tasks: [NKTextProcessingTask] = []
+    private let excludedTypeIds = ["OCA\\ContextChat\\TextProcessing\\ContextChatTaskType"]
+
+    init() {
+        load()
+    }
+
+    func load() {
+        loadAllTypes()
+    }
+
+    func filterTasks(ofType type: NKTextProcessingTaskType?) {
+        if let type {
+            self.filteredTasks = tasks.filter({ $0.type == type.id })
+        } else {
+            self.filteredTasks = tasks
+        }
+
+        self.filteredTasks = filteredTasks.sorted(by: { $0.completionExpectedAt ?? 0 > $1.completionExpectedAt ?? 0 })
+    }
+
+    func selectTaskType(_ type: NKTextProcessingTaskType?) {
+        selectedType = type
+        filterTasks(ofType: self.selectedType)
+    }
+
+    func selectTask(_ task: NKTextProcessingTask) {
+        selectedTask = task
+        guard let id = task.id else { return }
+        isLoading = true
+
+        NextcloudKit.shared.textProcessingGetTask(taskId: id) { _, task, _, error in
+            self.isLoading = false
+
+            if error != .success {
+                self.hasError = true
+                return
+            }
+
+            self.selectedTask = task
+        }
+    }
+
+    func scheduleTask(input: String) {
+        isLoading = true
+
+        NextcloudKit.shared.textProcessingSchedule(input: input, typeId: selectedType?.id ?? "", identifier: "assistant") { _, task, _, error in
+            self.isLoading = false
+
+            if error != .success {
+                self.hasError = true
+                return
+            }
+
+            guard let task else { return }
+
+            withAnimation {
+                self.tasks.insert(task, at: 0)
+                self.filteredTasks.insert(task, at: 0)
+            }
+
+        }
+    }
+
+    func deleteTask(_ task: NKTextProcessingTask) {
+        guard let id = task.id else { return }
+        isLoading = true
+
+        NextcloudKit.shared.textProcessingDeleteTask(taskId: id) { _, task, _, error in
+            self.isLoading = false
+
+            if error != .success {
+                self.hasError = true
+                return
+            }
+
+            withAnimation {
+                self.tasks.removeAll(where: { $0.id == task?.id })
+                self.filteredTasks.removeAll(where: { $0.id == task?.id })
+            }
+
+        }
+    }
+
+    private func loadAllTypes() {
+        isLoading = true
+
+        NextcloudKit.shared.textProcessingGetTypes { _, types, _, error in
+            self.isLoading = false
+
+            if error != .success {
+                self.hasError = true
+                return
+            }
+
+            guard let filteredTypes = types?.filter({ !self.excludedTypeIds.contains($0.id ?? "")}), !filteredTypes.isEmpty else { return }
+
+            withAnimation {
+                self.types = filteredTypes
+            }
+
+            if self.selectedType == nil {
+                self.selectTaskType(filteredTypes.first)
+            }
+
+            self.loadAllTasks()
+        }
+    }
+
+    private func loadAllTasks(appId: String = "assistant") {
+        isLoading = true
+
+        NextcloudKit.shared.textProcessingTaskList(appId: appId) { _, tasks, _, error in
+            self.isLoading = false
+
+            if error != .success {
+                self.hasError = true
+                return
+            }
+
+            guard let tasks = tasks else { return }
+            self.tasks = tasks
+            self.filterTasks(ofType: self.selectedType)
+        }
+    }
+}
+
+extension NCAssistantTask {
+    public func loadDummyData() {
+        let loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+
+        var tasks: [NKTextProcessingTask] = []
+
+        for index in 1...10 {
+            tasks.append(NKTextProcessingTask(id: index, type: "OCP\\TextProcessing\\FreePromptTaskType", status: index, userId: "christine", appId: "assistant", input: loremIpsum, output: loremIpsum, identifier: "", completionExpectedAt: 1712666412))
+        }
+
+        self.types = [
+            NKTextProcessingTaskType(id: "1", name: "Free Prompt", description: ""),
+            NKTextProcessingTaskType(id: "2", name: "Summarize", description: ""),
+            NKTextProcessingTaskType(id: "3", name: "Generate headline", description: ""),
+            NKTextProcessingTaskType(id: "4", name: "Reformulate", description: "")
+        ]
+        self.tasks = tasks
+        self.filteredTasks = tasks
+        self.selectedType = types[0]
+        self.selectedTask = filteredTasks[0]
+
+    }
+}
+
+extension NKTextProcessingTask {
+    struct StatusInfo {
+        let stringKey, imageSystemName: String
+    }
+
+    var statusInfo: StatusInfo {
+        return switch status {
+        case 1: StatusInfo(stringKey: "_assistant_task_scheduled_", imageSystemName: "clock")
+        case 2: StatusInfo(stringKey: "_assistant_task_in_progress_", imageSystemName: "clock.badge")
+        case 3: StatusInfo(stringKey: "_assistant_task_completed_", imageSystemName: "checkmark.circle")
+        case 4: StatusInfo(stringKey: "_assistant_task_failed_", imageSystemName: "exclamationmark.circle")
+        default: StatusInfo(stringKey: "_assistant_task_unknown_", imageSystemName: "questionmark.circle")
+        }
+    }
+}

+ 195 - 0
iOSClient/Assistant/NCAssistant.swift

@@ -0,0 +1,195 @@
+//
+//  NCAssistant.swift
+//  Nextcloud
+//
+//  Created by Milen on 03.04.24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import SwiftUI
+import NextcloudKit
+import PopupView
+
+struct NCAssistant: View {
+    @EnvironmentObject var model: NCAssistantTask
+    @State var presentNewTaskDialog = false
+    @State var input = ""
+    @Environment(\.presentationMode) var presentationMode
+
+    var body: some View {
+        NavigationView {
+            ZStack {
+                TaskList()
+
+                if model.types.isEmpty, !model.isLoading {
+                    NCAssistantEmptyView(titleKey: "_no_types_", subtitleKey: "_no_types_subtitle_")
+                } else if model.filteredTasks.isEmpty, !model.isLoading {
+                    NCAssistantEmptyView(titleKey: "_no_tasks_", subtitleKey: "_create_task_subtitle_")
+                }
+            }
+            .toolbar {
+                ToolbarItem(placement: .topBarLeading) {
+                    Button(NSLocalizedString("_close_", comment: "")) {
+                        presentationMode.wrappedValue.dismiss()
+                    }
+                }
+                ToolbarItem(placement: .topBarTrailing) {
+                    NavigationLink(destination: NCAssistantCreateNewTask()) {
+                        Image(systemName: "plus")
+                            .font(Font.system(.body).weight(.light))
+                    }
+                    .disabled(model.selectedType == nil)
+                }
+            }
+            .navigationBarTitleDisplayMode(.inline)
+            .navigationTitle(NSLocalizedString("_assistant_", comment: ""))
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
+            .safeAreaInset(edge: .top, spacing: -10) {
+                ScrollViewReader { scrollProxy in
+                    ScrollView(.horizontal, showsIndicators: false) {
+                        LazyHStack {
+                            ForEach(model.types, id: \.id) { type in
+                                TypeButton(taskType: type, scrollProxy: scrollProxy)
+                            }
+                        }
+                        .padding(20)
+                        .frame(height: 50)
+                    }
+                }
+            }
+        }
+        .navigationViewStyle(.stack)
+        .popup(isPresented: $model.hasError) {
+            Text(NSLocalizedString("_error_occurred_", comment: ""))
+                .padding()
+                .background(.red)
+                .cornerRadius(30.0)
+        } customize: {
+            $0
+                .type(.floater())
+                .autohideIn(2)
+                .position(.bottom)
+        }
+        .accentColor(Color(NCBrandColor.shared.iconImageColor))
+        .environmentObject(model)
+    }
+}
+
+#Preview {
+    let model = NCAssistantTask()
+
+    return NCAssistant()
+        .environmentObject(model)
+        .onAppear {
+            model.loadDummyData()
+        }
+}
+
+struct TaskList: View {
+    @EnvironmentObject var model: NCAssistantTask
+
+    var body: some View {
+        List(model.filteredTasks, id: \.id) { task in
+            TaskItem(task: task)
+        }
+        .if(!model.types.isEmpty) { view in
+            view.refreshable {
+                model.load()
+            }
+        }
+    }
+}
+
+struct TypeButton: View {
+    @EnvironmentObject var model: NCAssistantTask
+
+    let taskType: NKTextProcessingTaskType?
+    var scrollProxy: ScrollViewProxy
+
+    var body: some View {
+        Button {
+            model.selectTaskType(taskType)
+
+            withAnimation {
+                scrollProxy.scrollTo(taskType?.id, anchor: .center)
+            }
+        } label: {
+            Text(taskType?.name ?? "").font(.body)
+        }
+        .padding(.horizontal)
+        .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))
+        }
+        .if(model.selectedType?.id != taskType?.id) { view in
+            view.background(.ultraThinMaterial)
+        }
+        .clipShape(.capsule)
+        .overlay(
+            RoundedRectangle(cornerRadius: 20, style: RoundedCornerStyle.continuous)
+                .stroke(.tertiary.opacity(0.2), lineWidth: 1)
+        )
+        .id(taskType?.id)
+    }
+}
+
+struct TaskItem: View {
+    @EnvironmentObject var model: NCAssistantTask
+    @State var showDeleteConfirmation = false
+    let task: NKTextProcessingTask
+
+    var body: some View {
+        NavigationLink(destination: NCAssistantTaskDetail(task: task)) {
+            VStack(alignment: .leading) {
+                Text(task.input ?? "")
+                    .lineLimit(4)
+
+                HStack {
+                    Label(
+                        title: {
+                            Text(NSLocalizedString(task.statusInfo.stringKey, comment: ""))
+                        },
+                        icon: {
+                            Image(systemName: task.statusInfo.imageSystemName)
+                                .renderingMode(.original)
+                                .font(Font.system(.body).weight(.light))
+                        }
+                    )
+                    .padding(.top, 1)
+                    .labelStyle(CustomLabelStyle())
+
+                    if let completionExpectedAt = task.completionExpectedAt {
+                        Text(NCUtility().dateDiff(.init(timeIntervalSince1970: TimeInterval(completionExpectedAt))))
+                            .frame(maxWidth: .infinity, alignment: .trailing)
+                            .foregroundStyle(.tertiary)
+                    }
+                }
+            }
+            .swipeActions {
+                Button(NSLocalizedString("_delete_", comment: "")) {
+                    showDeleteConfirmation = true
+                }
+                .tint(.red)
+            }
+            .confirmationDialog("", isPresented: $showDeleteConfirmation) {
+                Button(NSLocalizedString("_delete_", comment: ""), role: .destructive) {
+                    withAnimation {
+                        model.deleteTask(task)
+                    }
+                }
+            }
+        }
+    }
+}
+
+private struct CustomLabelStyle: LabelStyle {
+    var spacing: Double = 5
+
+    func makeBody(configuration: Configuration) -> some View {
+        HStack(spacing: spacing) {
+            configuration.icon
+            configuration.title
+        }
+    }
+}

+ 37 - 0
iOSClient/Assistant/NCAssistantEmptyView.swift

@@ -0,0 +1,37 @@
+//
+//  EmptyTasksView.swift
+//  Nextcloud
+//
+//  Created by Milen on 16.04.24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import SwiftUI
+
+struct NCAssistantEmptyView: View {
+    let titleKey, subtitleKey: String
+
+    var body: some View {
+        VStack {
+            Image(systemName: "sparkles")
+                .renderingMode(.template)
+                .resizable()
+                .aspectRatio(contentMode: .fit)
+                .foregroundStyle(Color(NCBrandColor.shared.brandElement))
+                .font(Font.system(.body).weight(.light))
+                .frame(height: 100)
+
+            Text(NSLocalizedString(titleKey, comment: ""))
+                .font(.system(size: 22, weight: .bold))
+                .padding(.bottom, 5)
+
+            Text(NSLocalizedString(subtitleKey, comment: ""))
+                .font(.system(size: 14))
+                .foregroundStyle(.secondary)
+        }
+    }
+}
+
+#Preview {
+    NCAssistantEmptyView(titleKey: "_no_tasks_", subtitleKey: "_create_task_subtitle_")
+}

+ 102 - 0
iOSClient/Assistant/Task Detail/NCAssistantTaskDetail.swift

@@ -0,0 +1,102 @@
+//
+//  NCAssistantTaskDetail.swift
+//  Nextcloud
+//
+//  Created by Milen on 10.04.24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import SwiftUI
+import NextcloudKit
+
+struct NCAssistantTaskDetail: View {
+    @EnvironmentObject var model: NCAssistantTask
+    let task: NKTextProcessingTask
+
+    var body: some View {
+        ZStack(alignment: .bottom) {
+            InputOutputScrollView(task: task)
+
+            BottomDetailsBar(task: task)
+        }
+        .navigationBarTitleDisplayMode(.inline)
+        .navigationTitle(NSLocalizedString("_task_details_", comment: ""))
+        .onAppear {
+            model.selectTask(task)
+        }
+    }
+}
+
+#Preview {
+    let model = NCAssistantTask()
+
+    return NCAssistantTaskDetail(task: NKTextProcessingTask(id: 1, type: "OCP\\TextProcessing\\FreePromptTaskType", status: 1, userId: "christine", appId: "assistant", input: "", output: "", identifier: "", completionExpectedAt: 1712666412))
+        .environmentObject(model)
+        .onAppear {
+            model.loadDummyData()
+        }
+}
+
+struct InputOutputScrollView: View {
+    @EnvironmentObject var model: NCAssistantTask
+    let task: NKTextProcessingTask
+
+    var body: some View {
+        ScrollView {
+            VStack(alignment: .leading) {
+                Text(NSLocalizedString("_input_", comment: "")).font(.headline)
+                    .padding(.top, 10)
+
+                Text(model.selectedTask?.input ?? "")
+                    .frame(maxWidth: .infinity, alignment: .topLeading)
+                    .padding()
+                    .background(Color(NCBrandColor.shared.textColor2).opacity(0.1))
+                    .clipShape(.rect(cornerRadius: 8))
+
+                Text(NSLocalizedString("_output_", comment: "")).font(.headline)
+                    .padding(.top, 10)
+
+                Text(model.selectedTask?.output ?? "")
+                    .frame(maxWidth: .infinity, alignment: .topLeading)
+                    .padding()
+                    .background(Color(NCBrandColor.shared.textColor2).opacity(0.1))
+                    .clipShape(.rect(cornerRadius: 8))
+
+            }
+            .padding(.horizontal)
+            .padding(.bottom, 80)
+        }
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
+    }
+}
+
+struct BottomDetailsBar: View {
+    @EnvironmentObject var model: NCAssistantTask
+    let task: NKTextProcessingTask
+
+    var body: some View {
+        VStack(spacing: 0) {
+            Divider()
+            HStack(alignment: .bottom) {
+                Label(
+                    title: {
+                        Text(NSLocalizedString(model.selectedTask?.statusInfo.stringKey ?? "", comment: ""))
+                    }, icon: {
+                        Image(systemName: model.selectedTask?.statusInfo.imageSystemName ?? "")
+                            .renderingMode(.original)
+                            .font(Font.system(.body).weight(.light))
+                    }
+                )
+                .frame(maxWidth: .infinity, alignment: .leading)
+
+                if let completionExpectedAt = task.completionExpectedAt {
+                    Text(NCUtility().dateDiff(.init(timeIntervalSince1970: TimeInterval(completionExpectedAt))))
+                        .frame(maxWidth: .infinity, alignment: .trailing)
+                }
+            }
+            .padding()
+            .background(.bar)
+            .frame(alignment: .bottom)
+        }
+    }
+}

+ 3 - 2
iOSClient/AudioRecorder/NCAudioRecorderViewController.swift

@@ -33,6 +33,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
     var recording: NCAudioRecorder!
     var recording: NCAudioRecorder!
     var startDate: Date = Date()
     var startDate: Date = Date()
     var fileName: String = ""
     var fileName: String = ""
+    var serverUrl = ""
     let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
 
 
     @IBOutlet weak var contentContainerView: UIView!
     @IBOutlet weak var contentContainerView: UIView!
@@ -54,7 +55,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
         voiceRecordHUD.fillColor = UIColor.green
         voiceRecordHUD.fillColor = UIColor.green
 
 
         Task {
         Task {
-            self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.appDelegate.account, serverUrl: self.appDelegate.activeServerUrl)
+            self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.appDelegate.account, serverUrl: self.serverUrl)
             recording = NCAudioRecorder(to: self.fileName)
             recording = NCAudioRecorder(to: self.fileName)
             recording.delegate = self
             recording.delegate = self
             do {
             do {
@@ -96,7 +97,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
 
 
     func uploadMetadata() {
     func uploadMetadata() {
         let fileNamePath = NSTemporaryDirectory() + self.fileName
         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: appDelegate.activeServerUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
+        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: "")
         metadata.session = NCNetworking.shared.sessionUploadBackground
         metadata.session = NCNetworking.shared.sessionUploadBackground
         metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
         metadata.sessionSelector = NCGlobal.shared.selectorUploadFile
         metadata.status = NCGlobal.shared.metadataStatusWaitUpload
         metadata.status = NCGlobal.shared.metadataStatusWaitUpload

+ 1 - 1
iOSClient/BrowserWeb/NCBrowserWeb.swift

@@ -55,7 +55,7 @@ class NCBrowserWeb: UIViewController {
             buttonExit.isHidden = true
             buttonExit.isHidden = true
         } else {
         } else {
             self.view.bringSubviewToFront(buttonExit)
             self.view.bringSubviewToFront(buttonExit)
-            let image = NCUtility().loadImage(named: "xmark", color: .systemBlue)
+            let image = NCUtility().loadImage(named: "xmark", colors: [.systemBlue])
             buttonExit.setImage(image, for: .normal)
             buttonExit.setImage(image, for: .normal)
         }
         }
 
 

+ 1 - 1
iOSClient/Color/NCColorPicker.swift

@@ -65,7 +65,7 @@ class NCColorPicker: UIViewController {
             }
             }
         }
         }
 
 
-        closeButton.setImage(NCUtility().loadImage(named: "xmark", color: .label), for: .normal)
+        closeButton.setImage(NCUtility().loadImage(named: "xmark", colors: [NCBrandColor.shared.iconImageColor]), for: .normal)
         titleLabel.text = NSLocalizedString("_select_color_", comment: "")
         titleLabel.text = NSLocalizedString("_select_color_", comment: "")
 
 
         orangeButton.backgroundColor = .orange
         orangeButton.backgroundColor = .orange

+ 2 - 6
iOSClient/Data/NCManageDatabase+Account.swift

@@ -351,17 +351,13 @@ extension NCManageDatabase {
         }
         }
     }
     }
 
 
-    @objc func setAccountAutoUploadFileName(_ fileName: String?) {
+    @objc func setAccountAutoUploadFileName(_ fileName: String) {
 
 
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             try realm.write {
             try realm.write {
                 if let result = realm.objects(tableAccount.self).filter("active == true").first {
                 if let result = realm.objects(tableAccount.self).filter("active == true").first {
-                    if let fileName = fileName {
-                        result.autoUploadFileName = fileName
-                    } else {
-                        result.autoUploadFileName = self.getAccountAutoUploadFileName()
-                    }
+                    result.autoUploadFileName = fileName
                 }
                 }
             }
             }
         } catch let error {
         } catch let error {

+ 0 - 7
iOSClient/Data/NCManageDatabase+Avatar.swift

@@ -26,7 +26,6 @@ import RealmSwift
 import NextcloudKit
 import NextcloudKit
 
 
 class tableAvatar: Object {
 class tableAvatar: Object {
-
     @objc dynamic var date = NSDate()
     @objc dynamic var date = NSDate()
     @objc dynamic var etag = ""
     @objc dynamic var etag = ""
     @objc dynamic var fileName = ""
     @objc dynamic var fileName = ""
@@ -38,9 +37,7 @@ class tableAvatar: Object {
 }
 }
 
 
 extension NCManageDatabase {
 extension NCManageDatabase {
-
     func addAvatar(fileName: String, etag: String) {
     func addAvatar(fileName: String, etag: String) {
-
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             try realm.write {
             try realm.write {
@@ -57,7 +54,6 @@ extension NCManageDatabase {
     }
     }
 
 
     func getTableAvatar(fileName: String) -> tableAvatar? {
     func getTableAvatar(fileName: String) -> tableAvatar? {
-
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
@@ -71,7 +67,6 @@ extension NCManageDatabase {
     }
     }
 
 
     func clearAllAvatarLoaded() {
     func clearAllAvatarLoaded() {
-
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             try realm.write {
             try realm.write {
@@ -88,7 +83,6 @@ extension NCManageDatabase {
 
 
     @discardableResult
     @discardableResult
     func setAvatarLoaded(fileName: String) -> UIImage? {
     func setAvatarLoaded(fileName: String) -> UIImage? {
-
         let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
         let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
         var image: UIImage?
         var image: UIImage?
 
 
@@ -112,7 +106,6 @@ extension NCManageDatabase {
     }
     }
 
 
     func getImageAvatarLoaded(fileName: String) -> UIImage? {
     func getImageAvatarLoaded(fileName: String) -> UIImage? {
-
         let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
         let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
 
 
         do {
         do {

+ 12 - 3
iOSClient/Data/NCManageDatabase+Capabilities.swift

@@ -103,6 +103,7 @@ extension NCManageDatabase {
                         let external: External?
                         let external: External?
                         let groupfolders: GroupFolders?
                         let groupfolders: GroupFolders?
                         let securityguard: SecurityGuard?
                         let securityguard: SecurityGuard?
+                        let assistant: Assistant?
 
 
                         enum CodingKeys: String, CodingKey {
                         enum CodingKeys: String, CodingKey {
                             case filessharing = "files_sharing"
                             case filessharing = "files_sharing"
@@ -112,6 +113,7 @@ extension NCManageDatabase {
                             case userstatus = "user_status"
                             case userstatus = "user_status"
                             case external, groupfolders
                             case external, groupfolders
                             case securityguard = "security_guard"
                             case securityguard = "security_guard"
+                            case assistant
                         }
                         }
 
 
                         struct FilesSharing: Codable {
                         struct FilesSharing: Codable {
@@ -269,6 +271,11 @@ extension NCManageDatabase {
                         struct SecurityGuard: Codable {
                         struct SecurityGuard: Codable {
                             let diagnostics: Bool?
                             let diagnostics: Bool?
                         }
                         }
+
+                        struct Assistant: Codable {
+                            let enabled: Bool?
+                            let version: String?
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -324,14 +331,16 @@ extension NCManageDatabase {
             global.capabilityE2EEEnabled = data.capabilities.endtoendencryption?.enabled ?? false
             global.capabilityE2EEEnabled = data.capabilities.endtoendencryption?.enabled ?? false
             global.capabilityE2EEApiVersion = data.capabilities.endtoendencryption?.apiversion ?? ""
             global.capabilityE2EEApiVersion = data.capabilities.endtoendencryption?.apiversion ?? ""
 
 
-            global.capabilityRichdocumentsEnabled = json.ocs.data.capabilities.richdocuments?.directediting ?? false
-            global.capabilityRichdocumentsMimetypes.removeAll()
+            global.capabilityRichDocumentsEnabled = json.ocs.data.capabilities.richdocuments?.directediting ?? false
+            global.capabilityRichDocumentsMimetypes.removeAll()
             if let mimetypes = data.capabilities.richdocuments?.mimetypes {
             if let mimetypes = data.capabilities.richdocuments?.mimetypes {
                 for mimetype in mimetypes {
                 for mimetype in mimetypes {
-                    global.capabilityRichdocumentsMimetypes.append(mimetype)
+                    global.capabilityRichDocumentsMimetypes.append(mimetype)
                 }
                 }
             }
             }
 
 
+            global.capabilityAssistantEnabled = data.capabilities.assistant?.enabled ?? false
+
             global.capabilityActivity.removeAll()
             global.capabilityActivity.removeAll()
             if let activities = data.capabilities.activity?.apiv2 {
             if let activities = data.capabilities.activity?.apiv2 {
                 for activity in activities {
                 for activity in activities {

+ 8 - 4
iOSClient/Data/NCManageDatabase+Directory.swift

@@ -50,8 +50,9 @@ extension NCManageDatabase {
     func addDirectory(e2eEncrypted: Bool, favorite: Bool, ocId: String, fileId: String, etag: String? = nil, permissions: String? = nil, richWorkspace: String? = nil, serverUrl: String, account: String) {
     func addDirectory(e2eEncrypted: Bool, favorite: Bool, ocId: String, fileId: String, etag: String? = nil, permissions: String? = nil, richWorkspace: String? = nil, serverUrl: String, account: String) {
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
+            let result = realm.objects(tableDirectory.self).filter("account == %@ AND ocId == %@", account, ocId).first
             try realm.write {
             try realm.write {
-                if let result = realm.objects(tableDirectory.self).filter("account == %@ AND ocId == %@", account, ocId).first {
+                if let result {
                     result.e2eEncrypted = e2eEncrypted
                     result.e2eEncrypted = e2eEncrypted
                     result.favorite = favorite
                     result.favorite = favorite
                     if let etag { result.etag = etag }
                     if let etag { result.etag = etag }
@@ -68,7 +69,7 @@ extension NCManageDatabase {
                     if let richWorkspace { result.richWorkspace = richWorkspace }
                     if let richWorkspace { result.richWorkspace = richWorkspace }
                     result.serverUrl = serverUrl
                     result.serverUrl = serverUrl
                     result.account = account
                     result.account = account
-                    realm.add(result, update: .all)
+                    realm.add(result, update: .modified)
                 }
                 }
             }
             }
         } catch let error {
         } catch let error {
@@ -80,8 +81,11 @@ extension NCManageDatabase {
 
 
 #if !EXTENSION
 #if !EXTENSION
         DispatchQueue.main.async {
         DispatchQueue.main.async {
-            if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
-                appDelegate.listFilesVC[serverUrl] = nil
+            let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
+            for windowScene in windowScenes {
+                if let mainTabBarController = windowScene.keyWindow?.rootViewController as? NCMainTabBarController {
+                    mainTabBarController.filesServerUrl.removeValue(forKey: serverUrl)
+                }
             }
             }
         }
         }
 #endif
 #endif

+ 4 - 1
iOSClient/Data/NCManageDatabase+Metadata+Session.swift

@@ -104,7 +104,7 @@ extension NCManageDatabase {
     }
     }
 
 
     @discardableResult
     @discardableResult
-    func setMetadatasSessionInWaitDownload(metadatas: [tableMetadata], session: String, selector: String) -> tableMetadata? {
+    func setMetadatasSessionInWaitDownload(metadatas: [tableMetadata], session: String, selector: String, sceneIdentifier: String? = nil) -> tableMetadata? {
         if metadatas.isEmpty { return nil }
         if metadatas.isEmpty { return nil }
         var metadataUpdated: tableMetadata?
         var metadataUpdated: tableMetadata?
 
 
@@ -113,6 +113,7 @@ extension NCManageDatabase {
             try realm.write {
             try realm.write {
                 for metadata in metadatas {
                 for metadata in metadatas {
                     if let result = realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first {
                     if let result = realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first {
+                        result.sceneIdentifier = sceneIdentifier
                         result.session = session
                         result.session = session
                         result.sessionError = ""
                         result.sessionError = ""
                         result.sessionSelector = selector
                         result.sessionSelector = selector
@@ -120,6 +121,7 @@ extension NCManageDatabase {
                         result.sessionDate = Date()
                         result.sessionDate = Date()
                         metadataUpdated = tableMetadata(value: result)
                         metadataUpdated = tableMetadata(value: result)
                     } else {
                     } else {
+                        metadata.sceneIdentifier = sceneIdentifier
                         metadata.session = session
                         metadata.session = session
                         metadata.sessionError = ""
                         metadata.sessionError = ""
                         metadata.sessionSelector = selector
                         metadata.sessionSelector = selector
@@ -142,6 +144,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             try realm.write {
             try realm.write {
                 for metadata in metadatas {
                 for metadata in metadatas {
+                    metadata.sceneIdentifier = nil
                     metadata.session = ""
                     metadata.session = ""
                     metadata.sessionError = ""
                     metadata.sessionError = ""
                     metadata.sessionSelector = ""
                     metadata.sessionSelector = ""

+ 46 - 44
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -97,6 +97,7 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var quotaAvailableBytes: Int64 = 0
     @objc dynamic var quotaAvailableBytes: Int64 = 0
     @objc dynamic var resourceType = ""
     @objc dynamic var resourceType = ""
     @objc dynamic var richWorkspace: String?
     @objc dynamic var richWorkspace: String?
+    @objc dynamic var sceneIdentifier: String?
     @objc dynamic var serverUrl = ""
     @objc dynamic var serverUrl = ""
     @objc dynamic var session = ""
     @objc dynamic var session = ""
     @objc dynamic var sessionDate: Date?
     @objc dynamic var sessionDate: Date?
@@ -211,7 +212,7 @@ extension tableMetadata {
     }
     }
 
 
     var canSetAsAvailableOffline: Bool {
     var canSetAsAvailableOffline: Bool {
-        return session.isEmpty && !isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted
+        return session.isEmpty && !isDirectoryE2EE && !e2eEncrypted
     }
     }
 
 
     var canShare: Bool {
     var canShare: Bool {
@@ -388,14 +389,13 @@ extension NCManageDatabase {
         return metadata
         return metadata
     }
     }
 
 
-    func convertFilesToMetadatas(_ files: [NKFile], useMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatasFolder: [tableMetadata], _ metadatas: [tableMetadata]) -> Void) {
+    func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool, completion: @escaping (_ metadataFolder: tableMetadata, _ metadatas: [tableMetadata]) -> Void) {
 
 
         var counter: Int = 0
         var counter: Int = 0
         var isDirectoryE2EE: Bool = false
         var isDirectoryE2EE: Bool = false
         let listServerUrl = ThreadSafeDictionary<String, Bool>()
         let listServerUrl = ThreadSafeDictionary<String, Bool>()
 
 
         var metadataFolder = tableMetadata()
         var metadataFolder = tableMetadata()
-        var metadataFolders: [tableMetadata] = []
         var metadatas: [tableMetadata] = []
         var metadatas: [tableMetadata] = []
 
 
         for file in files {
         for file in files {
@@ -409,26 +409,23 @@ extension NCManageDatabase {
 
 
             let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
             let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
 
 
-            if counter == 0 && useMetadataFolder {
-                metadataFolder = tableMetadata.init(value: metadata)
+            if counter == 0 && useFirstAsMetadataFolder {
+                metadataFolder = tableMetadata(value: metadata)
             } else {
             } else {
                 metadatas.append(metadata)
                 metadatas.append(metadata)
-                if metadata.directory {
-                    metadataFolders.append(metadata)
-                }
             }
             }
 
 
             counter += 1
             counter += 1
         }
         }
 
 
-        completion(metadataFolder, metadataFolders, metadatas)
+        completion(metadataFolder, metadatas)
     }
     }
 
 
-    func convertFilesToMetadatas(_ files: [NKFile], useMetadataFolder: Bool) async -> (metadataFolder: tableMetadata, metadatasFolder: [tableMetadata], metadatas: [tableMetadata]) {
+    func convertFilesToMetadatas(_ files: [NKFile], useFirstAsMetadataFolder: Bool) async -> (metadataFolder: tableMetadata, metadatas: [tableMetadata]) {
 
 
         await withUnsafeContinuation({ continuation in
         await withUnsafeContinuation({ continuation in
-            convertFilesToMetadatas(files, useMetadataFolder: useMetadataFolder) { metadataFolder, metadatasFolder, metadatas in
-                continuation.resume(returning: (metadataFolder, metadatasFolder, metadatas))
+            convertFilesToMetadatas(files, useFirstAsMetadataFolder: useFirstAsMetadataFolder) { metadataFolder, metadatas in
+                continuation.resume(returning: (metadataFolder, metadatas))
             }
             }
         })
         })
     }
     }
@@ -488,7 +485,7 @@ extension NCManageDatabase {
 
 
     @discardableResult
     @discardableResult
     func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
     func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
-        let result = tableMetadata.init(value: metadata)
+        let result = tableMetadata(value: metadata)
 
 
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
@@ -499,7 +496,7 @@ extension NCManageDatabase {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
             return nil
             return nil
         }
         }
-        return tableMetadata.init(value: result)
+        return tableMetadata(value: result)
     }
     }
 
 
     func addMetadatasWithoutUpdate(_ metadatas: [tableMetadata]) {
     func addMetadatasWithoutUpdate(_ metadatas: [tableMetadata]) {
@@ -634,7 +631,11 @@ extension NCManageDatabase {
                     result.favorite = false
                     result.favorite = false
                 }
                 }
                 for metadata in metadatas {
                 for metadata in metadatas {
-                    realm.add(metadata, update: .all)
+                    if let result = realm.objects(tableMetadata.self).filter("account == %@ AND ocId == %@", account, metadata.ocId).first {
+                        result.favorite = true
+                    } else {
+                        realm.add(metadata, update: .modified)
+                    }
                 }
                 }
             }
             }
         } catch let error {
         } catch let error {
@@ -671,7 +672,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter(predicate).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -684,7 +685,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending).first else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -698,7 +699,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
             let results = realm.objects(tableMetadata.self).filter(predicate)
             let results = realm.objects(tableMetadata.self).filter(predicate)
-            return Array(results.map { tableMetadata.init(value: $0) })
+            return Array(results.map { tableMetadata(value: $0) })
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -711,7 +712,7 @@ extension NCManageDatabase {
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
-            return Array(results.map { tableMetadata.init(value: $0) })
+            return Array(results.map { tableMetadata(value: $0) })
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -720,7 +721,6 @@ extension NCManageDatabase {
     }
     }
 
 
     func getResultsMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> Results<tableMetadata>? {
     func getResultsMetadatas(predicate: NSPredicate, sorted: String? = nil, ascending: Bool = false) -> Results<tableMetadata>? {
-
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             if let sorted {
             if let sorted {
@@ -759,27 +759,17 @@ extension NCManageDatabase {
         return nil
         return nil
     }
     }
 
 
-    func getAdvancedMetadatas(predicate: NSPredicate, page: Int = 0, limit: Int = 0, sorted: String, ascending: Bool) -> [tableMetadata] {
-
+    func getMetadatas(predicate: NSPredicate, numItems: Int, sorted: String, ascending: Bool) -> [tableMetadata] {
+        var counter: Int = 0
         var metadatas: [tableMetadata] = []
         var metadatas: [tableMetadata] = []
 
 
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
-            if !results.isEmpty {
-                if page == 0 || limit == 0 {
-                    return Array(results.map { tableMetadata.init(value: $0) })
-                } else {
-                    let nFrom = (page - 1) * limit
-                    let nTo = nFrom + (limit - 1)
-                    for n in nFrom...nTo {
-                        if n == results.count {
-                            break
-                        }
-                        metadatas.append(tableMetadata.init(value: results[n]))
-                    }
-                }
+            for result in results where counter < numItems {
+                metadatas.append(tableMetadata(value: result))
+                counter += 1
             }
             }
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
@@ -797,7 +787,7 @@ extension NCManageDatabase {
             if results.isEmpty {
             if results.isEmpty {
                 return nil
                 return nil
             } else {
             } else {
-                return tableMetadata.init(value: results[index])
+                return tableMetadata(value: results[index])
             }
             }
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
@@ -813,7 +803,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -827,7 +817,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             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("fileName == %@ AND serverUrl == %@", fileName, serverUrl).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -845,7 +835,7 @@ extension NCManageDatabase {
                 let realm = try Realm()
                 let realm = try Realm()
                 realm.refresh()
                 realm.refresh()
                 guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
                 guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", ocId).first else { return nil }
-                return tableMetadata.init(value: result)
+                return tableMetadata(value: result)
             } catch let error as NSError {
             } catch let error as NSError {
                 NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
                 NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             }
             }
@@ -874,7 +864,7 @@ extension NCManageDatabase {
             realm.refresh()
             realm.refresh()
             guard let fileId = fileId else { return nil }
             guard let fileId = fileId else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -902,7 +892,7 @@ extension NCManageDatabase {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
             guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).first else { return nil }
             guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -992,7 +982,7 @@ extension NCManageDatabase {
         do {
         do {
             let realm = try Realm()
             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 }
             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.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -1053,7 +1043,7 @@ extension NCManageDatabase {
             realm.refresh()
             realm.refresh()
             guard let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
             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 }
             guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first else { return nil }
-            return tableMetadata.init(value: result)
+            return tableMetadata(value: result)
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
@@ -1150,11 +1140,23 @@ extension NCManageDatabase {
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
             let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
-            return ThreadSafeArray(results.map { tableMetadata.init(value: $0) })
+            return ThreadSafeArray(results.map { tableMetadata(value: $0) })
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
         }
         }
 
 
         return nil
         return nil
     }
     }
+
+    func getMetadata(from url: URL?) -> tableMetadata? {
+        guard let url,
+              var serverUrl = url.deletingLastPathComponent().absoluteString.removingPercentEncoding
+        else { return nil }
+        let fileName = url.lastPathComponent
+
+        if serverUrl.hasSuffix("/") {
+            serverUrl = String(serverUrl.dropLast())
+        }
+        return NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "serverUrl == %@ AND fileName == %@", serverUrl, fileName))
+    }
 }
 }

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

@@ -118,15 +118,11 @@ extension NCManageDatabase {
         }
         }
     }
     }
 
 
-    func getTrash(filePath: String, sort: String?, ascending: Bool?, account: String) -> [tableTrash] {
-
-        let sort = sort ?? "date"
-        let ascending = ascending ?? false
-
+    func getTrash(filePath: String, account: String) -> [tableTrash] {
         do {
         do {
             let realm = try Realm()
             let realm = try Realm()
             realm.refresh()
             realm.refresh()
-            let results = realm.objects(tableTrash.self).filter("account == %@ AND filePath == %@", account, filePath).sorted(byKeyPath: sort, ascending: ascending)
+            let results = realm.objects(tableTrash.self).filter("account == %@ AND filePath == %@", account, filePath).sorted(byKeyPath: "date", ascending: false)
             return Array(results.map { tableTrash.init(value: $0) })
             return Array(results.map { tableTrash.init(value: $0) })
         } catch let error as NSError {
         } catch let error as NSError {
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
             NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")

+ 48 - 66
iOSClient/Diagnostics/NCCapabilitiesView.swift

@@ -23,7 +23,6 @@
 
 
 import SwiftUI
 import SwiftUI
 import NextcloudKit
 import NextcloudKit
-import PreviewSnapshots
 
 
 @objc class NCHostingCapabilitiesView: NSObject {
 @objc class NCHostingCapabilitiesView: NSObject {
 
 
@@ -50,6 +49,7 @@ class NCCapabilitiesViewOO: ObservableObject {
     @Published var capabililies: [Capability] = []
     @Published var capabililies: [Capability] = []
     @Published var homeServer = ""
     @Published var homeServer = ""
     let utilityFileSystem = NCUtilityFileSystem()
     let utilityFileSystem = NCUtilityFileSystem()
+    let utility = NCUtility()
 
 
     init() {
     init() {
         loadCapabilities()
         loadCapabilities()
@@ -65,24 +65,23 @@ class NCCapabilitiesViewOO: ObservableObject {
 
 
         capabililies.removeAll()
         capabililies.removeAll()
 
 
-        if let image = UIImage(named: "share") {
-            capabililies.append(Capability(text: "File sharing", image: image, resize: true, available: NCGlobal.shared.capabilityFileSharingApiEnabled))
-        }
-        if let image = UIImage(systemName: "network") {
-            capabililies.append(Capability(text: "External site", image: image, resize: false, available: NCGlobal.shared.capabilityExternalSites))
-        }
-        if let image = UIImage(systemName: "lock") {
-            capabililies.append(Capability(text: "End-to-End Encryption", image: image, resize: false, available: NCGlobal.shared.capabilityE2EEEnabled))
-        }
-        if let image = UIImage(systemName: "bolt") {
-            capabililies.append(Capability(text: "Activity", image: image, resize: false, available: !NCGlobal.shared.capabilityActivity.isEmpty))
-        }
-        if let image = UIImage(systemName: "bell") {
-            capabililies.append(Capability(text: "Notification", image: image, resize: false, available: !NCGlobal.shared.capabilityNotification.isEmpty))
-        }
-        if let image = UIImage(systemName: "trash") {
-            capabililies.append(Capability(text: "Deleted files", image: image, resize: false, available: NCGlobal.shared.capabilityFilesUndelete))
-        }
+        var image = utility.loadImage(named: "person.fill.badge.plus")
+        capabililies.append(Capability(text: "File sharing", image: image, resize: false, available: NCGlobal.shared.capabilityFileSharingApiEnabled))
+
+        image = utility.loadImage(named: "network")
+        capabililies.append(Capability(text: "External site", image: image, resize: false, available: NCGlobal.shared.capabilityExternalSites))
+
+        image = utility.loadImage(named: "lock")
+        capabililies.append(Capability(text: "End-to-End Encryption", image: image, resize: false, available: NCGlobal.shared.capabilityE2EEEnabled))
+
+        image = utility.loadImage(named: "bolt")
+        capabililies.append(Capability(text: "Activity", image: image, resize: false, available: !NCGlobal.shared.capabilityActivity.isEmpty))
+
+        image = utility.loadImage(named: "bell")
+        capabililies.append(Capability(text: "Notification", image: image, resize: false, available: !NCGlobal.shared.capabilityNotification.isEmpty))
+
+        image = utility.loadImage(named: "trash")
+        capabililies.append(Capability(text: "Deleted files", image: image, resize: false, available: NCGlobal.shared.capabilityFilesUndelete))
 
 
         if let editors = NCManageDatabase.shared.getDirectEditingEditors(account: activeAccount.account) {
         if let editors = NCManageDatabase.shared.getDirectEditingEditors(account: activeAccount.account) {
             for editor in editors {
             for editor in editors {
@@ -94,31 +93,26 @@ class NCCapabilitiesViewOO: ObservableObject {
             }
             }
         }
         }
 
 
-        if let image = UIImage(systemName: "doc.text") {
-            capabililies.append(Capability(text: "Text", image: image, resize: false, available: textEditor))
-        }
-        if let image = UIImage(named: "onlyoffice") {
-            capabililies.append(Capability(text: "ONLYOFFICE", image: image, resize: true, available: onlyofficeEditors))
-        }
-        if let image = UIImage(named: "collabora") {
-            capabililies.append(Capability(text: "Collabora", image: image, resize: true, available: NCGlobal.shared.capabilityRichdocumentsEnabled))
-        }
-        if let image = UIImage(systemName: "moon") {
-            capabililies.append(Capability(text: "User Status", image: image, resize: false, available: NCGlobal.shared.capabilityUserStatusEnabled))
-        }
-        if let image = UIImage(systemName: "ellipsis.bubble") {
-            capabililies.append(Capability(text: "Comments", image: image, resize: false, available: NCGlobal.shared.capabilityFilesComments))
-        }
-        if let image = UIImage(systemName: "lock") {
-            capabililies.append(Capability(text: "Lock file", image: image, resize: false, available: !NCGlobal.shared.capabilityFilesLockVersion.isEmpty))
-        }
-        if let image = UIImage(systemName: "person.2") {
-            capabililies.append(Capability(text: "Group folders", image: image, resize: false, available: NCGlobal.shared.capabilityGroupfoldersEnabled))
-        }
-        if let image = UIImage(systemName: "shield") {
-            capabililies.append(Capability(text: "Security Guard Diagnostics", image: image, resize: false, available: NCGlobal.shared.capabilitySecurityGuardDiagnostics))
+        capabililies.append(Capability(text: "Text", image: utility.loadImage(named: "doc.text"), resize: false, available: textEditor))
+
+        capabililies.append(Capability(text: "ONLYOFFICE", image: utility.loadImage(named: "onlyoffice"), resize: true, available: onlyofficeEditors))
+
+        capabililies.append(Capability(text: "Collabora", image: utility.loadImage(named: "collabora"), resize: true, available: NCGlobal.shared.capabilityRichDocumentsEnabled))
+
+        capabililies.append(Capability(text: "User Status", image: utility.loadImage(named: "moon"), resize: false, available: NCGlobal.shared.capabilityUserStatusEnabled))
+
+        capabililies.append(Capability(text: "Comments", image: utility.loadImage(named: "ellipsis.bubble"), resize: false, available: NCGlobal.shared.capabilityFilesComments))
+
+        capabililies.append(Capability(text: "Lock file", image: utility.loadImage(named: "lock"), resize: false, available: !NCGlobal.shared.capabilityFilesLockVersion.isEmpty))
+
+        capabililies.append(Capability(text: "Group folders", image: utility.loadImage(named: "person.2"), resize: false, available: NCGlobal.shared.capabilityGroupfoldersEnabled))
+
+        if NCBrandOptions.shared.brand != "Nextcloud" {
+            capabililies.append(Capability(text: "Security Guard Diagnostics", image: utility.loadImage(named: "shield"), resize: false, available: NCGlobal.shared.capabilitySecurityGuardDiagnostics))
         }
         }
 
 
+        capabililies.append(Capability(text: "Assistant", image: utility.loadImage(named: "sparkles"), resize: false, available: NCGlobal.shared.capabilityAssistantEnabled))
+
         homeServer = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) + "/"
         homeServer = utilityFileSystem.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) + "/"
     }
     }
 }
 }
@@ -143,7 +137,7 @@ struct NCCapabilitiesView: View {
                     }
                     }
                 }
                 }
                 Section {
                 Section {
-                    CapabilityName(text: $capabilitiesViewOO.homeServer, image: Image(systemName: "house"), resize: false)
+                    CapabilityName(text: $capabilitiesViewOO.homeServer, image: Image(uiImage: NCUtility().loadImage(named: "house")), resize: false)
                 }
                 }
             }
             }
         }
         }
@@ -188,34 +182,22 @@ struct NCCapabilitiesView: View {
                     .foregroundColor(.green)
                     .foregroundColor(.green)
             } else {
             } else {
                 Image(systemName: "multiply.circle.fill")
                 Image(systemName: "multiply.circle.fill")
-                    .foregroundColor(.gray)
+                    .foregroundColor(Color(NCBrandColor.shared.textColor2))
             }
             }
         }
         }
     }
     }
 }
 }
 
 
-struct NCCapabilitiesView_Previews: PreviewProvider {
-    static var previews: some View {
-        snapshots.previews.previewLayout(.device)
+#Preview {
+    func getCapabilitiesViewOOForPreview() -> NCCapabilitiesViewOO {
+        let capabilitiesViewOO = NCCapabilitiesViewOO()
+        capabilitiesViewOO.capabililies = [
+            NCCapabilitiesViewOO.Capability(text: "Collabora", image: UIImage(named: "collabora")!, resize: true, available: true),
+            NCCapabilitiesViewOO.Capability(text: "XXX site", image: UIImage(systemName: "lock.shield")!, resize: false, available: false)
+        ]
+        capabilitiesViewOO.homeServer = "https://cloud.nextcloud.com/remote.php.dav/files/marino/"
+        return capabilitiesViewOO
     }
     }
 
 
-    static var snapshots: PreviewSnapshots<String> {
-        PreviewSnapshots(
-            configurations: [
-                .init(name: NCGlobal.shared.defaultSnapshotConfiguration, state: "")
-            ],
-            configure: { _ in
-                NCCapabilitiesView(capabilitiesStatus: getCapabilitiesViewOOForPreview()).padding(.top, 20).frameForPreview()
-            })
-    }
-}
-
-func getCapabilitiesViewOOForPreview() -> NCCapabilitiesViewOO {
-    let capabilitiesViewOO = NCCapabilitiesViewOO()
-    capabilitiesViewOO.capabililies = [
-        NCCapabilitiesViewOO.Capability(text: "Collabora", image: UIImage(named: "collabora")!, resize: true, available: true),
-        NCCapabilitiesViewOO.Capability(text: "XXX site", image: UIImage(systemName: "lock.shield")!, resize: false, available: false)
-    ]
-    capabilitiesViewOO.homeServer = "https://cloud.nextcloud.com/remote.php.dav/files/marino/"
-    return capabilitiesViewOO
+    return NCCapabilitiesView(capabilitiesStatus: getCapabilitiesViewOOForPreview())
 }
 }

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

@@ -31,7 +31,7 @@ extension UIAlertController {
     ///   - urlBase: UrlBase object
     ///   - urlBase: UrlBase object
     ///   - completion: If not` nil` it overrides the default behavior which shows an error using `NCContentPresenter`
     ///   - completion: If not` nil` it overrides the default behavior which shows an error using `NCContentPresenter`
     /// - Returns: The presentable alert controller
     /// - Returns: The presentable alert controller
-    static func createFolder(serverUrl: String, urlBase: NCUserBaseUrl, markE2ee: Bool = false, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController {
+    static func createFolder(serverUrl: String, urlBase: NCUserBaseUrl, 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 alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: nil, preferredStyle: .alert)
 
 
         let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
         let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
@@ -49,7 +49,7 @@ extension UIAlertController {
                     }
                     }
                 }
                 }
             } else {
             } else {
-                NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: serverUrl, account: urlBase.account, urlBase: urlBase.urlBase, userId: urlBase.userId, overwrite: false, withPush: true) { error in
+                NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: serverUrl, account: urlBase.account, urlBase: urlBase.urlBase, userId: urlBase.userId, overwrite: false, withPush: true, sceneIdentifier: sceneIdentifier) { error in
                     if let completion = completion {
                     if let completion = completion {
                         completion(error)
                         completion(error)
                     } else if error != .success {
                     } else if error != .success {

+ 28 - 0
iOSClient/Extensions/UIApplication+Extension.swift

@@ -0,0 +1,28 @@
+//
+//  UIApplication+Extension.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 25/03/24.
+//  Copyright © 2024 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+extension UIApplication {
+    var firstWindow: UIWindow? {
+        let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
+        let firstActiveScene = windowScenes.first
+        let keyWindow = firstActiveScene?.keyWindow
+        return keyWindow
+    }
+    func allSceneSessionDestructionExceptFirst() {
+        let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
+        let firstActiveScene = windowScenes.first
+        let options = UIWindowSceneDestructionRequestOptions()
+        options.windowDismissalAnimation = .standard
+        for windowScene in windowScenes {
+            if windowScene == firstActiveScene { continue }
+            requestSceneSessionDestruction(windowScene.session, options: options, errorHandler: nil)
+        }
+    }
+}

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

@@ -117,7 +117,7 @@ extension UIColor {
 
 
         guard let components = cgColor.components, components.count > 2 else {return false}
         guard let components = cgColor.components, components.count > 2 else {return false}
         let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
         let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
-        return (brightness > 0.95)
+        return (brightness > 0.90)
     }
     }
 
 
     @objc func isTooDark() -> Bool {
     @objc func isTooDark() -> Bool {
@@ -128,7 +128,7 @@ extension UIColor {
 
 
         guard let components = cgColor.components, components.count > 2 else {return false}
         guard let components = cgColor.components, components.count > 2 else {return false}
         let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
         let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
-        return (brightness < 0.05)
+        return (brightness < 0.10)
     }
     }
 
 
     func isLight(threshold: Float = 0.7) -> Bool {
     func isLight(threshold: Float = 0.7) -> Bool {

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

@@ -115,6 +115,10 @@ extension UIImage {
         return UIImage(cgImage: newCGImage, scale: 1, orientation: .up)
         return UIImage(cgImage: newCGImage, scale: 1, orientation: .up)
     }
     }
 
 
+    @objc func image(color: UIColor) -> UIImage {
+        return image(color: color, width: self.size.width, height: self.size.height)
+    }
+
     @objc func image(color: UIColor, size: CGFloat) -> UIImage {
     @objc func image(color: UIColor, size: CGFloat) -> UIImage {
         return image(color: color, width: size, height: size)
         return image(color: color, width: size, height: size)
     }
     }

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

@@ -32,13 +32,13 @@ extension UINavigationController {
 
 
     func setNavigationBarAppearance() {
     func setNavigationBarAppearance() {
 
 
-        navigationBar.tintColor = .systemBlue
+        navigationBar.tintColor = NCBrandColor.shared.iconImageColor
 
 
         let standardAppearance = UINavigationBarAppearance()
         let standardAppearance = UINavigationBarAppearance()
         standardAppearance.configureWithDefaultBackground()
         standardAppearance.configureWithDefaultBackground()
 
 
-        standardAppearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.label]
-        standardAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.label]
+        standardAppearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.textColor]
+        standardAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.textColor]
         navigationBar.standardAppearance = standardAppearance
         navigationBar.standardAppearance = standardAppearance
 
 
         let scrollEdgeAppearance = UINavigationBarAppearance()
         let scrollEdgeAppearance = UINavigationBarAppearance()
@@ -52,13 +52,13 @@ extension UINavigationController {
 
 
     func setGroupAppearance() {
     func setGroupAppearance() {
 
 
-        navigationBar.tintColor = .systemBlue
+        navigationBar.tintColor = NCBrandColor.shared.iconImageColor
 
 
         let standardAppearance = UINavigationBarAppearance()
         let standardAppearance = UINavigationBarAppearance()
         standardAppearance.configureWithDefaultBackground()
         standardAppearance.configureWithDefaultBackground()
 
 
-        standardAppearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.label]
-        standardAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.label]
+        standardAppearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.textColor]
+        standardAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.textColor]
         standardAppearance.backgroundColor = .systemGray6
         standardAppearance.backgroundColor = .systemGray6
         navigationBar.standardAppearance = standardAppearance
         navigationBar.standardAppearance = standardAppearance
 
 

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

@@ -37,4 +37,17 @@ extension View {
     func hiddenConditionally(isHidden: Bool) -> some View {
     func hiddenConditionally(isHidden: Bool) -> some View {
         isHidden ? AnyView(self.hidden()) : AnyView(self)
         isHidden ? AnyView(self.hidden()) : AnyView(self)
     }
     }
+
+    /// Applies the given transform if the given condition evaluates to `true`.
+    /// - Parameters:
+    ///   - condition: The condition to evaluate.
+    ///   - transform: The transform to apply to the source `View`.
+    /// - Returns: Either the original `View` or the modified `View` if the condition is `true`.
+    @ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
+        if condition {
+            transform(self)
+        } else {
+            self
+        }
+    }
 }
 }

+ 4 - 4
iOSClient/Favorites/NCFavorite.swift

@@ -33,7 +33,7 @@ class NCFavorite: NCCollectionViewCommon {
         layoutKey = NCGlobal.shared.layoutViewFavorite
         layoutKey = NCGlobal.shared.layoutViewFavorite
         enableSearchBar = false
         enableSearchBar = false
         headerRichWorkspaceDisable = true
         headerRichWorkspaceDisable = true
-        emptyImage = UIImage(named: "star.fill")?.image(color: NCBrandColor.shared.yellowFavorite, size: UIScreen.main.bounds.width)
+        emptyImage = utility.loadImage(named: "star.fill", colors: [NCBrandColor.shared.yellowFavorite])
         emptyTitle = "_favorite_no_files_"
         emptyTitle = "_favorite_no_files_"
         emptyDescription = "_tutorial_favorite_view_"
         emptyDescription = "_tutorial_favorite_view_"
     }
     }
@@ -73,7 +73,7 @@ class NCFavorite: NCCollectionViewCommon {
                                        searchResults: self.searchResults)
                                        searchResults: self.searchResults)
     }
     }
 
 
-    override func reloadDataSourceNetwork() {
+    override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
         super.reloadDataSourceNetwork()
         super.reloadDataSourceNetwork()
 
 
         NextcloudKit.shared.listingFavorites(showHiddenFiles: NCKeychain().showHiddenFiles, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { task in
         NextcloudKit.shared.listingFavorites(showHiddenFiles: NCKeychain().showHiddenFiles, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { task in
@@ -81,12 +81,12 @@ class NCFavorite: NCCollectionViewCommon {
             self.collectionView.reloadData()
             self.collectionView.reloadData()
         } completion: { account, files, _, error in
         } completion: { account, files, _, error in
             if error == .success {
             if error == .success {
-                NCManageDatabase.shared.convertFilesToMetadatas(files, useMetadataFolder: false) { _, _, metadatas in
+                NCManageDatabase.shared.convertFilesToMetadatas(files, useFirstAsMetadataFolder: false) { _, metadatas in
                     NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas)
                     NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas)
                     self.reloadDataSource()
                     self.reloadDataSource()
                 }
                 }
             } else {
             } else {
-                self.reloadDataSource(withQueryDB: false)
+                self.reloadDataSource(withQueryDB: withQueryDB)
             }
             }
         }
         }
     }
     }

+ 20 - 23
iOSClient/Files/NCFiles.swift

@@ -25,7 +25,6 @@ import UIKit
 import NextcloudKit
 import NextcloudKit
 
 
 class NCFiles: NCCollectionViewCommon {
 class NCFiles: NCCollectionViewCommon {
-
     internal var isRoot: Bool = true
     internal var isRoot: Bool = true
     internal var fileNameBlink: String?
     internal var fileNameBlink: String?
     internal var fileNameOpen: String?
     internal var fileNameOpen: String?
@@ -38,7 +37,7 @@ class NCFiles: NCCollectionViewCommon {
         enableSearchBar = true
         enableSearchBar = true
         headerRichWorkspaceDisable = false
         headerRichWorkspaceDisable = false
         headerMenuTransferView = true
         headerMenuTransferView = true
-        emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
+        emptyImage = NCImageCache.images.folder
         emptyTitle = "_files_no_files_"
         emptyTitle = "_files_no_files_"
         emptyDescription = "_no_file_pull_down_"
         emptyDescription = "_no_file_pull_down_"
     }
     }
@@ -54,8 +53,6 @@ class NCFiles: NCCollectionViewCommon {
                 self.navigationController?.popToRootViewController(animated: false)
                 self.navigationController?.popToRootViewController(animated: false)
 
 
                 self.serverUrl = self.utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
                 self.serverUrl = self.utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
-                self.appDelegate.activeServerUrl = self.serverUrl
-
                 self.isSearchingMode = false
                 self.isSearchingMode = false
                 self.isEditMode = false
                 self.isEditMode = false
                 self.selectOcId.removeAll()
                 self.selectOcId.removeAll()
@@ -78,7 +75,6 @@ class NCFiles: NCCollectionViewCommon {
     }
     }
 
 
     override func viewWillAppear(_ animated: Bool) {
     override func viewWillAppear(_ animated: Bool) {
-
         if isRoot {
         if isRoot {
             serverUrl = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
             serverUrl = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
             titleCurrentFolder = getNavigationTitle()
             titleCurrentFolder = getNavigationTitle()
@@ -86,9 +82,9 @@ class NCFiles: NCCollectionViewCommon {
         super.viewWillAppear(animated)
         super.viewWillAppear(animated)
 
 
         if dataSource.metadatas.isEmpty {
         if dataSource.metadatas.isEmpty {
-            reloadDataSource()
+            reloadDataSource(withQueryDB: true)
         }
         }
-        reloadDataSourceNetwork()
+        reloadDataSourceNetwork(withQueryDB: true)
     }
     }
 
 
     override func viewDidDisappear(_ animated: Bool) {
     override func viewDidDisappear(_ animated: Bool) {
@@ -139,7 +135,11 @@ class NCFiles: NCCollectionViewCommon {
         }
         }
     }
     }
 
 
-    override func reloadDataSourceNetwork() {
+    override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
+        if UIApplication.shared.applicationState == .background {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] Files not reload datasource network with the application in background")
+            return
+        }
         guard !isSearchingMode else {
         guard !isSearchingMode else {
             return networkSearch()
             return networkSearch()
         }
         }
@@ -172,49 +172,48 @@ class NCFiles: NCCollectionViewCommon {
                 if metadatasDifferentCount != 0 || metadatasModified != 0 {
                 if metadatasDifferentCount != 0 || metadatasModified != 0 {
                     self.reloadDataSource()
                     self.reloadDataSource()
                 } else {
                 } else {
-                    self.reloadDataSource(withQueryDB: false)
+                    self.reloadDataSource(withQueryDB: withQueryDB)
                 }
                 }
             } else {
             } else {
-                self.reloadDataSource(withQueryDB: false)
+                self.reloadDataSource(withQueryDB: withQueryDB)
             }
             }
         }
         }
     }
     }
 
 
     private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasDifferentCount: Int, _ metadatasModified: Int, _ error: NKError) -> Void) {
     private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasDifferentCount: Int, _ metadatasModified: Int, _ error: NKError) -> Void) {
-
         var tableDirectory: tableDirectory?
         var tableDirectory: tableDirectory?
 
 
         NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { task in
         NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { task in
             self.dataSourceTask = task
             self.dataSourceTask = task
             self.collectionView.reloadData()
             self.collectionView.reloadData()
-        } completion: { account, metadataFolder, error in
-            guard error == .success, let metadataFolder else {
+        } completion: { account, metadata, error in
+            guard error == .success, let metadata else {
                 return completion(nil, nil, 0, 0, error)
                 return completion(nil, nil, 0, 0, error)
             }
             }
-            tableDirectory = NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadataFolder.richWorkspace, account: account)
+            tableDirectory = NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadata.richWorkspace, account: account)
             // swiftlint:disable empty_string
             // swiftlint:disable empty_string
             let forceReplaceMetadatas = tableDirectory?.etag == ""
             let forceReplaceMetadatas = tableDirectory?.etag == ""
             // swiftlint:enable empty_string
             // swiftlint:enable empty_string
 
 
-            if tableDirectory?.etag != metadataFolder.etag || metadataFolder.e2eEncrypted {
+            if tableDirectory?.etag != metadata.etag || metadata.e2eEncrypted {
                 NCNetworking.shared.readFolder(serverUrl: self.serverUrl,
                 NCNetworking.shared.readFolder(serverUrl: self.serverUrl,
                                                account: self.appDelegate.account,
                                                account: self.appDelegate.account,
                                                forceReplaceMetadatas: forceReplaceMetadatas) { task in
                                                forceReplaceMetadatas: forceReplaceMetadatas) { task in
                     self.dataSourceTask = task
                     self.dataSourceTask = task
                     self.collectionView.reloadData()
                     self.collectionView.reloadData()
-                } completion: { _, metadataFolder, metadatas, metadatasDifferentCount, metadatasModified, error in
-                    guard error == .success else {
+                } completion: { account, metadataFolder, metadatas, metadatasDifferentCount, metadatasModified, error in
+                    guard account == self.appDelegate.account, error == .success else {
                         return completion(tableDirectory, nil, 0, 0, error)
                         return completion(tableDirectory, nil, 0, 0, error)
                     }
                     }
                     self.metadataFolder = metadataFolder
                     self.metadataFolder = metadataFolder
                     // E2EE
                     // E2EE
                     if let metadataFolder = metadataFolder,
                     if let metadataFolder = metadataFolder,
                        metadataFolder.e2eEncrypted,
                        metadataFolder.e2eEncrypted,
-                       NCKeychain().isEndToEndEnabled(account: self.appDelegate.account),
-                       !NCNetworkingE2EE().isInUpload(account: self.appDelegate.account, serverUrl: self.serverUrl) {
-                        let lock = NCManageDatabase.shared.getE2ETokenLock(account: self.appDelegate.account, serverUrl: self.serverUrl)
+                       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, version, e2eMetadata, signature, _, error in
                         NCNetworkingE2EE().getMetadata(fileId: metadataFolder.ocId, e2eToken: lock?.e2eToken) { account, version, e2eMetadata, signature, _, error in
-                            if error == .success, let e2eMetadata = e2eMetadata {
+                            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)
                                 let error = NCEndToEndMetadata().decodeMetadata(e2eMetadata, signature: signature, serverUrl: self.serverUrl, account: account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
                                 if error == .success {
                                 if error == .success {
                                     if version == "v1", NCGlobal.shared.capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
                                     if version == "v1", NCGlobal.shared.capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
@@ -262,7 +261,6 @@ class NCFiles: NCCollectionViewCommon {
     }
     }
 
 
     func blinkCell(fileName: String?) {
     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)) {
         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)
             let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
             if let indexPath = indexPath {
             if let indexPath = indexPath {
@@ -283,7 +281,6 @@ class NCFiles: NCCollectionViewCommon {
     }
     }
 
 
     func openFile(fileName: String?) {
     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)) {
         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)
             let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
             if let indexPath = indexPath {
             if let indexPath = indexPath {

+ 1 - 1
iOSClient/GUI/ComponentView.swift

@@ -51,7 +51,7 @@ struct ButtonRounded: ButtonStyle {
         configuration.label
         configuration.label
             .padding(.horizontal, 40)
             .padding(.horizontal, 40)
             .padding(.vertical, 10)
             .padding(.vertical, 10)
-            .background(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.brand))
+            .background(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.brandElement))
             .foregroundColor(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.brandText))
             .foregroundColor(disabled ? Color(UIColor.placeholderText) : Color(NCBrandColor.shared.brandText))
             .clipShape(Capsule())
             .clipShape(Capsule())
             .opacity(configuration.isPressed ? 0.5 : 1.0)
             .opacity(configuration.isPressed ? 0.5 : 1.0)

+ 3 - 16
iOSClient/GUI/HUDView.swift

@@ -22,7 +22,6 @@
 //
 //
 
 
 import SwiftUI
 import SwiftUI
-import PreviewSnapshots
 
 
 struct HUDView: View {
 struct HUDView: View {
 
 
@@ -55,7 +54,7 @@ struct Blur: UIViewRepresentable {
 
 
     func makeUIView(context: Context) -> UIVisualEffectView {
     func makeUIView(context: Context) -> UIVisualEffectView {
         let effectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
         let effectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
-        effectView.backgroundColor = NCBrandColor.shared.brand
+        effectView.backgroundColor = NCBrandColor.shared.brandElement
         return effectView
         return effectView
     }
     }
 
 
@@ -92,18 +91,6 @@ struct ContentView: View {
     }
     }
 }
 }
 
 
-struct HUDView_Previews: PreviewProvider {
-    static var previews: some View {
-        snapshots.previews.previewLayout(.sizeThatFits)
-    }
-
-    static var snapshots: PreviewSnapshots<String> {
-        PreviewSnapshots(
-            configurations: [
-                .init(name: NCGlobal.shared.defaultSnapshotConfiguration, state: "")
-            ],
-            configure: { _ in
-                ContentView().frameForPreview()
-            })
-    }
+#Preview {
+    ContentView()
 }
 }

+ 3 - 3
iOSClient/Groupfolders/NCGroupfolders.swift

@@ -33,7 +33,7 @@ class NCGroupfolders: NCCollectionViewCommon {
         layoutKey = NCGlobal.shared.layoutViewGroupfolders
         layoutKey = NCGlobal.shared.layoutViewGroupfolders
         enableSearchBar = false
         enableSearchBar = false
         headerRichWorkspaceDisable = true
         headerRichWorkspaceDisable = true
-        emptyImage = UIImage(named: "folder_group")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
+        emptyImage = utility.loadImage(named: "folder_group", colors: [NCBrandColor.shared.brandElement])
         emptyTitle = "_files_no_files_"
         emptyTitle = "_files_no_files_"
         emptyDescription = "_tutorial_groupfolders_view_"
         emptyDescription = "_tutorial_groupfolders_view_"
     }
     }
@@ -74,7 +74,7 @@ class NCGroupfolders: NCCollectionViewCommon {
             searchResults: self.searchResults)
             searchResults: self.searchResults)
     }
     }
 
 
-    override func reloadDataSourceNetwork() {
+    override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
         super.reloadDataSourceNetwork()
         super.reloadDataSourceNetwork()
 
 
         let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
         let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
@@ -102,7 +102,7 @@ class NCGroupfolders: NCCollectionViewCommon {
                     self.reloadDataSource()
                     self.reloadDataSource()
                 }
                 }
             } else {
             } else {
-                self.reloadDataSource(withQueryDB: false)
+                self.reloadDataSource(withQueryDB: withQueryDB)
             }
             }
         }
         }
     }
     }

+ 1 - 1
iOSClient/Images.xcassets/contact.imageset/Contents.json → iOSClient/Images.xcassets/InfoNetwork.imageset/Contents.json

@@ -1,7 +1,7 @@
 {
 {
   "images" : [
   "images" : [
     {
     {
-      "filename" : "contact.svg",
+      "filename" : "InfoNetwork.pdf",
       "idiom" : "universal"
       "idiom" : "universal"
     }
     }
   ],
   ],

+ 0 - 0
iOSClient/Images.xcassets/networkInProgress.imageset/networkInProgress.pdf → iOSClient/Images.xcassets/InfoNetwork.imageset/InfoNetwork.pdf


+ 0 - 26
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/Contents.json

@@ -1,26 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "MenuGroupByAlphabetic.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "filename" : "MenuGroupByAlphabetic@3x-1.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "MenuGroupByAlphabetic@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic.png


BIN
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic@3x-1.png


BIN
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic@3x.png


+ 0 - 26
iOSClient/Images.xcassets/MenuGroupByDate.imageset/Contents.json

@@ -1,26 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "MenuGroupByDate.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "filename" : "MenuGroupByDate@2x.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "MenuGroupByDate@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate.png


BIN
iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate@2x.png


BIN
iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate@3x.png


+ 0 - 26
iOSClient/Images.xcassets/MenuGroupByFile.imageset/Contents.json

@@ -1,26 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "MenuGroupByFile.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "filename" : "MenuGroupByFile@2x.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "MenuGroupByFile@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile.png


BIN
iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile@2x.png


BIN
iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile@3x.png


+ 0 - 26
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/Contents.json

@@ -1,26 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "MenuOrderByFileName.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "filename" : "MenuOrderByFileName@2x.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "MenuOrderByFileName@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName.png


BIN
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName@2x.png


BIN
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName@3x.png


+ 0 - 26
iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/Contents.json

@@ -1,26 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "MenuOrdeyByDate.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "filename" : "MenuOrdeyByDate@2x.png",
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "filename" : "MenuOrdeyByDate@3x.png",
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate.png


BIN
iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate@2x.png


BIN
iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate@3x.png


Some files were not shown because too many files changed in this diff