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>
|
@ -11,23 +11,22 @@ on:
|
|||
- master
|
||||
- develop
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: Build and Test
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
if: github.event.pull_request.draft == false
|
||||
env:
|
||||
PROJECT: Nextcloud.xcodeproj
|
||||
DESTINATION: platform=iOS Simulator,name=iPhone 14
|
||||
DESTINATION: platform=iOS Simulator,name=iPhone 15
|
||||
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
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore Carhage Cache
|
||||
uses: actions/cache@v3
|
||||
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
|
||||
- name: Build iOS Share
|
||||
run: |
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
|
||||
env:
|
||||
SCHEME: Share
|
||||
- name: Build iOS File Extension
|
||||
run: |
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
|
||||
env:
|
||||
SCHEME: File Provider Extension
|
||||
- name: Build iOS Notification Extension
|
||||
run: |
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
|
||||
env:
|
||||
SCHEME: Notification Service Extension
|
||||
- name: Build iOS Widget
|
||||
run: |
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
|
||||
env:
|
||||
SCHEME: Widget
|
||||
- name: Build iOS Widget Dashboard IntentHandler
|
||||
run: |
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcpretty
|
||||
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify --quieter
|
||||
env:
|
||||
SCHEME: WidgetDashboardIntentHandler
|
||||
|
|
|
@ -14,13 +14,17 @@ on:
|
|||
- master
|
||||
- develop
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
Lint:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: GitHub Action for SwiftLint
|
||||
uses: norio-nomura/action-swiftlint@3.2.1
|
||||
|
|
|
@ -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 }}
|
||||
|
|
@ -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"
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
coverage_service: cobertura_xml
|
||||
xcodeproj: Nextcloud.xcodeproj
|
||||
scheme: Nextcloud
|
|
@ -44,5 +44,6 @@ excluded:
|
|||
- Brand/NCBrand.swift
|
||||
- iOSClient/NCGlobal.swift
|
||||
- iOSClient/Utility/NCLivePhoto.swift
|
||||
|
||||
- DerivedData
|
||||
|
||||
reporter: "xcode"
|
||||
|
|
|
@ -26,4 +26,4 @@ import Foundation
|
|||
// Database Realm
|
||||
//
|
||||
let databaseName = "nextcloud.realm"
|
||||
let databaseSchemaVersion: UInt64 = 346
|
||||
let databaseSchemaVersion: UInt64 = 347
|
||||
|
|
|
@ -34,6 +34,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
|
|||
@IBOutlet weak var pageControl: UIPageControl!
|
||||
|
||||
@objc weak var delegate: NCIntroViewController?
|
||||
|
||||
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 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) {
|
||||
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() {
|
||||
|
@ -158,11 +162,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
|
|||
}
|
||||
|
||||
@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) {
|
||||
appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introSignup, openLoginWeb: false)
|
||||
appDelegate.openLogin(selector: NCGlobal.shared.introSignup, openLoginWeb: false)
|
||||
}
|
||||
|
||||
@IBAction func host(_ sender: Any) {
|
||||
|
|
|
@ -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 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 brandText: UIColor // don't touch me
|
||||
|
||||
|
@ -161,12 +161,37 @@ class NCBrandColor: NSObject {
|
|||
public var themingColorElement: 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 {
|
||||
get {
|
||||
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() {
|
||||
brand = customer
|
||||
brandElement = customer
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.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>
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.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>
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.it.twsweb.Crypto-Cloud</string>
|
||||
</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>
|
||||
</plist>
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.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>
|
||||
|
|
|
@ -4,11 +4,23 @@
|
|||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.nextcloud.apps</string>
|
||||
<string>group.it.twsweb.Crypto-Cloud</string>
|
||||
</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>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)it.twsweb.Crypto-Cloud</string>
|
||||
|
|
|
@ -104,8 +104,6 @@
|
|||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
|
@ -176,5 +174,22 @@
|
|||
</dict>
|
||||
</dict>
|
||||
</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>
|
||||
</plist>
|
||||
|
|
|
@ -32,7 +32,7 @@ class DocumentActionViewController: FPUIActionExtensionViewController {
|
|||
override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
view.backgroundColor = NCBrandColor.shared.brand
|
||||
view.backgroundColor = NCBrandColor.shared.brandElement
|
||||
titleError.textColor = NCBrandColor.shared.brandText
|
||||
cancelButton.setTitleColor(NCBrandColor.shared.brandText, for: .normal)
|
||||
|
||||
|
|
|
@ -93,7 +93,6 @@ class fileProviderData: NSObject {
|
|||
NCManageDatabase.shared.setCapabilities(account: account)
|
||||
|
||||
NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
|
||||
NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
|
||||
|
||||
return tableAccount.init(value: activeAccount)
|
||||
}
|
||||
|
@ -117,7 +116,6 @@ class fileProviderData: NSObject {
|
|||
NCManageDatabase.shared.setCapabilities(account: account)
|
||||
|
||||
NextcloudKit.shared.setup(account: activeAccount.account, user: activeAccount.user, userId: activeAccount.userId, password: NCKeychain().getPassword(account: activeAccount.account), urlBase: activeAccount.urlBase, userAgent: userAgent, nextcloudVersion: NCGlobal.shared.capabilityServerVersionMajor, delegate: NCNetworking.shared)
|
||||
NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate
|
||||
|
||||
return tableAccount.init(value: activeAccount)
|
||||
}
|
||||
|
|
|
@ -212,24 +212,20 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
|||
|
||||
if error == .success {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ import Alamofire
|
|||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------- */
|
||||
|
||||
class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
|
||||
class FileProviderExtension: NSFileProviderExtension {
|
||||
|
||||
var outstandingSessionTasks: [URL: URLSessionTask] = [:]
|
||||
var outstandingOcIdTemp: [String: String] = [:]
|
||||
|
|
3
Gemfile
|
@ -1,3 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
gem 'slather'
|
||||
gem 'xcpretty'
|
|
@ -21,7 +21,7 @@
|
|||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "NO"
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
|
@ -35,7 +35,7 @@
|
|||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "NO"
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
|
@ -49,7 +49,7 @@
|
|||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "NO"
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
|
@ -92,6 +92,26 @@
|
|||
ReferencedContainer = "container:Nextcloud.xcodeproj">
|
||||
</BuildableReference>
|
||||
</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>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
|
|
|
@ -63,7 +63,7 @@ class NCShareCell: UITableViewCell {
|
|||
let fileSize = utilityFileSystem.getFileSize(filePath: (NSTemporaryDirectory() + fileName))
|
||||
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) {
|
||||
|
|
|
@ -44,11 +44,11 @@ extension NCShareExtension: UICollectionViewDelegate {
|
|||
if kind == UICollectionView.elementKindSectionHeader {
|
||||
guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderEmptyData", for: indexPath) as? NCSectionHeaderEmptyData else { return NCSectionHeaderEmptyData() }
|
||||
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.emptyDescription.text = ""
|
||||
} 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.emptyDescription.text = ""
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ extension NCShareExtension: UICollectionViewDataSource {
|
|||
cell.indexPath = indexPath
|
||||
cell.fileUser = metadata.ownerId
|
||||
cell.labelTitle.text = metadata.fileNameView
|
||||
cell.labelTitle.textColor = .label
|
||||
cell.labelTitle.textColor = NCBrandColor.shared.textColor
|
||||
|
||||
cell.imageSelect.image = nil
|
||||
cell.imageStatus.image = nil
|
||||
|
|
|
@ -55,6 +55,8 @@ extension NCShareExtension: NCAccountRequestDelegate {
|
|||
self.present(popup, animated: true)
|
||||
}
|
||||
|
||||
func accountRequestAddAccount() { }
|
||||
|
||||
func accountRequestChangeAccount(account: String) {
|
||||
guard let activeAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) else {
|
||||
cancel(with: NCShareExtensionError.noAccount)
|
||||
|
|
|
@ -100,14 +100,13 @@ class NCShareExtension: UIViewController {
|
|||
commandViewHeightConstraint.constant = heightCommandView
|
||||
|
||||
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: "")
|
||||
let createFolderGesture = UITapGestureRecognizer(target: self, action: #selector(actionCreateFolder))
|
||||
createFolderView.addGestureRecognizer(createFolderGesture)
|
||||
|
||||
uploadView.layer.cornerRadius = 10
|
||||
|
||||
// uploadImage.image = utility).loadImage(named: "square.and.arrow.up", color: .label)
|
||||
uploadLabel.text = NSLocalizedString("_upload_", comment: "")
|
||||
uploadLabel.textColor = .systemBlue
|
||||
let uploadGesture = UITapGestureRecognizer(target: self, action: #selector(actionUpload))
|
||||
|
@ -137,6 +136,7 @@ class NCShareExtension: UIViewController {
|
|||
hud.indicatorView = JGProgressHUDRingIndicatorView()
|
||||
if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
|
||||
indicatorView.ringWidth = 1.5
|
||||
indicatorView.ringColor = NCBrandColor.shared.brandElement
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didCreateFolder(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil)
|
||||
|
@ -160,8 +160,10 @@ class NCShareExtension: UIViewController {
|
|||
self.filesName = fileNames
|
||||
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 {
|
||||
hud.indicatorView = JGProgressHUDSuccessIndicatorView()
|
||||
hud.indicatorView?.tintColor = NCBrandColor.shared.brandElement
|
||||
hud.textLabel.text = NSLocalizedString("_success_", comment: "")
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
self.extensionContext?.completeRequest(returningItems: self.extensionContext?.inputItems, completionHandler: nil)
|
||||
|
|
|
@ -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 }}"
|
||||
}
|
Двоичные данные
Sourcery/bin/sourcery
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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)"
|
||||
}
|
|
@ -20,17 +20,8 @@
|
|||
// 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 {
|
||||
return Int.random(in: 1000...Int.max)
|
||||
}
|
||||
|
|
|
@ -34,30 +34,30 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
|
|||
let expectation = expectation(description: "Should finish last callback")
|
||||
|
||||
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)"
|
||||
|
||||
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
|
||||
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.errorDescription, error.errorDescription)
|
||||
|
||||
Thread.sleep(forTimeInterval: 0.2)
|
||||
Thread.sleep(forTimeInterval: 1)
|
||||
|
||||
// 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.errorDescription, error.errorDescription)
|
||||
XCTAssertEqual(metadataFolder?.fileName, folderName)
|
||||
|
||||
|
||||
// Check Realm directory, should exist
|
||||
let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
|
||||
XCTAssertNotNil(directory)
|
||||
|
||||
Thread.sleep(forTimeInterval: 0.2)
|
||||
Thread.sleep(forTimeInterval: 1)
|
||||
|
||||
Task {
|
||||
// Test deleting folder
|
||||
|
@ -66,13 +66,14 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
|
|||
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
|
||||
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
|
||||
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() }
|
||||
|
||||
XCTAssertEqual(404, error.errorCode)
|
||||
XCTAssertEqual(0, error.errorCode)
|
||||
XCTAssertNil(metadataFolder?.fileName)
|
||||
|
||||
// Check Realm directory, should NOT exist
|
||||
|
@ -83,6 +84,6 @@ final class FilesIntegrationTests: BaseIntegrationXCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 100)
|
||||
waitForExpectations(timeout: TestConstants.timeoutLong)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -21,9 +21,7 @@
|
|||
|
||||
import XCTest
|
||||
|
||||
class BaseUIXCTestCase: XCTestCase {
|
||||
let timeoutSeconds: Double = 100
|
||||
|
||||
class BaseUIXCTestCase: BaseXCTestCase {
|
||||
override final class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
false
|
||||
}
|
||||
|
@ -31,13 +29,13 @@ class BaseUIXCTestCase: XCTestCase {
|
|||
internal func waitForEnabled(object: Any?) {
|
||||
let predicate = NSPredicate(format: "enabled == true")
|
||||
expectation(for: predicate, evaluatedWith: object, handler: nil)
|
||||
waitForExpectations(timeout: timeoutSeconds, handler: nil)
|
||||
waitForExpectations(timeout: TestConstants.timeoutLong, handler: nil)
|
||||
}
|
||||
|
||||
internal func waitForHittable(object: Any?) {
|
||||
let predicate = NSPredicate(format: "hittable == true")
|
||||
expectation(for: predicate, evaluatedWith: object, handler: nil)
|
||||
waitForExpectations(timeout: timeoutSeconds, handler: nil)
|
||||
waitForExpectations(timeout: TestConstants.timeoutLong, handler: nil)
|
||||
}
|
||||
|
||||
internal func waitForEnabledAndHittable(object: Any?) {
|
||||
|
|
|
@ -20,14 +20,10 @@
|
|||
//
|
||||
|
||||
import XCTest
|
||||
import NextcloudKit
|
||||
@testable import Nextcloud
|
||||
|
||||
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()
|
||||
|
||||
override func setUp() {
|
||||
|
@ -38,40 +34,42 @@ final class LoginUITests: BaseUIXCTestCase {
|
|||
app.launch()
|
||||
|
||||
let loginButton = app.buttons["Log in"]
|
||||
XCTAssert(loginButton.waitForExistence(timeout: timeoutSeconds))
|
||||
XCTAssert(loginButton.waitForExistence(timeout: TestConstants.timeoutLong))
|
||||
loginButton.tap()
|
||||
|
||||
let serverAddressHttpsTextField = app.textFields["Server address https:// …"]
|
||||
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()
|
||||
|
||||
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@*/
|
||||
XCTAssert(loginButton2.waitForExistence(timeout: timeoutSeconds))
|
||||
XCTAssert(loginButton2.waitForExistence(timeout: TestConstants.timeoutLong))
|
||||
waitForEnabledAndHittable(object: loginButton2)
|
||||
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.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.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@*/
|
||||
XCTAssert(loginButton3.waitForExistence(timeout: timeoutSeconds))
|
||||
XCTAssert(loginButton3.waitForExistence(timeout: TestConstants.timeoutLong))
|
||||
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@*/
|
||||
XCTAssert(grantAccessButton.waitForExistence(timeout: timeoutSeconds))
|
||||
XCTAssert(grantAccessButton.waitForExistence(timeout: TestConstants.timeoutLong))
|
||||
waitForEnabledAndHittable(object: grantAccessButton)
|
||||
grantAccessButton.tap()
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,13 +65,12 @@ let dashboardDatasTest: [DashboardData] = [
|
|||
]
|
||||
|
||||
func getDashboardItems(displaySize: CGSize, withButton: Bool) -> Int {
|
||||
|
||||
if withButton {
|
||||
let height = Int((displaySize.height - 85) / 50)
|
||||
return height
|
||||
let items = Int((displaySize.height - 90) / 55)
|
||||
return items
|
||||
} 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 {
|
||||
let fileNamePath: String = utilityFileSystem.directoryUserData + "/" + fileName + ".png"
|
||||
if let image = UIImage(contentsOfFile: fileNamePath) {
|
||||
imagetmp = image.withTintColor(.label, renderingMode: .alwaysOriginal)
|
||||
imagetmp = image.withTintColor(NCBrandColor.shared.iconImageColor, renderingMode: .alwaysOriginal)
|
||||
}
|
||||
}
|
||||
let titleImage = imagetmp
|
||||
|
@ -222,7 +221,7 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis
|
|||
let path = (urlComponents.path as NSString)
|
||||
let colorString = ((path.lastPathComponent) as NSString).deletingPathExtension
|
||||
imageColor = UIColor(hex: colorString)
|
||||
icon = UIImage(systemName: "circle.fill")!
|
||||
icon = utility.loadImage(named: "circle.fill")
|
||||
} else if let fileName = iconFileName {
|
||||
let fileNamePath: String = utilityFileSystem.directoryUserData + "/" + fileName + ".png"
|
||||
if FileManager().fileExists(atPath: fileNamePath), let image = UIImage(contentsOfFile: fileNamePath) {
|
||||
|
|
|
@ -37,6 +37,7 @@ struct DashboardWidgetView: View {
|
|||
Image(systemName: "checkmark")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.font(Font.system(.body).weight(.light))
|
||||
.frame(width: 50, height: 50)
|
||||
Text(NSLocalizedString("_no_items_", comment: ""))
|
||||
.font(.system(size: 25))
|
||||
|
@ -48,9 +49,7 @@ struct DashboardWidgetView: View {
|
|||
}
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
|
||||
HStack {
|
||||
|
||||
Image(uiImage: entry.titleImage)
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
|
@ -143,8 +142,10 @@ struct DashboardWidgetView: View {
|
|||
.padding(.leading, 10)
|
||||
.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) {
|
||||
|
||||
let brandColor = Color(NCBrandColor.shared.brand)
|
||||
let brandColor = Color(NCBrandColor.shared.brandElement)
|
||||
let brandTextColor = Color(NCBrandColor.shared.brandText)
|
||||
|
||||
ForEach(buttons, id: \.index) { element in
|
||||
|
@ -181,12 +182,13 @@ struct DashboardWidgetView: View {
|
|||
.resizable()
|
||||
.scaledToFit()
|
||||
.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)
|
||||
.font(.caption2)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
}
|
||||
.padding(.horizontal, 15.0)
|
||||
.frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
|
||||
|
|
|
@ -43,6 +43,7 @@ struct FilesData: Identifiable, Hashable {
|
|||
var title: String
|
||||
var subTitle: String
|
||||
var url: URL
|
||||
var useTypeIconFile: Bool = false
|
||||
}
|
||||
|
||||
let filesDatasTest: [FilesData] = [
|
||||
|
@ -79,9 +80,8 @@ func getTitleFilesWidget(account: tableAccount?) -> String {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -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: "")))
|
||||
}
|
||||
|
||||
@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
|
||||
let password = NCKeychain().getPassword(account: account.account)
|
||||
NextcloudKit.shared.setup(
|
||||
|
@ -181,7 +163,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
|
|||
</d:order>
|
||||
</d:orderby>
|
||||
<d:limit>
|
||||
<d:nresults>50</d:nresults>
|
||||
<d:nresults>25</d:nresults>
|
||||
</d:limit>
|
||||
</d:basicsearch>
|
||||
</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)
|
||||
}
|
||||
|
||||
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
|
||||
Task {
|
||||
var datas: [FilesData] = []
|
||||
var imageRecent = UIImage(named: "file")!
|
||||
let title = getTitleFilesWidget(account: account)
|
||||
let files = files.sorted(by: { ($0.date as Date) > ($1.date as Date) })
|
||||
|
||||
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
|
||||
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 }
|
||||
|
||||
// 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 {
|
||||
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 fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(file.ocId, etag: file.etag)
|
||||
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)
|
||||
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 metadata = NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
|
||||
|
||||
// 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)
|
||||
if datas.count == filesItems { break}
|
||||
}
|
||||
|
|
|
@ -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)!
|
||||
|
||||
GeometryReader { geo in
|
||||
|
||||
if entry.isEmpty {
|
||||
VStack(alignment: .center) {
|
||||
Image(systemName: "checkmark")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.font(Font.system(.body).weight(.light))
|
||||
.frame(width: 50, height: 50)
|
||||
Text(NSLocalizedString("_no_items_", comment: ""))
|
||||
.font(.system(size: 25))
|
||||
|
@ -57,7 +57,6 @@ struct FilesWidgetView: View {
|
|||
ZStack(alignment: .topLeading) {
|
||||
|
||||
HStack {
|
||||
|
||||
Text(entry.tile)
|
||||
.font(.system(size: 12))
|
||||
.fontWeight(.bold)
|
||||
|
@ -70,39 +69,43 @@ struct FilesWidgetView: View {
|
|||
|
||||
if !entry.isEmpty {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
VStack(spacing: 0) {
|
||||
|
||||
ForEach(entry.datas, id: \.id) { element in
|
||||
|
||||
Link(destination: element.url) {
|
||||
|
||||
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) {
|
||||
|
||||
Text(element.title)
|
||||
.font(.system(size: 12))
|
||||
.fontWeight(.regular)
|
||||
|
||||
Text(element.subTitle)
|
||||
.font(.system(size: CGFloat(10)))
|
||||
.foregroundColor(Color(.systemGray))
|
||||
.foregroundColor(Color(NCBrandColor.shared.iconImageColor2))
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.leading, 10)
|
||||
.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) {
|
||||
|
||||
let sizeButton: CGFloat = 40
|
||||
|
||||
Link(destination: entry.isPlaceholder ? linkNoAction : linkActionUploadAsset, label: {
|
||||
|
@ -120,7 +122,7 @@ struct FilesWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding(11)
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
|
@ -132,9 +134,10 @@ struct FilesWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding(11)
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.font(Font.system(.body).weight(.light))
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
})
|
||||
|
||||
|
@ -144,7 +147,7 @@ struct FilesWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding(11)
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
|
@ -156,27 +159,27 @@ struct FilesWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding(11)
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.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 : [])
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: entry.footerImage)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.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)
|
||||
.font(.caption2)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
}
|
||||
.padding(.horizontal, 15.0)
|
||||
.frame(maxWidth: geo.size.width, maxHeight: geo.size.height - 2, alignment: .bottomTrailing)
|
||||
|
|
|
@ -39,7 +39,7 @@ struct LockscreenWidgetView: View {
|
|||
label: {},
|
||||
currentValueLabel: {
|
||||
Image(systemName: "xmark.icloud")
|
||||
.font(.system(size: 25.0))
|
||||
.font(.system(size: 25.0).weight(.light))
|
||||
}
|
||||
)
|
||||
.gaugeStyle(.accessoryCircularCapacity)
|
||||
|
@ -66,17 +66,17 @@ struct LockscreenWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.foregroundColor(.gray)
|
||||
.foregroundColor(Color(NCBrandColor.shared.textColor2))
|
||||
.frame(width: 11, height: 11)
|
||||
Text(NSLocalizedString("_recent_activity_", comment: ""))
|
||||
.font(.system(size: 11))
|
||||
.fontWeight(.heavy)
|
||||
.foregroundColor(.gray)
|
||||
.foregroundColor(Color(NCBrandColor.shared.textColor2))
|
||||
}
|
||||
if entry.error {
|
||||
VStack(spacing: 1) {
|
||||
Image(systemName: "xmark.icloud")
|
||||
.font(.system(size: 25.0))
|
||||
.font(Font.system(size: 25.0).weight(.light))
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
}.padding(8)
|
||||
} else {
|
||||
|
|
|
@ -51,7 +51,7 @@ struct ToolbarWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding()
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
|
@ -61,9 +61,10 @@ struct ToolbarWidgetView: View {
|
|||
Image(systemName: "doc.text.viewfinder")
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.font(Font.system(.body).weight(.light))
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding()
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
|
@ -75,7 +76,7 @@ struct ToolbarWidgetView: View {
|
|||
.renderingMode(.template)
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding()
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
|
@ -86,7 +87,7 @@ struct ToolbarWidgetView: View {
|
|||
.resizable()
|
||||
.foregroundColor(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandText))
|
||||
.padding()
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brand))
|
||||
.background(entry.isPlaceholder ? Color(.systemGray4) : Color(NCBrandColor.shared.brandElement))
|
||||
.clipShape(Circle())
|
||||
.scaledToFit()
|
||||
.frame(width: geo.size.width / 4, height: sizeButton)
|
||||
|
@ -96,17 +97,17 @@ struct ToolbarWidgetView: View {
|
|||
.redacted(reason: entry.isPlaceholder ? .placeholder : [])
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: entry.footerImage)
|
||||
.resizable()
|
||||
.font(Font.system(.body).weight(.light))
|
||||
.scaledToFit()
|
||||
.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)
|
||||
.font(.caption2)
|
||||
.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)
|
||||
}
|
||||
|
|
|
@ -47,7 +47,9 @@ struct DashboardWidget: Widget {
|
|||
.supportedFamilies([.systemLarge])
|
||||
.configurationDisplayName("Dashboard")
|
||||
.description(NSLocalizedString("_description_dashboardwidget_", comment: ""))
|
||||
#if !targetEnvironment(simulator)
|
||||
.contentMarginsDisabled()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +63,9 @@ struct FilesWidget: Widget {
|
|||
.supportedFamilies([.systemLarge])
|
||||
.configurationDisplayName("Files")
|
||||
.description(NSLocalizedString("_description_fileswidget_", comment: ""))
|
||||
#if !targetEnvironment(simulator)
|
||||
.contentMarginsDisabled()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +79,9 @@ struct ToolbarWidget: Widget {
|
|||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName("Toolbar")
|
||||
.description(NSLocalizedString("_description_toolbarwidget_", comment: ""))
|
||||
#if !targetEnvironment(simulator)
|
||||
.contentMarginsDisabled()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +96,9 @@ struct LockscreenWidget: Widget {
|
|||
.supportedFamilies([.accessoryRectangular, .accessoryCircular])
|
||||
.configurationDisplayName(NSLocalizedString("_title_lockscreenwidget_", comment: ""))
|
||||
.description(NSLocalizedString("_description_lockscreenwidget_", comment: ""))
|
||||
#if !targetEnvironment(simulator)
|
||||
.contentMarginsDisabled()
|
||||
#endif
|
||||
} else {
|
||||
return EmptyWidgetConfiguration()
|
||||
}
|
||||
|
@ -99,6 +107,7 @@ struct LockscreenWidget: Widget {
|
|||
|
||||
extension View {
|
||||
func widgetBackground(_ backgroundView: some View) -> some View {
|
||||
#if !targetEnvironment(simulator)
|
||||
if #available(iOSApplicationExtension 17.0, *) {
|
||||
return containerBackground(for: .widget) {
|
||||
backgroundView
|
||||
|
@ -106,5 +115,9 @@ extension View {
|
|||
} else {
|
||||
return background(backgroundView)
|
||||
}
|
||||
#else
|
||||
return background(backgroundView)
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
#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
|
||||
|
|
|
@ -29,14 +29,7 @@ public protocol NCAccountRequestDelegate: AnyObject {
|
|||
func accountRequestChangeAccount(account: String)
|
||||
}
|
||||
|
||||
// optional func
|
||||
public extension NCAccountRequestDelegate {
|
||||
func accountRequestAddAccount() {}
|
||||
func accountRequestChangeAccount(account: String) {}
|
||||
}
|
||||
|
||||
class NCAccountRequest: UIViewController {
|
||||
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
@IBOutlet weak var progressView: UIProgressView!
|
||||
|
@ -73,8 +66,8 @@ class NCAccountRequest: UIViewController {
|
|||
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) {
|
||||
|
@ -97,7 +90,6 @@ class NCAccountRequest: UIViewController {
|
|||
// MARK: - NotificationCenter
|
||||
|
||||
@objc func applicationDidEnterBackground() {
|
||||
|
||||
if dismissDidEnterBackground {
|
||||
dismiss(animated: false)
|
||||
}
|
||||
|
@ -106,19 +98,17 @@ class NCAccountRequest: UIViewController {
|
|||
// MARK: - Progress
|
||||
|
||||
@objc func startTimer() {
|
||||
|
||||
if enableTimerProgress {
|
||||
time = 0
|
||||
timer?.invalidate()
|
||||
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
|
||||
progressView.isHidden = false
|
||||
progressView?.isHidden = false
|
||||
} else {
|
||||
progressView.isHidden = true
|
||||
progressView?.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateProgress() {
|
||||
|
||||
time += 0.1
|
||||
if time >= secondsAutoDismiss {
|
||||
dismiss(animated: true)
|
||||
|
@ -129,9 +119,7 @@ class NCAccountRequest: UIViewController {
|
|||
}
|
||||
|
||||
extension NCAccountRequest: UITableViewDelegate {
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
|
||||
timer?.invalidate()
|
||||
progressView.progress = 0
|
||||
}
|
||||
|
@ -141,14 +129,10 @@ extension NCAccountRequest: UITableViewDelegate {
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
|
||||
if indexPath.row == accounts.count {
|
||||
|
||||
dismiss(animated: true)
|
||||
delegate?.accountRequestAddAccount()
|
||||
|
||||
} else {
|
||||
|
||||
let account = accounts[indexPath.row]
|
||||
if account.account != activeAccount?.account {
|
||||
dismiss(animated: true) {
|
||||
|
@ -162,7 +146,6 @@ extension NCAccountRequest: UITableViewDelegate {
|
|||
}
|
||||
|
||||
extension NCAccountRequest: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if enableAddAccount {
|
||||
return accounts.count + 1
|
||||
|
@ -172,10 +155,8 @@ extension NCAccountRequest: UITableViewDataSource {
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
cell.backgroundColor = tableView.backgroundColor
|
||||
|
||||
let avatarImage = cell.viewWithTag(10) as? UIImageView
|
||||
let userLabel = cell.viewWithTag(20) as? UILabel
|
||||
let urlLabel = cell.viewWithTag(30) as? UILabel
|
||||
|
@ -186,7 +167,7 @@ extension NCAccountRequest: UITableViewDataSource {
|
|||
|
||||
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
|
||||
userLabel?.text = NSLocalizedString("_add_account_", comment: "")
|
||||
userLabel?.textColor = .systemBlue
|
||||
|
@ -209,7 +190,7 @@ extension NCAccountRequest: UITableViewDataSource {
|
|||
}
|
||||
|
||||
if account.active {
|
||||
activeImage?.image = utility.loadImage(named: "checkmark").image(color: .systemBlue, size: 30)
|
||||
activeImage?.image = utility.loadImage(named: "checkmark", colors: [.systemBlue])
|
||||
} else {
|
||||
activeImage?.image = nil
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
|
|||
let utility = NCUtility()
|
||||
var allItems: [DateCompareable] = []
|
||||
var sectionDates: [Date] = []
|
||||
var dataSourceTask: URLSessionTask?
|
||||
|
||||
var insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
||||
var didSelectItemEnable: Bool = true
|
||||
|
@ -102,9 +103,12 @@ class NCActivity: UIViewController, NCSharePagingContent {
|
|||
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() {
|
||||
|
@ -123,7 +127,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
|
|||
|
||||
let label = UILabel()
|
||||
label.font = UIFont.systemFont(ofSize: 15)
|
||||
label.textColor = UIColor.systemGray
|
||||
label.textColor = NCBrandColor.shared.textColor2
|
||||
label.textAlignment = .center
|
||||
label.text = NSLocalizedString("_no_activity_footer_", comment: "")
|
||||
label.frame = CGRect(x: 0, y: 10, width: tableView.frame.width, height: 60)
|
||||
|
@ -156,7 +160,7 @@ extension NCActivity: UITableViewDelegate {
|
|||
|
||||
let label = UILabel()
|
||||
label.font = UIFont.boldSystemFont(ofSize: 13)
|
||||
label.textColor = .label
|
||||
label.textColor = NCBrandColor.shared.textColor
|
||||
label.text = utility.getTitleFromDate(sectionDates[section])
|
||||
label.textAlignment = .center
|
||||
label.layer.cornerRadius = 11
|
||||
|
@ -209,16 +213,16 @@ extension NCActivity: UITableViewDataSource {
|
|||
|
||||
// Image
|
||||
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
|
||||
cell.labelUser.text = comment.actorDisplayName
|
||||
cell.labelUser.textColor = .label
|
||||
cell.labelUser.textColor = NCBrandColor.shared.textColor
|
||||
// Date
|
||||
cell.labelDate.text = utility.dateDiff(comment.creationDateTime as Date)
|
||||
cell.labelDate.textColor = .systemGray4
|
||||
// Message
|
||||
cell.labelMessage.text = comment.message
|
||||
cell.labelMessage.textColor = .label
|
||||
cell.labelMessage.textColor = NCBrandColor.shared.textColor
|
||||
// Button Menu
|
||||
if comment.actorId == appDelegate.userId {
|
||||
cell.buttonMenu.isHidden = false
|
||||
|
@ -242,7 +246,7 @@ extension NCActivity: UITableViewDataSource {
|
|||
cell.avatar.isHidden = true
|
||||
cell.subjectTrailingConstraint.constant = 10
|
||||
cell.didSelectItemEnable = self.didSelectItemEnable
|
||||
cell.subject.textColor = .label
|
||||
cell.subject.textColor = NCBrandColor.shared.textColor
|
||||
cell.viewController = self
|
||||
|
||||
// icon
|
||||
|
@ -276,7 +280,7 @@ extension NCActivity: UITableViewDataSource {
|
|||
|
||||
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
|
||||
|
@ -426,7 +430,9 @@ extension NCActivity {
|
|||
limit: 1,
|
||||
objectId: nil,
|
||||
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() }
|
||||
|
||||
let largestActivityId = max(activityFirstKnown, activityLastGiven)
|
||||
|
@ -453,7 +459,9 @@ extension NCActivity {
|
|||
limit: min(limit, 200),
|
||||
objectId: metadata?.fileId,
|
||||
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() }
|
||||
guard error == .success,
|
||||
account == self.appDelegate.account,
|
||||
|
@ -494,7 +502,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
|
|||
actions.append(
|
||||
NCMenuAction(
|
||||
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
|
||||
guard let metadata = self.metadata, let tableComments = tableComments else { return }
|
||||
|
||||
|
@ -525,7 +533,8 @@ extension NCActivity: NCShareCommentsCellDelegate {
|
|||
actions.append(
|
||||
NCMenuAction(
|
||||
title: NSLocalizedString("_delete_comment_", comment: ""),
|
||||
icon: utility.loadImage(named: "trash"),
|
||||
destructive: true,
|
||||
icon: utility.loadImage(named: "trash", colors: [.red]),
|
||||
action: { _ in
|
||||
guard let metadata = self.metadata, let tableComments = tableComments else { return }
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
|
|||
if let image = UIImage(contentsOfFile: fileNameLocalPath) {
|
||||
imageItem.image = image
|
||||
} else {
|
||||
imageItem.image = UIImage(named: "avatar")
|
||||
imageItem.image = NCUtility().loadImage(named: "person.crop.circle", colors: [NCBrandColor.shared.iconImageColor])
|
||||
}
|
||||
|
||||
if account.displayName.isEmpty {
|
||||
|
@ -48,7 +48,7 @@ class NCActivityCommentView: UIView, UITextFieldDelegate {
|
|||
} else {
|
||||
labelUser.text = account.displayName
|
||||
}
|
||||
labelUser.textColor = .label
|
||||
labelUser.textColor = NCBrandColor.shared.textColor
|
||||
}
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
|
|
|
@ -166,7 +166,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
|
|||
if let imageNamePath = imageNamePath, id == self.idActivity, let image = UIImage(contentsOfFile: imageNamePath) {
|
||||
cell.imageView.image = image
|
||||
} else {
|
||||
cell.imageView.image = UIImage(named: "file")
|
||||
cell.imageView.image = NCImageCache.images.file
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,11 +176,11 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
|
|||
|
||||
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) {
|
||||
cell.imageView.image = image
|
||||
} 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) {
|
||||
cell.imageView.image = image
|
||||
cell.imageView?.contentMode = .scaleAspectFill
|
||||
} 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
|
||||
if !FileManager.default.fileExists(atPath: fileNamePath) {
|
||||
if NCNetworking.shared.downloadThumbnailActivityQueue.operations.filter({ ($0 as? NCOperationDownloadThumbnailActivity)?.fileId == fileId }).isEmpty {
|
||||
|
|
|
@ -24,41 +24,32 @@
|
|||
import UIKit
|
||||
import BackgroundTasks
|
||||
import NextcloudKit
|
||||
import TOPasscodeViewController
|
||||
import LocalAuthentication
|
||||
import Firebase
|
||||
import WidgetKit
|
||||
import Queuer
|
||||
import EasyTipView
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, NCUserBaseUrl {
|
||||
|
||||
var backgroundSessionCompletionHandler: (() -> Void)?
|
||||
var window: UIWindow?
|
||||
|
||||
@objc var account: String = ""
|
||||
@objc var urlBase: String = ""
|
||||
@objc var user: String = ""
|
||||
@objc var userId: String = ""
|
||||
@objc var password: String = ""
|
||||
|
||||
var tipView: EasyTipView?
|
||||
var backgroundSessionCompletionHandler: (() -> Void)?
|
||||
var activeLogin: NCLogin?
|
||||
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 isAppRefresh: Bool = false
|
||||
var isProcessingTask: Bool = false
|
||||
|
||||
var timerErrorNetworkingDisabled: Bool = false
|
||||
var taskAutoUploadDate: Date = Date()
|
||||
var isUiTestingEnabled: Bool {
|
||||
return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
|
||||
}
|
||||
var notificationSettings: UNNotificationSettings?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
if isUiTestingEnabled {
|
||||
|
@ -87,8 +78,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
NextcloudKit.shared.setup(delegate: NCNetworking.shared)
|
||||
NextcloudKit.shared.setup(userAgent: userAgent)
|
||||
|
||||
startTimerErrorNetworking()
|
||||
|
||||
var levelLog = 0
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Account active \(activeAccount.account)")
|
||||
if NCKeychain().getPassword(account: activeAccount.account).isEmpty {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] PASSWORD NOT FOUND for \(activeAccount.account)")
|
||||
}
|
||||
|
||||
account = activeAccount.account
|
||||
urlBase = activeAccount.urlBase
|
||||
|
@ -140,6 +126,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
NCImageCache.shared.createImagesCache()
|
||||
|
||||
// Push Notification & display notification
|
||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||
self.notificationSettings = settings
|
||||
}
|
||||
application.registerForRemoteNotifications()
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
|
||||
|
@ -158,127 +147,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
if UIApplication.shared.backgroundRefreshStatus == .available {
|
||||
|
||||
if self.notificationSettings?.authorizationStatus != .denied && UIApplication.shared.backgroundRefreshStatus == .available {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NCBrandOptions.shared.brand
|
||||
content.body = NSLocalizedString("_keep_running_", comment: "")
|
||||
|
@ -290,11 +163,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
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
|
||||
|
||||
/*
|
||||
@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() {
|
||||
|
||||
|
@ -302,7 +188,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds.
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(request)
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Refresh task: ok")
|
||||
} catch {
|
||||
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.
|
||||
< MAX over 1 minute >
|
||||
*/
|
||||
func scheduleAppProcessing() {
|
||||
|
||||
|
@ -320,7 +204,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
request.requiresExternalPower = false
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(request)
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Processing task: ok")
|
||||
} catch {
|
||||
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) {
|
||||
scheduleAppRefresh()
|
||||
|
||||
if isProcessingTask {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] ProcessingTask already in progress, abort.")
|
||||
return task.setTaskCompleted(success: true)
|
||||
}
|
||||
isAppRefresh = true
|
||||
|
||||
handleAppRefreshProcessingTask(taskText: "AppRefresh") {
|
||||
task.setTaskCompleted(success: true)
|
||||
self.isAppRefresh = false
|
||||
}
|
||||
}
|
||||
|
||||
func handleProcessingTask(_ task: BGTask) {
|
||||
scheduleAppProcessing()
|
||||
|
||||
if isAppRefresh {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] AppRefresh already in progress, abort.")
|
||||
return task.setTaskCompleted(success: true)
|
||||
}
|
||||
isProcessingTask = true
|
||||
|
||||
handleAppRefreshProcessingTask(taskText: "ProcessingTask") {
|
||||
task.setTaskCompleted(success: true)
|
||||
self.isProcessingTask = false
|
||||
}
|
||||
}
|
||||
|
||||
func handleAppRefreshProcessingTask(taskText: String, completion: @escaping () -> Void = {}) {
|
||||
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",
|
||||
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) {
|
||||
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() {
|
||||
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
|
||||
}
|
||||
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
|
||||
UIApplication.shared.applicationIconBadgeNumber = counter
|
||||
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) completion handle")
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) completion handle")
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +271,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
|
||||
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
|
||||
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start handle Events For Background URLSession: \(identifier)")
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] Start handle Events For Background URLSession: \(identifier)")
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
backgroundSessionCompletionHandler = completionHandler
|
||||
}
|
||||
|
@ -413,7 +294,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
}
|
||||
|
||||
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 {
|
||||
NCPushNotification.shared().registerForRemoteNotifications(withDeviceToken: deviceToken)
|
||||
}
|
||||
|
@ -446,35 +327,51 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
let navigationController = UINavigationController(rootViewController: viewController)
|
||||
navigationController.modalPresentationStyle = .fullScreen
|
||||
self.window?.rootViewController?.present(navigationController, animated: true)
|
||||
UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true)
|
||||
}
|
||||
} else if !findAccount {
|
||||
let message = NSLocalizedString("_the_account_", comment: "") + " " + accountPush + " " + NSLocalizedString("_does_not_exist_", comment: "")
|
||||
let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
|
||||
self.window?.rootViewController?.present(alertController, animated: true, completion: { })
|
||||
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]
|
||||
if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig {
|
||||
|
||||
if activeLoginWeb?.view.window == nil {
|
||||
activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
|
||||
activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
|
||||
showLoginViewController(activeLoginWeb, contextViewController: viewController)
|
||||
showLoginViewController(activeLoginWeb)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Nextcloud standard login
|
||||
if selector == NCGlobal.shared.introSignup {
|
||||
|
||||
if activeLoginWeb?.view.window == nil {
|
||||
activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
|
||||
if selector == NCGlobal.shared.introSignup {
|
||||
|
@ -482,72 +379,45 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
} else {
|
||||
activeLoginWeb?.urlBase = self.urlBase
|
||||
}
|
||||
showLoginViewController(activeLoginWeb, contextViewController: viewController)
|
||||
showLoginViewController(activeLoginWeb)
|
||||
}
|
||||
|
||||
} else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url {
|
||||
|
||||
if activeLoginWeb?.view.window == nil {
|
||||
activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
|
||||
activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
|
||||
showLoginViewController(activeLoginWeb, contextViewController: viewController)
|
||||
showLoginViewController(activeLoginWeb)
|
||||
}
|
||||
|
||||
} else if openLoginWeb {
|
||||
|
||||
// Used also for reinsert the account (change passwd)
|
||||
if activeLoginWeb?.view.window == nil {
|
||||
activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
|
||||
activeLoginWeb?.urlBase = urlBase
|
||||
activeLoginWeb?.user = user
|
||||
showLoginViewController(activeLoginWeb, contextViewController: viewController)
|
||||
showLoginViewController(activeLoginWeb)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if activeLogin?.view.window == nil {
|
||||
activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
|
||||
showLoginViewController(activeLogin, contextViewController: viewController)
|
||||
showLoginViewController(activeLogin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showLoginViewController(_ viewController: UIViewController?, contextViewController: UIViewController?) {
|
||||
// MARK: - Error Networking
|
||||
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
@objc func startTimerErrorNetworking(scene: UIScene) {
|
||||
timerErrorNetworkingDisabled = false
|
||||
timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking(_:)), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
@objc func startTimerErrorNetworking() {
|
||||
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) {
|
||||
|
@ -578,11 +448,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
let viewController = navigationController.topViewController as? NCViewCertificateDetails {
|
||||
viewController.delegate = self
|
||||
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
|
||||
|
@ -632,6 +502,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
|
||||
@objc func deleteAccount(_ account: String, wipe: Bool) {
|
||||
|
||||
UIApplication.shared.allSceneSessionDestructionExceptFirst()
|
||||
|
||||
if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
|
||||
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)
|
||||
}
|
||||
} 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()
|
||||
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 {
|
||||
func viewCertificateDetailsDismiss(host: String) {
|
||||
|
@ -901,45 +607,3 @@ extension AppDelegate: NCCreateFormUploadConflictDelegate {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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_")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
|
|||
var recording: NCAudioRecorder!
|
||||
var startDate: Date = Date()
|
||||
var fileName: String = ""
|
||||
var serverUrl = ""
|
||||
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
|
||||
|
||||
@IBOutlet weak var contentContainerView: UIView!
|
||||
|
@ -54,7 +55,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
|
|||
voiceRecordHUD.fillColor = UIColor.green
|
||||
|
||||
Task {
|
||||
self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.appDelegate.account, serverUrl: self.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.delegate = self
|
||||
do {
|
||||
|
@ -96,7 +97,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
|
|||
|
||||
func uploadMetadata() {
|
||||
let fileNamePath = NSTemporaryDirectory() + self.fileName
|
||||
let metadata = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: UUID().uuidString, serverUrl: 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.sessionSelector = NCGlobal.shared.selectorUploadFile
|
||||
metadata.status = NCGlobal.shared.metadataStatusWaitUpload
|
||||
|
|
|
@ -55,7 +55,7 @@ class NCBrowserWeb: UIViewController {
|
|||
buttonExit.isHidden = true
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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: "")
|
||||
|
||||
orangeButton.backgroundColor = .orange
|
||||
|
|
|
@ -351,17 +351,13 @@ extension NCManageDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
@objc func setAccountAutoUploadFileName(_ fileName: String?) {
|
||||
@objc func setAccountAutoUploadFileName(_ fileName: String) {
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
try realm.write {
|
||||
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 {
|
||||
|
|
|
@ -26,7 +26,6 @@ import RealmSwift
|
|||
import NextcloudKit
|
||||
|
||||
class tableAvatar: Object {
|
||||
|
||||
@objc dynamic var date = NSDate()
|
||||
@objc dynamic var etag = ""
|
||||
@objc dynamic var fileName = ""
|
||||
|
@ -38,9 +37,7 @@ class tableAvatar: Object {
|
|||
}
|
||||
|
||||
extension NCManageDatabase {
|
||||
|
||||
func addAvatar(fileName: String, etag: String) {
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
try realm.write {
|
||||
|
@ -57,7 +54,6 @@ extension NCManageDatabase {
|
|||
}
|
||||
|
||||
func getTableAvatar(fileName: String) -> tableAvatar? {
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
|
@ -71,7 +67,6 @@ extension NCManageDatabase {
|
|||
}
|
||||
|
||||
func clearAllAvatarLoaded() {
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
try realm.write {
|
||||
|
@ -88,7 +83,6 @@ extension NCManageDatabase {
|
|||
|
||||
@discardableResult
|
||||
func setAvatarLoaded(fileName: String) -> UIImage? {
|
||||
|
||||
let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
|
||||
var image: UIImage?
|
||||
|
||||
|
@ -112,7 +106,6 @@ extension NCManageDatabase {
|
|||
}
|
||||
|
||||
func getImageAvatarLoaded(fileName: String) -> UIImage? {
|
||||
|
||||
let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
|
||||
|
||||
do {
|
||||
|
|
|
@ -103,6 +103,7 @@ extension NCManageDatabase {
|
|||
let external: External?
|
||||
let groupfolders: GroupFolders?
|
||||
let securityguard: SecurityGuard?
|
||||
let assistant: Assistant?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case filessharing = "files_sharing"
|
||||
|
@ -112,6 +113,7 @@ extension NCManageDatabase {
|
|||
case userstatus = "user_status"
|
||||
case external, groupfolders
|
||||
case securityguard = "security_guard"
|
||||
case assistant
|
||||
}
|
||||
|
||||
struct FilesSharing: Codable {
|
||||
|
@ -269,6 +271,11 @@ extension NCManageDatabase {
|
|||
struct SecurityGuard: Codable {
|
||||
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.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 {
|
||||
for mimetype in mimetypes {
|
||||
global.capabilityRichdocumentsMimetypes.append(mimetype)
|
||||
global.capabilityRichDocumentsMimetypes.append(mimetype)
|
||||
}
|
||||
}
|
||||
|
||||
global.capabilityAssistantEnabled = data.capabilities.assistant?.enabled ?? false
|
||||
|
||||
global.capabilityActivity.removeAll()
|
||||
if let activities = data.capabilities.activity?.apiv2 {
|
||||
for activity in activities {
|
||||
|
|
|
@ -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) {
|
||||
do {
|
||||
let realm = try Realm()
|
||||
let result = realm.objects(tableDirectory.self).filter("account == %@ AND ocId == %@", account, ocId).first
|
||||
try realm.write {
|
||||
if let result = realm.objects(tableDirectory.self).filter("account == %@ AND ocId == %@", account, ocId).first {
|
||||
if let result {
|
||||
result.e2eEncrypted = e2eEncrypted
|
||||
result.favorite = favorite
|
||||
if let etag { result.etag = etag }
|
||||
|
@ -68,7 +69,7 @@ extension NCManageDatabase {
|
|||
if let richWorkspace { result.richWorkspace = richWorkspace }
|
||||
result.serverUrl = serverUrl
|
||||
result.account = account
|
||||
realm.add(result, update: .all)
|
||||
realm.add(result, update: .modified)
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
|
@ -80,8 +81,11 @@ extension NCManageDatabase {
|
|||
|
||||
#if !EXTENSION
|
||||
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
|
||||
|
|
|
@ -104,7 +104,7 @@ extension NCManageDatabase {
|
|||
}
|
||||
|
||||
@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 }
|
||||
var metadataUpdated: tableMetadata?
|
||||
|
||||
|
@ -113,6 +113,7 @@ extension NCManageDatabase {
|
|||
try realm.write {
|
||||
for metadata in metadatas {
|
||||
if let result = realm.objects(tableMetadata.self).filter("ocId == %@", metadata.ocId).first {
|
||||
result.sceneIdentifier = sceneIdentifier
|
||||
result.session = session
|
||||
result.sessionError = ""
|
||||
result.sessionSelector = selector
|
||||
|
@ -120,6 +121,7 @@ extension NCManageDatabase {
|
|||
result.sessionDate = Date()
|
||||
metadataUpdated = tableMetadata(value: result)
|
||||
} else {
|
||||
metadata.sceneIdentifier = sceneIdentifier
|
||||
metadata.session = session
|
||||
metadata.sessionError = ""
|
||||
metadata.sessionSelector = selector
|
||||
|
@ -142,6 +144,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
try realm.write {
|
||||
for metadata in metadatas {
|
||||
metadata.sceneIdentifier = nil
|
||||
metadata.session = ""
|
||||
metadata.sessionError = ""
|
||||
metadata.sessionSelector = ""
|
||||
|
|
|
@ -97,6 +97,7 @@ class tableMetadata: Object, NCUserBaseUrl {
|
|||
@objc dynamic var quotaAvailableBytes: Int64 = 0
|
||||
@objc dynamic var resourceType = ""
|
||||
@objc dynamic var richWorkspace: String?
|
||||
@objc dynamic var sceneIdentifier: String?
|
||||
@objc dynamic var serverUrl = ""
|
||||
@objc dynamic var session = ""
|
||||
@objc dynamic var sessionDate: Date?
|
||||
|
@ -211,7 +212,7 @@ extension tableMetadata {
|
|||
}
|
||||
|
||||
var canSetAsAvailableOffline: Bool {
|
||||
return session.isEmpty && !isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted
|
||||
return session.isEmpty && !isDirectoryE2EE && !e2eEncrypted
|
||||
}
|
||||
|
||||
var canShare: Bool {
|
||||
|
@ -388,14 +389,13 @@ extension NCManageDatabase {
|
|||
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 isDirectoryE2EE: Bool = false
|
||||
let listServerUrl = ThreadSafeDictionary<String, Bool>()
|
||||
|
||||
var metadataFolder = tableMetadata()
|
||||
var metadataFolders: [tableMetadata] = []
|
||||
var metadatas: [tableMetadata] = []
|
||||
|
||||
for file in files {
|
||||
|
@ -409,26 +409,23 @@ extension NCManageDatabase {
|
|||
|
||||
let metadata = convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
|
||||
|
||||
if counter == 0 && useMetadataFolder {
|
||||
metadataFolder = tableMetadata.init(value: metadata)
|
||||
if counter == 0 && useFirstAsMetadataFolder {
|
||||
metadataFolder = tableMetadata(value: metadata)
|
||||
} else {
|
||||
metadatas.append(metadata)
|
||||
if metadata.directory {
|
||||
metadataFolders.append(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
|
||||
let result = tableMetadata.init(value: metadata)
|
||||
let result = tableMetadata(value: metadata)
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
|
@ -499,7 +496,7 @@ extension NCManageDatabase {
|
|||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
|
||||
return nil
|
||||
}
|
||||
return tableMetadata.init(value: result)
|
||||
return tableMetadata(value: result)
|
||||
}
|
||||
|
||||
func addMetadatasWithoutUpdate(_ metadatas: [tableMetadata]) {
|
||||
|
@ -634,7 +631,11 @@ extension NCManageDatabase {
|
|||
result.favorite = false
|
||||
}
|
||||
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 {
|
||||
|
@ -671,7 +672,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -684,7 +685,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
guard let result = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending).first else { return nil }
|
||||
return tableMetadata.init(value: result)
|
||||
return tableMetadata(value: result)
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -698,7 +699,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -711,7 +712,7 @@ extension NCManageDatabase {
|
|||
do {
|
||||
let realm = try Realm()
|
||||
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 {
|
||||
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>? {
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
if let sorted {
|
||||
|
@ -759,27 +759,17 @@ extension NCManageDatabase {
|
|||
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] = []
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: sorted, ascending: ascending)
|
||||
if !results.isEmpty {
|
||||
if page == 0 || limit == 0 {
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
|
@ -797,7 +787,7 @@ extension NCManageDatabase {
|
|||
if results.isEmpty {
|
||||
return nil
|
||||
} else {
|
||||
return tableMetadata.init(value: results[index])
|
||||
return tableMetadata(value: results[index])
|
||||
}
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
|
@ -813,7 +803,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -827,7 +817,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -845,7 +835,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -874,7 +864,7 @@ extension NCManageDatabase {
|
|||
realm.refresh()
|
||||
guard let fileId = fileId else { return nil }
|
||||
guard let result = realm.objects(tableMetadata.self).filter("fileId == %@", fileId).first else { return nil }
|
||||
return tableMetadata.init(value: result)
|
||||
return tableMetadata(value: result)
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -902,7 +892,7 @@ extension NCManageDatabase {
|
|||
let realm = try Realm()
|
||||
realm.refresh()
|
||||
guard let result = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@", account, serverUrl, fileName).first else { return nil }
|
||||
return tableMetadata.init(value: result)
|
||||
return tableMetadata(value: result)
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -992,7 +982,7 @@ extension NCManageDatabase {
|
|||
do {
|
||||
let realm = try Realm()
|
||||
guard let result = realm.objects(tableMetadata.self).filter(NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileId == %@", metadata.account, metadata.serverUrl, metadata.livePhotoFile)).first else { return nil }
|
||||
return tableMetadata.init(value: result)
|
||||
return tableMetadata(value: result)
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -1053,7 +1043,7 @@ extension NCManageDatabase {
|
|||
realm.refresh()
|
||||
guard let directory = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first else { return nil }
|
||||
guard let result = realm.objects(tableMetadata.self).filter("ocId == %@", directory.ocId).first else { return nil }
|
||||
return tableMetadata.init(value: result)
|
||||
return tableMetadata(value: result)
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
@ -1150,11 +1140,23 @@ extension NCManageDatabase {
|
|||
do {
|
||||
let realm = try Realm()
|
||||
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 {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
let realm = try Realm()
|
||||
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) })
|
||||
} catch let error as NSError {
|
||||
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access to database: \(error)")
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
import SwiftUI
|
||||
import NextcloudKit
|
||||
import PreviewSnapshots
|
||||
|
||||
@objc class NCHostingCapabilitiesView: NSObject {
|
||||
|
||||
|
@ -50,6 +49,7 @@ class NCCapabilitiesViewOO: ObservableObject {
|
|||
@Published var capabililies: [Capability] = []
|
||||
@Published var homeServer = ""
|
||||
let utilityFileSystem = NCUtilityFileSystem()
|
||||
let utility = NCUtility()
|
||||
|
||||
init() {
|
||||
loadCapabilities()
|
||||
|
@ -65,24 +65,23 @@ class NCCapabilitiesViewOO: ObservableObject {
|
|||
|
||||
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) {
|
||||
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) + "/"
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +137,7 @@ struct NCCapabilitiesView: View {
|
|||
}
|
||||
}
|
||||
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)
|
||||
} else {
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ extension UIAlertController {
|
|||
/// - urlBase: UrlBase object
|
||||
/// - completion: If not` nil` it overrides the default behavior which shows an error using `NCContentPresenter`
|
||||
/// - Returns: The presentable alert controller
|
||||
static func createFolder(serverUrl: String, 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 okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
|
||||
|
@ -49,7 +49,7 @@ extension UIAlertController {
|
|||
}
|
||||
}
|
||||
} 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 {
|
||||
completion(error)
|
||||
} else if error != .success {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -117,7 +117,7 @@ extension UIColor {
|
|||
|
||||
guard let components = cgColor.components, components.count > 2 else {return false}
|
||||
let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
|
||||
return (brightness > 0.95)
|
||||
return (brightness > 0.90)
|
||||
}
|
||||
|
||||
@objc func isTooDark() -> Bool {
|
||||
|
@ -128,7 +128,7 @@ extension UIColor {
|
|||
|
||||
guard let components = cgColor.components, components.count > 2 else {return false}
|
||||
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 {
|
||||
|
|
|
@ -115,6 +115,10 @@ extension UIImage {
|
|||
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 {
|
||||
return image(color: color, width: size, height: size)
|
||||
}
|
||||
|
|
|
@ -32,13 +32,13 @@ extension UINavigationController {
|
|||
|
||||
func setNavigationBarAppearance() {
|
||||
|
||||
navigationBar.tintColor = .systemBlue
|
||||
navigationBar.tintColor = NCBrandColor.shared.iconImageColor
|
||||
|
||||
let standardAppearance = UINavigationBarAppearance()
|
||||
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
|
||||
|
||||
let scrollEdgeAppearance = UINavigationBarAppearance()
|
||||
|
@ -52,13 +52,13 @@ extension UINavigationController {
|
|||
|
||||
func setGroupAppearance() {
|
||||
|
||||
navigationBar.tintColor = .systemBlue
|
||||
navigationBar.tintColor = NCBrandColor.shared.iconImageColor
|
||||
|
||||
let standardAppearance = UINavigationBarAppearance()
|
||||
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
|
||||
navigationBar.standardAppearance = standardAppearance
|
||||
|
||||
|
|
|
@ -37,4 +37,17 @@ extension View {
|
|||
func hiddenConditionally(isHidden: Bool) -> some View {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class NCFavorite: NCCollectionViewCommon {
|
|||
layoutKey = NCGlobal.shared.layoutViewFavorite
|
||||
enableSearchBar = false
|
||||
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_"
|
||||
emptyDescription = "_tutorial_favorite_view_"
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ class NCFavorite: NCCollectionViewCommon {
|
|||
searchResults: self.searchResults)
|
||||
}
|
||||
|
||||
override func reloadDataSourceNetwork() {
|
||||
override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
|
||||
super.reloadDataSourceNetwork()
|
||||
|
||||
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()
|
||||
} completion: { account, files, _, error in
|
||||
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)
|
||||
self.reloadDataSource()
|
||||
}
|
||||
} else {
|
||||
self.reloadDataSource(withQueryDB: false)
|
||||
self.reloadDataSource(withQueryDB: withQueryDB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import UIKit
|
|||
import NextcloudKit
|
||||
|
||||
class NCFiles: NCCollectionViewCommon {
|
||||
|
||||
internal var isRoot: Bool = true
|
||||
internal var fileNameBlink: String?
|
||||
internal var fileNameOpen: String?
|
||||
|
@ -38,7 +37,7 @@ class NCFiles: NCCollectionViewCommon {
|
|||
enableSearchBar = true
|
||||
headerRichWorkspaceDisable = false
|
||||
headerMenuTransferView = true
|
||||
emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
|
||||
emptyImage = NCImageCache.images.folder
|
||||
emptyTitle = "_files_no_files_"
|
||||
emptyDescription = "_no_file_pull_down_"
|
||||
}
|
||||
|
@ -54,8 +53,6 @@ class NCFiles: NCCollectionViewCommon {
|
|||
self.navigationController?.popToRootViewController(animated: false)
|
||||
|
||||
self.serverUrl = self.utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
|
||||
self.appDelegate.activeServerUrl = self.serverUrl
|
||||
|
||||
self.isSearchingMode = false
|
||||
self.isEditMode = false
|
||||
self.selectOcId.removeAll()
|
||||
|
@ -78,7 +75,6 @@ class NCFiles: NCCollectionViewCommon {
|
|||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
||||
if isRoot {
|
||||
serverUrl = utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
|
||||
titleCurrentFolder = getNavigationTitle()
|
||||
|
@ -86,9 +82,9 @@ class NCFiles: NCCollectionViewCommon {
|
|||
super.viewWillAppear(animated)
|
||||
|
||||
if dataSource.metadatas.isEmpty {
|
||||
reloadDataSource()
|
||||
reloadDataSource(withQueryDB: true)
|
||||
}
|
||||
reloadDataSourceNetwork()
|
||||
reloadDataSourceNetwork(withQueryDB: true)
|
||||
}
|
||||
|
||||
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 {
|
||||
return networkSearch()
|
||||
}
|
||||
|
@ -172,49 +172,48 @@ class NCFiles: NCCollectionViewCommon {
|
|||
if metadatasDifferentCount != 0 || metadatasModified != 0 {
|
||||
self.reloadDataSource()
|
||||
} else {
|
||||
self.reloadDataSource(withQueryDB: false)
|
||||
self.reloadDataSource(withQueryDB: withQueryDB)
|
||||
}
|
||||
} 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) {
|
||||
|
||||
var tableDirectory: tableDirectory?
|
||||
|
||||
NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { task in
|
||||
self.dataSourceTask = task
|
||||
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)
|
||||
}
|
||||
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
|
||||
let forceReplaceMetadatas = tableDirectory?.etag == ""
|
||||
// swiftlint:enable empty_string
|
||||
|
||||
if tableDirectory?.etag != metadataFolder.etag || metadataFolder.e2eEncrypted {
|
||||
if tableDirectory?.etag != metadata.etag || metadata.e2eEncrypted {
|
||||
NCNetworking.shared.readFolder(serverUrl: self.serverUrl,
|
||||
account: self.appDelegate.account,
|
||||
forceReplaceMetadatas: forceReplaceMetadatas) { task in
|
||||
self.dataSourceTask = task
|
||||
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)
|
||||
}
|
||||
self.metadataFolder = metadataFolder
|
||||
// E2EE
|
||||
if let metadataFolder = metadataFolder,
|
||||
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
|
||||
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)
|
||||
if error == .success {
|
||||
if version == "v1", NCGlobal.shared.capabilityE2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
|
||||
|
@ -262,7 +261,6 @@ class NCFiles: NCCollectionViewCommon {
|
|||
}
|
||||
|
||||
func blinkCell(fileName: String?) {
|
||||
|
||||
if let fileName = fileName, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, fileName)) {
|
||||
let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
|
||||
if let indexPath = indexPath {
|
||||
|
@ -283,7 +281,6 @@ class NCFiles: NCCollectionViewCommon {
|
|||
}
|
||||
|
||||
func openFile(fileName: String?) {
|
||||
|
||||
if let fileName = fileName, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, fileName)) {
|
||||
let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
|
||||
if let indexPath = indexPath {
|
||||
|
|
|
@ -51,7 +51,7 @@ struct ButtonRounded: ButtonStyle {
|
|||
configuration.label
|
||||
.padding(.horizontal, 40)
|
||||
.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))
|
||||
.clipShape(Capsule())
|
||||
.opacity(configuration.isPressed ? 0.5 : 1.0)
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import PreviewSnapshots
|
||||
|
||||
struct HUDView: View {
|
||||
|
||||
|
@ -55,7 +54,7 @@ struct Blur: UIViewRepresentable {
|
|||
|
||||
func makeUIView(context: Context) -> UIVisualEffectView {
|
||||
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
|
||||
effectView.backgroundColor = NCBrandColor.shared.brand
|
||||
effectView.backgroundColor = NCBrandColor.shared.brandElement
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class NCGroupfolders: NCCollectionViewCommon {
|
|||
layoutKey = NCGlobal.shared.layoutViewGroupfolders
|
||||
enableSearchBar = false
|
||||
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_"
|
||||
emptyDescription = "_tutorial_groupfolders_view_"
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class NCGroupfolders: NCCollectionViewCommon {
|
|||
searchResults: self.searchResults)
|
||||
}
|
||||
|
||||
override func reloadDataSourceNetwork() {
|
||||
override func reloadDataSourceNetwork(withQueryDB: Bool = false) {
|
||||
super.reloadDataSourceNetwork()
|
||||
|
||||
let homeServerUrl = utilityFileSystem.getHomeServer(urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
|
||||
|
@ -102,7 +102,7 @@ class NCGroupfolders: NCCollectionViewCommon {
|
|||
self.reloadDataSource()
|
||||
}
|
||||
} else {
|
||||
self.reloadDataSource(withQueryDB: false)
|
||||
self.reloadDataSource(withQueryDB: withQueryDB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "contact.svg",
|
||||
"filename" : "InfoNetwork.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
|
@ -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
|
||||
}
|
||||
}
|
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic.png
поставляемый
До Ширина: | Высота: | Размер: 542 B |
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic@3x-1.png
поставляемый
До Ширина: | Высота: | Размер: 1.1 KiB |
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByAlphabetic.imageset/MenuGroupByAlphabetic@3x.png
поставляемый
До Ширина: | Высота: | Размер: 1.7 KiB |
|
@ -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
|
||||
}
|
||||
}
|
До Ширина: | Высота: | Размер: 319 B |
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate@2x.png
поставляемый
До Ширина: | Высота: | Размер: 570 B |
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByDate.imageset/MenuGroupByDate@3x.png
поставляемый
До Ширина: | Высота: | Размер: 925 B |
|
@ -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
|
||||
}
|
||||
}
|
До Ширина: | Высота: | Размер: 639 B |
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile@2x.png
поставляемый
До Ширина: | Высота: | Размер: 729 B |
Двоичные данные
iOSClient/Images.xcassets/MenuGroupByFile.imageset/MenuGroupByFile@3x.png
поставляемый
До Ширина: | Высота: | Размер: 934 B |
|
@ -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
|
||||
}
|
||||
}
|
Двоичные данные
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName.png
поставляемый
До Ширина: | Высота: | Размер: 459 B |
Двоичные данные
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName@2x.png
поставляемый
До Ширина: | Высота: | Размер: 841 B |
Двоичные данные
iOSClient/Images.xcassets/MenuOrderByFileName.imageset/MenuOrderByFileName@3x.png
поставляемый
До Ширина: | Высота: | Размер: 1.2 KiB |
|
@ -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
|
||||
}
|
||||
}
|
До Ширина: | Высота: | Размер: 464 B |
Двоичные данные
iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate@2x.png
поставляемый
До Ширина: | Высота: | Размер: 724 B |
Двоичные данные
iOSClient/Images.xcassets/MenuOrdeyByDate.imageset/MenuOrdeyByDate@3x.png
поставляемый
До Ширина: | Высота: | Размер: 1.3 KiB |