diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index cd45ed28d..09b9ff159 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -14,5 +14,5 @@ For _everything else_, please visit our official [Cryptomator Community](https:/ - Discussions about the apps - [Development discussions](https://community.cryptomator.org/c/development) - General questions - - Discussions regarding our design decissions + - Discussions regarding our design decisions - Our roadmap diff --git a/Cryptomator.xcodeproj/project.pbxproj b/Cryptomator.xcodeproj/project.pbxproj index 69493c602..67b464602 100644 --- a/Cryptomator.xcodeproj/project.pbxproj +++ b/Cryptomator.xcodeproj/project.pbxproj @@ -290,8 +290,6 @@ 4ABCF3522726D24800A7FBB7 /* MoveVaultViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABCF3512726D24800A7FBB7 /* MoveVaultViewModelTests.swift */; }; 4AC005F127C3D80B006FFE87 /* PremiumManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC005F027C3D80B006FFE87 /* PremiumManager.swift */; }; 4AC005F327C3D932006FFE87 /* PremiumManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC005F227C3D932006FFE87 /* PremiumManagerMock.swift */; }; - 4AC1157627F5BD890023F51B /* Promise+AllIgnoringResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC1157527F5BD890023F51B /* Promise+AllIgnoringResult.swift */; }; - 4AC1157827F5BEFD0023F51B /* Promise+AllIgnoringResultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC1157727F5BEFD0023F51B /* Promise+AllIgnoringResultsTests.swift */; }; 4AC86270273598CC00E15BA5 /* UIViewController+ProgressHUDError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC8626F273598CC00E15BA5 /* UIViewController+ProgressHUDError.swift */; }; 4AD0F61C24AF203F0026B765 /* FileProvider+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD0F61B24AF203F0026B765 /* FileProvider+Actions.swift */; }; 4AD3D7D6282EBDE7008188CD /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AD3D7D5282EBDE7008188CD /* Intents.framework */; }; @@ -434,6 +432,8 @@ 74F5DC1C26DCD2FB00AFE989 /* StoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F5DC1B26DCD2FB00AFE989 /* StoreObserver.swift */; }; 74F5DC1F26DD036D00AFE989 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F5DC1E26DD036D00AFE989 /* StoreManager.swift */; }; 74FC576125ADED030003ED27 /* VaultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FC576025ADED030003ED27 /* VaultCell.swift */; }; + B330CB452CB5735300C21E03 /* UnauthorizedErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */; }; + B3D19A442CB937C700CD18A5 /* FileProviderCoordinatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -828,8 +828,6 @@ 4ABCF3512726D24800A7FBB7 /* MoveVaultViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveVaultViewModelTests.swift; sourceTree = ""; }; 4AC005F027C3D80B006FFE87 /* PremiumManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumManager.swift; sourceTree = ""; }; 4AC005F227C3D932006FFE87 /* PremiumManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumManagerMock.swift; sourceTree = ""; }; - 4AC1157527F5BD890023F51B /* Promise+AllIgnoringResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+AllIgnoringResult.swift"; sourceTree = ""; }; - 4AC1157727F5BEFD0023F51B /* Promise+AllIgnoringResultsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+AllIgnoringResultsTests.swift"; sourceTree = ""; }; 4AC8626F273598CC00E15BA5 /* UIViewController+ProgressHUDError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+ProgressHUDError.swift"; sourceTree = ""; }; 4AD0F61B24AF203F0026B765 /* FileProvider+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProvider+Actions.swift"; sourceTree = ""; }; 4AD3D7D4282EBDE7008188CD /* CryptomatorIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = CryptomatorIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1041,6 +1039,8 @@ 74F5DC1B26DCD2FB00AFE989 /* StoreObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreObserver.swift; sourceTree = ""; }; 74F5DC1E26DD036D00AFE989 /* StoreManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreManager.swift; sourceTree = ""; }; 74FC576025ADED030003ED27 /* VaultCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultCell.swift; sourceTree = ""; }; + B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnauthorizedErrorViewController.swift; sourceTree = ""; }; + B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderCoordinatorError.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1214,7 +1214,6 @@ 4AFBFA19282946BF00E30818 /* InMemoryProgressManagerTests.swift */, 4AB1D4EF27D20420009060AB /* LocalURLProviderTests.swift */, 4A0AA12C2ABA277800CF24FD /* PermissionProviderImplTests.swift */, - 4AC1157727F5BEFD0023F51B /* Promise+AllIgnoringResultsTests.swift */, 4ADC66C427A7F6D6002E6CC7 /* UnlockMonitorTests.swift */, 4A4F47F224B875070033328B /* URL+NameCollisionExtensionTests.swift */, 4AE5196427F48D6600BA6E4A /* WorkflowDependencyFactoryTests.swift */, @@ -1663,7 +1662,9 @@ 4A9FCB0B251A02A3002A8B41 /* FileProviderExtensionUI.entitlements */, 4AA621EB249A6A8400A0BCBD /* Info.plist */, 4A6A521A268B7147006F7368 /* FileProviderCoordinator.swift */, + B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */, 4A6A5218268B6D31006F7368 /* OnboardingViewController.swift */, + B330CB442CB5735000C21E03 /* UnauthorizedErrorViewController.swift */, 4A6A520C268B5EF7006F7368 /* RootViewController.swift */, 4A9BED65268F2D9C00721BAA /* UnlockVaultViewController.swift */, 4AFD8C0E269304A700F77BA6 /* UnlockVaultViewModel.swift */, @@ -1926,7 +1927,6 @@ 4AEE6EE02822A33400E1B35E /* NSFileProviderItemIdentifier+Database.swift */, 4A0AA12A2AB8DB1800CF24FD /* PermissionProvider.swift */, 4AEE6EE92825716400E1B35E /* ProgressManager.swift */, - 4AC1157527F5BD890023F51B /* Promise+AllIgnoringResult.swift */, 4ADD233F26737CD400374E4E /* RootFileProviderItem.swift */, 4A7514A02937F777002E802E /* SessionTaskRegistrator.swift */, 4ADC66C027A7F426002E6CC7 /* UnlockMonitor.swift */, @@ -2572,7 +2572,6 @@ 4AB1C33C265E9DBC00DC7A49 /* CloudTaskExecutorTestCase.swift in Sources */, 4AE5196727F495BF00BA6E4A /* WorkflowDependencyTasksCollectionMock.swift in Sources */, 4A0AA12F2ABA2A1600CF24FD /* PermissionProviderMock.swift in Sources */, - 4AC1157827F5BEFD0023F51B /* Promise+AllIgnoringResultsTests.swift in Sources */, 4AE5196527F48D6600BA6E4A /* WorkflowDependencyFactoryTests.swift in Sources */, 4A49FABE271ECDE80069A0CC /* ItemEnumerationTaskManagerTests.swift in Sources */, 4A248229266E2DD6002D9F59 /* FileProviderAdapterCreateDirectoryTests.swift in Sources */, @@ -2670,6 +2669,8 @@ 4A804082276952C300D7D999 /* FileProviderCoordinatorSnapshotMock.swift in Sources */, 4A9BED66268F2D9D00721BAA /* UnlockVaultViewController.swift in Sources */, 4A6A521B268B7147006F7368 /* FileProviderCoordinator.swift in Sources */, + B330CB452CB5735300C21E03 /* UnauthorizedErrorViewController.swift in Sources */, + B3D19A442CB937C700CD18A5 /* FileProviderCoordinatorError.swift in Sources */, 4A6A5219268B6D32006F7368 /* OnboardingViewController.swift in Sources */, 4AFD8C0F269304A700F77BA6 /* UnlockVaultViewModel.swift in Sources */, ); @@ -2955,7 +2956,6 @@ 4ADD234026737CD400374E4E /* RootFileProviderItem.swift in Sources */, 747F2F232587BC250072FB30 /* ItemMetadataDBManager.swift in Sources */, 4A511D47265FEFBE000A0E01 /* DownloadTask.swift in Sources */, - 4AC1157627F5BD890023F51B /* Promise+AllIgnoringResult.swift in Sources */, 4A09E54E27071F4F0056D32A /* ErrorMapper.swift in Sources */, 4AEECD35279EB0FD00C6E2B5 /* FileProviderEnumerator.swift in Sources */, 4AE0D8DC2653DF1300DF5D22 /* DownloadTaskExecutor.swift in Sources */, @@ -3320,7 +3320,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 2.6.2; + MARKETING_VERSION = 2.6.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -3382,7 +3382,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 2.6.2; + MARKETING_VERSION = 2.6.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=200 -Xfrontend -warn-long-function-bodies=200"; diff --git a/Cryptomator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Cryptomator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5bef68762..5a4425aba 100644 --- a/Cryptomator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Cryptomator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/openid/AppAuth-iOS.git", "state" : { - "revision" : "71cde449f13d453227e687458144bde372d30fc7", - "version" : "1.6.2" + "revision" : "c89ed571ae140f8eb1142735e6e23d7bb8c34cb2", + "version" : "1.7.5" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/aws-sdk-ios-spm.git", "state" : { - "revision" : "8ff8bebfe24271f7b16c5abaeb78daf82bee3a80", - "version" : "2.34.2" + "revision" : "6e23b91609f9ddf988fda58bf711896a6062d4ff", + "version" : "2.35.0" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/cryptomator/cloud-access-swift.git", "state" : { - "revision" : "2d8444001a4b2c2ccf396942fd78680eefa9dc52", - "version" : "1.11.3" + "revision" : "299be2306bc133b6eefbf18c172a1b5ed9808a44", + "version" : "1.12.1" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/leif-ibsen/Digest", "state" : { - "revision" : "bb5de567a7b109a473ef5fddd3f02f1e5b9e2a41", - "version" : "1.7.0" + "revision" : "844a17be22efaa443130d081f2c4fa5f12c68e91", + "version" : "1.8.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/groue/GRDB.swift.git", "state" : { - "revision" : "dd7e7f39e8e4d7a22d258d9809a882f914690b01", - "version" : "5.26.1" + "revision" : "2cf6c756e1e5ef6901ebae16576a7e4e4b834622", + "version" : "6.29.3" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/gtm-session-fetcher.git", "state" : { - "revision" : "d415594121c9e8a4f9d79cecee0965cf35e74dbd", - "version" : "3.1.1" + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GTMAppAuth.git", "state" : { - "revision" : "41aba100f28395ebe842cd66e5d371cdd46c6792", - "version" : "4.0.0" + "revision" : "5d7d66f647400952b1758b230e019b07c0b4b22a", + "version" : "4.1.1" } }, { @@ -158,8 +158,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/AzureAD/microsoft-authentication-library-for-objc.git", "state" : { - "revision" : "d2f81ded070ac6452b2a6acb5bc45eb566427fe7", - "version" : "1.3.3" + "revision" : "b8733236bfd16e10849f4752a4d1c4621e4bf930", + "version" : "1.5.0" } }, { diff --git a/Cryptomator/Common/CloudAccountList/AccountListPosition.swift b/Cryptomator/Common/CloudAccountList/AccountListPosition.swift index f544be34b..9f5948efd 100644 --- a/Cryptomator/Common/CloudAccountList/AccountListPosition.swift +++ b/Cryptomator/Common/CloudAccountList/AccountListPosition.swift @@ -20,17 +20,17 @@ extension AccountListPosition: FetchableRecord, MutablePersistableRecord { static let databaseSelection: [SQLSelectable] = [AllColumns(), Column.rowID] static let account = belongsTo(CloudProviderAccount.self) - mutating func didInsert(with rowID: Int64, for column: String?) { - id = rowID + mutating func didInsert(_ inserted: InsertionSuccess) { + id = inserted.rowID } - init(row: Row) { + init(row: Row) throws { self.id = row[Column.rowID] self.position = row[Columns.position] self.accountUID = row[Columns.accountUID] } - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Column.rowID] = id container[Columns.position] = position container[Columns.accountUID] = accountUID diff --git a/Cryptomator/Common/DatabaseManager.swift b/Cryptomator/Common/DatabaseManager.swift index 1b0d9419b..c7142b0b2 100644 --- a/Cryptomator/Common/DatabaseManager.swift +++ b/Cryptomator/Common/DatabaseManager.swift @@ -97,7 +97,7 @@ class DatabaseManager { let observation = ValueObservation .tracking { db in try Row.fetchAll(db, request) } .removeDuplicates() - .map { rows in rows.map(AccountWithDisplayName.init(row:)) } + .map { rows in try rows.map(AccountWithDisplayName.init(row:)) } .map { annotatedAccounts in annotatedAccounts.map(\.account) } return observation.start(in: database, scheduling: .immediate, onError: onError, onChange: onChange) } diff --git a/Cryptomator/Snapshots/SnapshotCoordinator.swift b/Cryptomator/Snapshots/SnapshotCoordinator.swift index 30a75aa07..e9e29171a 100644 --- a/Cryptomator/Snapshots/SnapshotCoordinator.swift +++ b/Cryptomator/Snapshots/SnapshotCoordinator.swift @@ -100,10 +100,6 @@ private class SnapshotVaultLockingMock: VaultLocking { reply(true) } - func getUnlockedVaultDomainIdentifiers(reply: @escaping ([NSFileProviderDomainIdentifier]) -> Void) { - fatalError() - } - var serviceName: NSFileProviderServiceName = .init("org.cryptomator.ios.vault-locking") func makeListenerEndpoint() throws -> NSXPCListenerEndpoint { diff --git a/Cryptomator/VaultList/VaultCellViewModel.swift b/Cryptomator/VaultList/VaultCellViewModel.swift index a97e41c98..1a0ccf816 100644 --- a/Cryptomator/VaultList/VaultCellViewModel.swift +++ b/Cryptomator/VaultList/VaultCellViewModel.swift @@ -16,7 +16,6 @@ protocol VaultCellViewModelProtocol: TableViewCellViewModel, ViewModel { var vault: VaultInfo { get } var lockButtonIsHidden: AnyPublisher { get } func lockVault() -> Promise - func setVaultUnlockStatus(unlocked: Bool) } class VaultCellViewModel: TableViewCellViewModel, VaultCellViewModelProtocol { @@ -46,7 +45,7 @@ class VaultCellViewModel: TableViewCellViewModel, VaultCellViewModelProtocol { return getXPCPromise.then { xpc in xpc.proxy.lockVault(domainIdentifier: domainIdentifier) }.then { - self.setVaultUnlockStatus(unlocked: false) + self.vault.vaultIsUnlocked.value = false }.catch { error in self.errorPublisher.send(error) }.always { @@ -54,10 +53,6 @@ class VaultCellViewModel: TableViewCellViewModel, VaultCellViewModelProtocol { } } - func setVaultUnlockStatus(unlocked: Bool) { - vault.vaultIsUnlocked.value = unlocked - } - override func hash(into hasher: inout Hasher) { hasher.combine(vault.vaultUID) } diff --git a/Cryptomator/VaultList/VaultListPosition.swift b/Cryptomator/VaultList/VaultListPosition.swift index 71516e457..c00bf52a6 100644 --- a/Cryptomator/VaultList/VaultListPosition.swift +++ b/Cryptomator/VaultList/VaultListPosition.swift @@ -18,17 +18,17 @@ struct VaultListPosition: Codable { extension VaultListPosition: FetchableRecord, MutablePersistableRecord { static let databaseSelection: [SQLSelectable] = [AllColumns(), Column.rowID] - mutating func didInsert(with rowID: Int64, for column: String?) { - id = rowID + mutating func didInsert(_ inserted: InsertionSuccess) { + id = inserted.rowID } - init(row: Row) { + init(row: Row) throws { self.id = row[Column.rowID] self.position = row[Columns.position] self.vaultUID = row[Columns.vaultUID] } - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Column.rowID] = id container[Columns.position] = position container[Columns.vaultUID] = vaultUID diff --git a/Cryptomator/VaultList/VaultListViewModel.swift b/Cryptomator/VaultList/VaultListViewModel.swift index facc4ed0a..0adb8722b 100644 --- a/Cryptomator/VaultList/VaultListViewModel.swift +++ b/Cryptomator/VaultList/VaultListViewModel.swift @@ -104,24 +104,23 @@ class VaultListViewModel: ViewModel, VaultListViewModelProtocol { } func refreshVaultLockStates() -> Promise { - let getXPCPromise: Promise> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domain: nil) + let promises = vaultCellViewModels.map { vaultCellViewModel in + return checkVaultUnlockStatus(for: vaultCellViewModel.vault) + } + return all(ignoringResult: promises) + } + + private func checkVaultUnlockStatus(for vault: VaultInfo) -> Promise { + let domainIdentifier = NSFileProviderDomainIdentifier(vault.vaultUID) + let getXPCPromise: Promise> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier) return getXPCPromise.then { xpc in - return wrap { handler in - xpc.proxy.getUnlockedVaultDomainIdentifiers(reply: handler) - } - }.then { unlockedVaultDomainIdentifiers -> Void in - for domainIdentifier in unlockedVaultDomainIdentifiers { - let vaultInfo = self.vaultCellViewModels.first { $0.vault.vaultUID == domainIdentifier.rawValue } - vaultInfo?.setVaultUnlockStatus(unlocked: true) - } - self.vaultCellViewModels.filter { vaultCellViewModel in - unlockedVaultDomainIdentifiers.allSatisfy { domainIdentifier in - vaultCellViewModel.vault.vaultUID != domainIdentifier.rawValue - } - }.forEach { vaultCellViewModel in - vaultCellViewModel.setVaultUnlockStatus(unlocked: false) - } + return xpc.proxy.getIsUnlockedVault(domainIdentifier: domainIdentifier) + }.then { isUnlocked -> Void in + DDLogDebug("Vault \(vault.vaultUID) unlock status: \(isUnlocked ? "Unlocked" : "Locked")") + vault.vaultIsUnlocked.value = isUnlocked + }.catch { error in + DDLogError("Failed to check unlock status for vault \(vault.vaultUID): \(error)") }.always { self.fileProviderConnector.invalidateXPC(getXPCPromise) } diff --git a/CryptomatorCommon/Package.swift b/CryptomatorCommon/Package.swift index f86b99c00..26987fb3d 100644 --- a/CryptomatorCommon/Package.swift +++ b/CryptomatorCommon/Package.swift @@ -26,11 +26,11 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/cryptomator/cloud-access-swift.git", .upToNextMinor(from: "1.11.0")), + .package(url: "https://github.com/cryptomator/cloud-access-swift.git", .upToNextMinor(from: "1.12.0")), .package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", .upToNextMinor(from: "3.8.0")), .package(url: "https://github.com/PhilLibs/simple-swift-dependencies", .upToNextMajor(from: "0.1.0")), .package(url: "https://github.com/siteline/SwiftUI-Introspect.git", .upToNextMajor(from: "0.3.0")), - .package(url: "https://github.com/leif-ibsen/SwiftECC", from: "5.0.0") + .package(url: "https://github.com/leif-ibsen/SwiftECC", .upToNextMinor(from: "5.3.0")) ], targets: [ .target( diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/CryptomatorDatabase.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/CryptomatorDatabase.swift index e8fcd36ad..1a0a9b773 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/CryptomatorDatabase.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/CryptomatorDatabase.swift @@ -15,13 +15,14 @@ private enum CryptomatorDatabaseKey: DependencyKey { static let liveValue: DatabaseWriter = CryptomatorDatabase.live static var testValue: DatabaseWriter { - let inMemoryDB = DatabaseQueue(configuration: .defaultCryptomatorConfiguration) do { + let inMemoryDB = try DatabaseQueue(configuration: .defaultCryptomatorConfiguration) try CryptomatorDatabase.migrator.migrate(inMemoryDB) + return inMemoryDB } catch { - DDLogError("Failed to migrate in-memory database: \(error)") + DDLogError("Failed to initialize in-memory database: \(error)") + fatalError("Failed to initialize in-memory database") } - return inMemoryDB } } diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/FileProviderXPC/VaultLocking.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/FileProviderXPC/VaultLocking.swift index efa769e1c..f5fe652bc 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/FileProviderXPC/VaultLocking.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/FileProviderXPC/VaultLocking.swift @@ -14,7 +14,6 @@ import Promises func lockVault(domainIdentifier: NSFileProviderDomainIdentifier) func gracefulLockVault(domainIdentifier: NSFileProviderDomainIdentifier, reply: @escaping (Error?) -> Void) func getIsUnlockedVault(domainIdentifier: NSFileProviderDomainIdentifier, reply: @escaping (Bool) -> Void) - func getUnlockedVaultDomainIdentifiers(reply: @escaping ([NSFileProviderDomainIdentifier]) -> Void) } public extension NSFileProviderServiceName { @@ -41,10 +40,4 @@ public extension VaultLocking { self.getIsUnlockedVault(domainIdentifier: domainIdentifier, reply: replyHandler) } } - - func getUnlockedVaultDomainIdentifiers() -> Promise<[NSFileProviderDomainIdentifier]> { - return wrap { replyHandler in - self.getUnlockedVaultDomainIdentifiers(reply: replyHandler) - } - } } diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubRepository.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubRepository.swift index f44ee2488..3fd46cd5c 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubRepository.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubRepository.swift @@ -31,7 +31,7 @@ private struct HubVaultRow: Codable, Equatable, PersistableRecord, FetchableReco case vaultUID, subscriptionState } - public func encode(to container: inout PersistenceContainer) { + public func encode(to container: inout PersistenceContainer) throws { container[Columns.vaultUID] = vaultUID container[Columns.subscriptionState] = subscriptionState } diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/CloudProviderAccountDBManager.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/CloudProviderAccountDBManager.swift index 5fe7ded1f..590595e97 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/CloudProviderAccountDBManager.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/CloudProviderAccountDBManager.swift @@ -24,7 +24,7 @@ public struct CloudProviderAccount: Decodable, FetchableRecord, TableRecord, Equ } extension CloudProviderAccount: PersistableRecord { - public func encode(to container: inout PersistenceContainer) { + public func encode(to container: inout PersistenceContainer) throws { container[CloudProviderAccount.accountUIDKey] = accountUID container[CloudProviderAccount.cloudProviderTypeKey] = cloudProviderType } diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultAccountDBManager.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultAccountDBManager.swift index 3069ca55b..7e13cbd19 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultAccountDBManager.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultAccountDBManager.swift @@ -35,7 +35,7 @@ public struct VaultAccount: Decodable, FetchableRecord, TableRecord, Equatable { } extension VaultAccount: PersistableRecord { - public func encode(to container: inout PersistenceContainer) { + public func encode(to container: inout PersistenceContainer) throws { container[VaultAccount.vaultUIDKey] = vaultUID container[VaultAccount.delegateAccountUIDKey] = delegateAccountUID container[VaultAccount.vaultPathKey] = vaultPath diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultDBCache.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultDBCache.swift index 5c81ae1b5..a835c0b20 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultDBCache.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/Manager/VaultDBCache.swift @@ -35,7 +35,7 @@ extension CachedVault: FetchableRecord, TableRecord, PersistableRecord { case vaultUID, masterkeyFileData, vaultConfigToken, lastUpToDateCheck, masterkeyFileLastModifiedDate, vaultConfigLastModifiedDate } - public func encode(to container: inout PersistenceContainer) { + public func encode(to container: inout PersistenceContainer) throws { container[Columns.vaultUID] = vaultUID container[Columns.masterkeyFileData] = masterkeyFileData container[Columns.vaultConfigToken] = vaultConfigToken diff --git a/CryptomatorFileProvider/Promise+AllIgnoringResult.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/Promise+AllIgnoringResult.swift similarity index 69% rename from CryptomatorFileProvider/Promise+AllIgnoringResult.swift rename to CryptomatorCommon/Sources/CryptomatorCommonCore/Promise+AllIgnoringResult.swift index 057ba025a..d16ca1db6 100644 --- a/CryptomatorFileProvider/Promise+AllIgnoringResult.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/Promise+AllIgnoringResult.swift @@ -9,7 +9,7 @@ import Foundation import Promises -func all(ignoringResult promises: Container) -> Promise where Container.Element == Promise { +public func all(ignoringResult promises: Container) -> Promise where Container.Element == Promise { return any(promises).then { _ -> Void in // discard result }.recover { _ -> Void in diff --git a/CryptomatorCommon/Sources/CryptomatorCommonCore/S3/S3CredentialManager.swift b/CryptomatorCommon/Sources/CryptomatorCommonCore/S3/S3CredentialManager.swift index 5e431b141..5736eb913 100644 --- a/CryptomatorCommon/Sources/CryptomatorCommonCore/S3/S3CredentialManager.swift +++ b/CryptomatorCommon/Sources/CryptomatorCommonCore/S3/S3CredentialManager.swift @@ -5,6 +5,7 @@ // Created by Philipp Schmid on 29.06.22. // +import CocoaLumberjackSwift import CryptomatorCloudAccessCore import Dependencies import Foundation @@ -80,9 +81,14 @@ extension S3CredentialManager { var configuration = Configuration() // Workaround for a SQLite regression (see https://github.com/groue/GRDB.swift/issues/1171 for more details) configuration.acceptsDoubleQuotedStringLiterals = true - let inMemoryDB = DatabaseQueue(configuration: configuration) - try? CryptomatorDatabase.migrator.migrate(inMemoryDB) - return inMemoryDB + do { + let inMemoryDB = try DatabaseQueue(configuration: configuration) + try CryptomatorDatabase.migrator.migrate(inMemoryDB) + return inMemoryDB + } catch { + DDLogError("Failed to initialize in-memory database: \(error)") + fatalError("Failed to initialize in-memory database") + } } public static let demo = S3CredentialManager(keychain: CryptomatorKeychain(service: "s3CredentialDemo")) diff --git a/CryptomatorFileProviderTests/Promise+AllIgnoringResultsTests.swift b/CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Promise+AllIgnoringResultsTests.swift similarity index 92% rename from CryptomatorFileProviderTests/Promise+AllIgnoringResultsTests.swift rename to CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Promise+AllIgnoringResultsTests.swift index df9833f9a..106e5b744 100644 --- a/CryptomatorFileProviderTests/Promise+AllIgnoringResultsTests.swift +++ b/CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Promise+AllIgnoringResultsTests.swift @@ -1,14 +1,14 @@ // // Promise+AllIgnoringResultsTests.swift -// CryptomatorFileProviderTests +// CryptomatorCommonCoreTests // // Created by Philipp Schmid on 31.03.22. // Copyright © 2022 Skymatic GmbH. All rights reserved. // +import CryptomatorCommonCore import Promises import XCTest -@testable import CryptomatorFileProvider class Promise_AllIgnoringResultsTests: XCTestCase { func testWaitForAll() throws { diff --git a/CryptomatorCommon/Tests/CryptomatorCommonCoreTests/XCTestCase+Promises.swift b/CryptomatorCommon/Tests/CryptomatorCommonCoreTests/XCTestCase+Promises.swift index 938c35b74..ef9ccdcd8 100644 --- a/CryptomatorCommon/Tests/CryptomatorCommonCoreTests/XCTestCase+Promises.swift +++ b/CryptomatorCommon/Tests/CryptomatorCommonCoreTests/XCTestCase+Promises.swift @@ -37,4 +37,13 @@ extension XCTestCase { } wait(for: [expectation], timeout: 1.0) } + + func XCTAssertGetsNotExecuted(_ promise: Promise, timeout seconds: TimeInterval = 1.0, file: StaticString = #filePath, line: UInt = #line) { + let expectation = XCTestExpectation() + expectation.isInverted = true + promise.always { + expectation.fulfill() + } + wait(for: [expectation], timeout: seconds) + } } diff --git a/CryptomatorFileProvider/DB/DeletionTaskRecord.swift b/CryptomatorFileProvider/DB/DeletionTaskRecord.swift index 1036130b0..c88898ad1 100644 --- a/CryptomatorFileProvider/DB/DeletionTaskRecord.swift +++ b/CryptomatorFileProvider/DB/DeletionTaskRecord.swift @@ -30,7 +30,7 @@ extension DeletionTaskRecord { } extension DeletionTaskRecord: PersistableRecord { - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.correspondingItem] = correspondingItem container[Columns.cloudPath] = cloudPath container[Columns.parentID] = parentID diff --git a/CryptomatorFileProvider/DB/DownloadTaskRecord.swift b/CryptomatorFileProvider/DB/DownloadTaskRecord.swift index e577c2cd2..c10ab5bba 100644 --- a/CryptomatorFileProvider/DB/DownloadTaskRecord.swift +++ b/CryptomatorFileProvider/DB/DownloadTaskRecord.swift @@ -21,7 +21,7 @@ struct DownloadTaskRecord: Decodable, FetchableRecord, TableRecord { } extension DownloadTaskRecord: PersistableRecord { - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.correspondingItem] = correspondingItem container[Columns.replaceExisting] = replaceExisting container[Columns.localURL] = localURL diff --git a/CryptomatorFileProvider/DB/ItemEnumerationTaskRecord.swift b/CryptomatorFileProvider/DB/ItemEnumerationTaskRecord.swift index a01aae313..10ba72d2e 100644 --- a/CryptomatorFileProvider/DB/ItemEnumerationTaskRecord.swift +++ b/CryptomatorFileProvider/DB/ItemEnumerationTaskRecord.swift @@ -20,7 +20,7 @@ struct ItemEnumerationTaskRecord: Decodable, FetchableRecord, TableRecord { } extension ItemEnumerationTaskRecord: PersistableRecord { - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.correspondingItem] = correspondingItem container[Columns.pageToken] = pageToken } diff --git a/CryptomatorFileProvider/DB/ItemMetadata.swift b/CryptomatorFileProvider/DB/ItemMetadata.swift index 4a10d78ae..1ff2aa50d 100644 --- a/CryptomatorFileProvider/DB/ItemMetadata.swift +++ b/CryptomatorFileProvider/DB/ItemMetadata.swift @@ -32,7 +32,7 @@ public class ItemMetadata: Record, Codable { var favoriteRank: Int64? var tagData: Data? - required init(row: Row) { + required init(row: Row) throws { self.id = row[Columns.id] self.name = row[Columns.name] self.type = row[Columns.type] @@ -45,7 +45,7 @@ public class ItemMetadata: Record, Codable { self.isMaybeOutdated = row[Columns.isMaybeOutdated] self.favoriteRank = row[Columns.favoriteRank] self.tagData = row[Columns.tagData] - super.init(row: row) + try super.init(row: row) } convenience init(item: CloudItemMetadata, withParentID parentID: Int64, isPlaceholderItem: Bool = false) { @@ -68,11 +68,11 @@ public class ItemMetadata: Record, Codable { super.init() } - override public func didInsert(with rowID: Int64, for column: String?) { - id = rowID + override public func didInsert(_ inserted: InsertionSuccess) { + id = inserted.rowID } - override public func encode(to container: inout PersistenceContainer) { + override public func encode(to container: inout PersistenceContainer) throws { container[Columns.id] = id container[Columns.name] = name container[Columns.type] = type diff --git a/CryptomatorFileProvider/DB/LocalCachedFileInfo.swift b/CryptomatorFileProvider/DB/LocalCachedFileInfo.swift index 403c3cf71..268f1ecd4 100644 --- a/CryptomatorFileProvider/DB/LocalCachedFileInfo.swift +++ b/CryptomatorFileProvider/DB/LocalCachedFileInfo.swift @@ -23,7 +23,7 @@ struct LocalCachedFileInfo: Decodable, FetchableRecord, TableRecord { } extension LocalCachedFileInfo: PersistableRecord { - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.lastModifiedDate] = lastModifiedDate container[Columns.correspondingItem] = correspondingItem container[Columns.localLastModifiedDate] = localLastModifiedDate diff --git a/CryptomatorFileProvider/DB/MaintenanceDBManager.swift b/CryptomatorFileProvider/DB/MaintenanceDBManager.swift index a2969c5a0..910c95b83 100644 --- a/CryptomatorFileProvider/DB/MaintenanceDBManager.swift +++ b/CryptomatorFileProvider/DB/MaintenanceDBManager.swift @@ -76,7 +76,7 @@ private struct MaintenanceModeEntry: Decodable, FetchableRecord, TableRecord, Pe case id, flag } - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.id] = id container[Columns.flag] = flag } diff --git a/CryptomatorFileProvider/DB/ReparentTaskRecord.swift b/CryptomatorFileProvider/DB/ReparentTaskRecord.swift index 391ab0dea..7ca472329 100644 --- a/CryptomatorFileProvider/DB/ReparentTaskRecord.swift +++ b/CryptomatorFileProvider/DB/ReparentTaskRecord.swift @@ -25,7 +25,7 @@ struct ReparentTaskRecord: Decodable, FetchableRecord, TableRecord { } extension ReparentTaskRecord: PersistableRecord { - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.correspondingItem] = correspondingItem container[Columns.sourceCloudPath] = sourceCloudPath container[Columns.targetCloudPath] = targetCloudPath diff --git a/CryptomatorFileProvider/DB/UploadTaskRecord.swift b/CryptomatorFileProvider/DB/UploadTaskRecord.swift index 02d4fa879..49947b59b 100644 --- a/CryptomatorFileProvider/DB/UploadTaskRecord.swift +++ b/CryptomatorFileProvider/DB/UploadTaskRecord.swift @@ -40,7 +40,7 @@ struct UploadTaskRecord: Decodable, FetchableRecord, TableRecord { } extension UploadTaskRecord: PersistableRecord { - func encode(to container: inout PersistenceContainer) { + func encode(to container: inout PersistenceContainer) throws { container[Columns.correspondingItem] = correspondingItem container[Columns.lastFailedUploadDate] = lastFailedUploadDate container[Columns.uploadErrorCode] = uploadErrorCode diff --git a/CryptomatorFileProvider/FileProviderAdapterManager.swift b/CryptomatorFileProvider/FileProviderAdapterManager.swift index d53e08185..fcaaa2c5b 100644 --- a/CryptomatorFileProvider/FileProviderAdapterManager.swift +++ b/CryptomatorFileProvider/FileProviderAdapterManager.swift @@ -141,12 +141,6 @@ public class FileProviderAdapterManager: FileProviderAdapterProviding { return adapterCache.getItem(identifier: domainIdentifier) != nil } - public func getDomainIdentifiersOfUnlockedVaults() -> [NSFileProviderDomainIdentifier] { - let cachedIdentifiers = adapterCache.getAllCachedIdentifiers() - cachedIdentifiers.forEach { updateLockStatus(domainIdentifier: $0) } - return adapterCache.getAllCachedIdentifiers() - } - /** Locks a vault gracefully. @@ -250,7 +244,6 @@ protocol FileProviderAdapterCacheType { func cacheItem(_ item: AdapterCacheItem, identifier: NSFileProviderDomainIdentifier) func removeItem(identifier: NSFileProviderDomainIdentifier) func getItem(identifier: NSFileProviderDomainIdentifier) -> AdapterCacheItem? - func getAllCachedIdentifiers() -> [NSFileProviderDomainIdentifier] } class FileProviderAdapterCache: FileProviderAdapterCacheType { @@ -274,10 +267,4 @@ class FileProviderAdapterCache: FileProviderAdapterCacheType { return cachedAdapters[identifier] } } - - func getAllCachedIdentifiers() -> [NSFileProviderDomainIdentifier] { - queue.sync { - return cachedAdapters.map { $0.key } - } - } } diff --git a/CryptomatorFileProvider/ServiceSource/VaultLockingServiceSource.swift b/CryptomatorFileProvider/ServiceSource/VaultLockingServiceSource.swift index b6e5576f7..96f118126 100644 --- a/CryptomatorFileProvider/ServiceSource/VaultLockingServiceSource.swift +++ b/CryptomatorFileProvider/ServiceSource/VaultLockingServiceSource.swift @@ -35,8 +35,4 @@ public class VaultLockingServiceSource: ServiceSource, VaultLocking { public func getIsUnlockedVault(domainIdentifier: NSFileProviderDomainIdentifier, reply: @escaping (Bool) -> Void) { reply(FileProviderAdapterManager.shared.vaultIsUnlocked(domainIdentifier: domainIdentifier)) } - - public func getUnlockedVaultDomainIdentifiers(reply: @escaping ([NSFileProviderDomainIdentifier]) -> Void) { - reply(FileProviderAdapterManager.shared.getDomainIdentifiersOfUnlockedVaults()) - } } diff --git a/CryptomatorFileProvider/Workflow/WorkflowDependencyFactory.swift b/CryptomatorFileProvider/Workflow/WorkflowDependencyFactory.swift index c35c7f311..9aa0d2fb0 100644 --- a/CryptomatorFileProvider/Workflow/WorkflowDependencyFactory.swift +++ b/CryptomatorFileProvider/Workflow/WorkflowDependencyFactory.swift @@ -8,6 +8,7 @@ import CocoaLumberjackSwift import CryptomatorCloudAccessCore +import CryptomatorCommonCore import Foundation import Promises diff --git a/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift b/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift index b5edb91f1..bfd6b9d4e 100644 --- a/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift @@ -19,7 +19,7 @@ class CachedFileManagerTests: CacheTestCase { override func setUpWithError() throws { try super.setUpWithError() - inMemoryDB = DatabaseQueue() + inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = CachedFileDBManager(database: inMemoryDB, fileManagerHelper: .init(fileCoordinator: .init())) metadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift index a8094f6e3..6395bece3 100644 --- a/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift @@ -16,7 +16,7 @@ class DeletionTaskManagerTests: XCTestCase { var itemMetadataManager: ItemMetadataDBManager! override func setUpWithError() throws { - let inMemoryDB = DatabaseQueue() + let inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = try DeletionTaskDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift index abb9f426e..1b7258f0b 100644 --- a/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift @@ -17,7 +17,7 @@ class DownloadTaskManagerTests: XCTestCase { var inMemoryDB: DatabaseQueue! override func setUpWithError() throws { - inMemoryDB = DatabaseQueue() + inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = try DownloadTaskDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift index 4eebc6a8d..fa5b1e02e 100644 --- a/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift @@ -17,7 +17,7 @@ class ItemEnumerationTaskManagerTests: XCTestCase { var inMemoryDB: DatabaseQueue! override func setUpWithError() throws { - inMemoryDB = DatabaseQueue() + inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = try ItemEnumerationTaskDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift b/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift index 1065ab970..600cb3da7 100644 --- a/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift @@ -23,7 +23,7 @@ class MaintenanceManagerTests: XCTestCase { var inMemoryDB: DatabaseQueue! override func setUpWithError() throws { - inMemoryDB = DatabaseQueue() + inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = MaintenanceDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift b/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift index e9981167c..e4be8e517 100644 --- a/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift @@ -15,7 +15,7 @@ class MetadataManagerTests: XCTestCase { var manager: ItemMetadataDBManager! override func setUpWithError() throws { - let inMemoryDB = DatabaseQueue() + let inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = ItemMetadataDBManager(database: inMemoryDB) } diff --git a/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift index ece6ef73c..78cbd2fce 100644 --- a/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift @@ -17,7 +17,7 @@ class ReparentTaskManagerTests: XCTestCase { var inMemoryDB: DatabaseQueue! override func setUpWithError() throws { - inMemoryDB = DatabaseQueue() + inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = try ReparentTaskDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift index 585a8d53b..bf078d23c 100644 --- a/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift @@ -18,7 +18,7 @@ class UploadTaskManagerTests: XCTestCase { var inMemoryDB: DatabaseQueue! override func setUpWithError() throws { - inMemoryDB = DatabaseQueue() + inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) manager = UploadTaskDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) diff --git a/CryptomatorFileProviderTests/FileProviderAdapterManagerTests.swift b/CryptomatorFileProviderTests/FileProviderAdapterManagerTests.swift index 683b8e843..5838120cb 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapterManagerTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapterManagerTests.swift @@ -226,34 +226,4 @@ class FileProviderAdapterManagerTests: XCTestCase { XCTAssertEqual(1, maintenanceManagerMock.disableMaintenanceModeCallsCount) XCTAssert(cache.isEmpty) } - - func testGetDomainIdentifiersOfUnlockedVaults() throws { - let unlockedDomainIdentifier = NSFileProviderDomainIdentifier(rawValue: "1") - let lockedDomainIdentifier = NSFileProviderDomainIdentifier(rawValue: "2") - notificatorManagerMock.getFileProviderNotificatorForReturnValue = fileProviderNotificatorMock - let unlockedVaultMaintenanceManagerMock = MaintenanceManagerMock() - let lockedVaultMaintenanceManagerMock = MaintenanceManagerMock() - let unlockedVaultAdapterCacheItem = AdapterCacheItem(adapter: FileProviderAdapterTypeMock(), maintenanceManager: unlockedVaultMaintenanceManagerMock, workingSetObserver: workingSetObservationMock) - let lockedVaultAdapterCacheItem = AdapterCacheItem(adapter: FileProviderAdapterTypeMock(), maintenanceManager: lockedVaultMaintenanceManagerMock, workingSetObserver: workingSetObservationMock) - - var cache = [NSFileProviderDomainIdentifier: AdapterCacheItem]() - cache[unlockedDomainIdentifier] = unlockedVaultAdapterCacheItem - cache[lockedDomainIdentifier] = lockedVaultAdapterCacheItem - adapterCacheMock.getItemIdentifierClosure = { return cache[$0] } - adapterCacheMock.getAllCachedIdentifiersClosure = { cache.map { $0.key }} - adapterCacheMock.removeItemIdentifierClosure = { - cache[$0] = nil - } - vaultKeepUnlockedHelperMock.shouldAutoLockVaultWithVaultUIDClosure = { $0 != unlockedDomainIdentifier.rawValue } - - XCTAssertEqual([unlockedDomainIdentifier], fileProviderAdapterManager.getDomainIdentifiersOfUnlockedVaults()) - - XCTAssertFalse(unlockedVaultMaintenanceManagerMock.enableMaintenanceModeCalled) - XCTAssertFalse(unlockedVaultMaintenanceManagerMock.disableMaintenanceModeCalled) - XCTAssertEqual(1, lockedVaultMaintenanceManagerMock.enableMaintenanceModeCallsCount) - XCTAssertEqual(1, lockedVaultMaintenanceManagerMock.disableMaintenanceModeCallsCount) - - XCTAssertEqual(1, cache.count) - XCTAssertNotNil(cache[unlockedDomainIdentifier]) - } } diff --git a/CryptomatorFileProviderTests/Mocks/FileProviderAdapterCacheTypeMock.swift b/CryptomatorFileProviderTests/Mocks/FileProviderAdapterCacheTypeMock.swift index 0b597508d..94b1bcb6b 100644 --- a/CryptomatorFileProviderTests/Mocks/FileProviderAdapterCacheTypeMock.swift +++ b/CryptomatorFileProviderTests/Mocks/FileProviderAdapterCacheTypeMock.swift @@ -65,19 +65,4 @@ final class FileProviderAdapterCacheTypeMock: FileProviderAdapterCacheType { getItemIdentifierReceivedInvocations.append(identifier) return getItemIdentifierClosure.map({ $0(identifier) }) ?? getItemIdentifierReturnValue } - - // MARK: - getAllCachedIdentifiers - - var getAllCachedIdentifiersCallsCount = 0 - var getAllCachedIdentifiersCalled: Bool { - getAllCachedIdentifiersCallsCount > 0 - } - - var getAllCachedIdentifiersReturnValue: [NSFileProviderDomainIdentifier]! - var getAllCachedIdentifiersClosure: (() -> [NSFileProviderDomainIdentifier])? - - func getAllCachedIdentifiers() -> [NSFileProviderDomainIdentifier] { - getAllCachedIdentifiersCallsCount += 1 - return getAllCachedIdentifiersClosure.map({ $0() }) ?? getAllCachedIdentifiersReturnValue - } } diff --git a/CryptomatorFileProviderTests/WorkingSetObserverTests.swift b/CryptomatorFileProviderTests/WorkingSetObserverTests.swift index 728b31357..f3cf38f58 100644 --- a/CryptomatorFileProviderTests/WorkingSetObserverTests.swift +++ b/CryptomatorFileProviderTests/WorkingSetObserverTests.swift @@ -22,7 +22,7 @@ class WorkingSetObserverTests: XCTestCase { override func setUpWithError() throws { notificatorMock = FileProviderNotificatorTypeMock() - observer = WorkingSetObserver(domainIdentifier: .test, database: DatabaseQueue(), notificator: notificatorMock, uploadTaskManager: UploadTaskManagerMock(), cachedFileManager: CloudTaskExecutorTestCase.CachedFileManagerMock()) + observer = try WorkingSetObserver(domainIdentifier: .test, database: DatabaseQueue(), notificator: notificatorMock, uploadTaskManager: UploadTaskManagerMock(), cachedFileManager: CloudTaskExecutorTestCase.CachedFileManagerMock()) } func testHandleNewWorkingSetUpdate() throws { diff --git a/CryptomatorIntents/af.lproj/Intents.strings b/CryptomatorIntents/af.lproj/Intents.strings new file mode 100644 index 000000000..e69de29bb diff --git a/CryptomatorIntents/ja.lproj/Intents.strings b/CryptomatorIntents/ja.lproj/Intents.strings index 79aa1b66b..355084f98 100644 --- a/CryptomatorIntents/ja.lproj/Intents.strings +++ b/CryptomatorIntents/ja.lproj/Intents.strings @@ -12,7 +12,7 @@ "isUnlockedIntent.description" = "金庫が解錠かどうか戻します。"; "isUnlockedIntent.title" = "解錠済"; -"isVaultLockedIntent.title" = "金庫が解錠ですか?"; +"isVaultLockedIntent.title" = "金庫は解錠済みか?"; "isVaultUnlockedIntent.text" = "${vault} が解錠してありますか?"; "lockVaultIntent.description" = "金庫を施錠します。"; diff --git a/CryptomatorIntents/no.lproj/Intents.strings b/CryptomatorIntents/no.lproj/Intents.strings new file mode 100644 index 000000000..e69de29bb diff --git a/CryptomatorIntents/th.lproj/Intents.strings b/CryptomatorIntents/th.lproj/Intents.strings index c369e1822..c9b582825 100644 --- a/CryptomatorIntents/th.lproj/Intents.strings +++ b/CryptomatorIntents/th.lproj/Intents.strings @@ -1 +1,15 @@ +"common.failureReason" = "สาเหตุความขัดข้อง"; +"common.false" = "เท็จ"; +"common.folder" = "โฟลเดอร์"; +"common.true" = "ถูกต้อง"; "common.vault" = "Vault"; +"getFolderIntent.path" = "ที่อยู่"; +"isUnlockedIntent.title" = "ปลดล็อกแล้ว"; + +"lockVaultIntent.description" = "ล็อก vault ที่กำหนด"; +"lockVaultIntent.title" = "ล็อก Vault"; +"openVaultIntent.title" = "เปิด Vault"; + +"saveFileIntent.description" = "บันทึกไฟล์ลงใน vault"; +"saveFileIntent.file" = "ไฟล์"; +"saveFileIntent.title" = "บันทึกไฟล์"; diff --git a/CryptomatorIntents/ug.lproj/Intents.strings b/CryptomatorIntents/ug.lproj/Intents.strings new file mode 100644 index 000000000..60b703038 --- /dev/null +++ b/CryptomatorIntents/ug.lproj/Intents.strings @@ -0,0 +1 @@ +"common.vault" = "ئامبار"; diff --git a/CryptomatorTests/VaultListViewModelTests.swift b/CryptomatorTests/VaultListViewModelTests.swift index 897506aba..8b465988f 100644 --- a/CryptomatorTests/VaultListViewModelTests.swift +++ b/CryptomatorTests/VaultListViewModelTests.swift @@ -131,10 +131,9 @@ class VaultListViewModelTests: XCTestCase { vaultLockingMock.unlockedVaults.append(NSFileProviderDomainIdentifier("vault1")) vaultListViewModel.refreshVaultLockStates().then { - XCTAssertNil(self.fileProviderConnectorMock.passedDomain) + XCTAssertEqual(self.fileProviderConnectorMock.passedDomainIdentifier?.rawValue, "vault1") XCTAssertEqual(NSFileProviderServiceName("org.cryptomator.ios.vault-locking"), self.fileProviderConnectorMock.passedServiceName) - XCTAssertEqual(1, vaultLockingMock.unlockedVaults.count) let unlockedVaults = vaultListViewModel.getVaults().filter({ $0.vaultIsUnlocked.value }) XCTAssertEqual(1, unlockedVaults.count) XCTAssertTrue(unlockedVaults.contains(where: { $0.vaultUID == "vault1" })) @@ -144,7 +143,7 @@ class VaultListViewModelTests: XCTestCase { expectation.fulfill() } wait(for: [expectation], timeout: 1.0) - XCTAssertEqual(1, fileProviderConnectorMock.xpcInvalidationCallCount) + XCTAssertEqual(2, fileProviderConnectorMock.xpcInvalidationCallCount) } } @@ -245,10 +244,6 @@ class VaultLockingMock: VaultLocking { reply(unlockedVaults.contains(domainIdentifier)) } - func getUnlockedVaultDomainIdentifiers(reply: @escaping ([NSFileProviderDomainIdentifier]) -> Void) { - reply(unlockedVaults) - } - let serviceName = NSFileProviderServiceName("org.cryptomator.ios.vault-locking") func makeListenerEndpoint() throws -> NSXPCListenerEndpoint { diff --git a/FileProviderExtensionUI/FileProviderCoordinator.swift b/FileProviderExtensionUI/FileProviderCoordinator.swift index 40bc0ff09..eb74e39f5 100644 --- a/FileProviderExtensionUI/FileProviderCoordinator.swift +++ b/FileProviderExtensionUI/FileProviderCoordinator.swift @@ -63,9 +63,13 @@ class FileProviderCoordinator: Coordinator { func handleError(_ error: Error, for viewController: UIViewController) { DDLogError("Error: \(error)") - let alertController = UIAlertController(title: LocalizedString.getValue("common.alert.error.title"), message: error.localizedDescription, preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: LocalizedString.getValue("common.button.ok"), style: .default)) - viewController.present(alertController, animated: true) + if let fileProviderError = error as? FileProviderCoordinatorError, case let .unauthorized(vaultName) = fileProviderError { + showUnauthorizedError(vaultName: vaultName) + } else { + let alertController = UIAlertController(title: LocalizedString.getValue("common.alert.error.title"), message: error.localizedDescription, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: LocalizedString.getValue("common.button.ok"), style: .default)) + viewController.present(alertController, animated: true) + } } func done() { @@ -80,6 +84,12 @@ class FileProviderCoordinator: Coordinator { navigationController.pushViewController(onboardingVC, animated: false) } + func showUnauthorizedError(vaultName: String) { + let unauthorizedErrorVC = UnauthorizedErrorViewController(vaultName: vaultName) + unauthorizedErrorVC.coordinator = self + navigationController.pushViewController(unauthorizedErrorVC, animated: true) + } + func openCryptomatorApp() { let url = URL(string: "cryptomator:")! extensionContext.open(url) { success in @@ -134,6 +144,8 @@ class FileProviderCoordinator: Coordinator { switch error { case CloudProviderError.noInternetConnection, LocalizedCloudProviderError.itemNotFound: break + case LocalizedCloudProviderError.unauthorized: + throw FileProviderCoordinatorError.unauthorized(vaultName: domain.displayName) default: throw error } diff --git a/FileProviderExtensionUI/FileProviderCoordinatorError.swift b/FileProviderExtensionUI/FileProviderCoordinatorError.swift new file mode 100644 index 000000000..9c097d2d0 --- /dev/null +++ b/FileProviderExtensionUI/FileProviderCoordinatorError.swift @@ -0,0 +1,13 @@ +// +// FileProviderCoordinatorError.swift +// Cryptomator +// +// Created by Majid Achhoud on 11.10.24. +// Copyright © 2024 Skymatic GmbH. All rights reserved. +// + +import Foundation + +public enum FileProviderCoordinatorError: Error { + case unauthorized(vaultName: String) +} diff --git a/FileProviderExtensionUI/UnauthorizedErrorViewController.swift b/FileProviderExtensionUI/UnauthorizedErrorViewController.swift new file mode 100644 index 000000000..10797c1dd --- /dev/null +++ b/FileProviderExtensionUI/UnauthorizedErrorViewController.swift @@ -0,0 +1,81 @@ +// +// UnauthorizedErrorViewController.swift +// Cryptomator +// +// Created by Majid Achhoud on 08.10.24. +// Copyright © 2024 Skymatic GmbH. All rights reserved. +// + +import CryptomatorCommonCore +import UIKit + +class UnauthorizedErrorViewController: UITableViewController { + weak var coordinator: FileProviderCoordinator? + private var vaultName: String + + private lazy var openCryptomatorCell: UITableViewCell = { + let cell = UITableViewCell() + cell.textLabel?.text = LocalizedString.getValue("fileProvider.onboarding.button.openCryptomator") + cell.textLabel?.textColor = .cryptomatorPrimary + return cell + }() + + init(vaultName: String) { + self.vaultName = vaultName + super.init(style: .insetGrouped) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + title = vaultName + let doneButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(done)) + navigationItem.rightBarButtonItem = doneButton + tableView.backgroundColor = .cryptomatorBackground + tableView.cellLayoutMarginsFollowReadableWidth = true + } + + @objc func done() { + coordinator?.userCancelled() + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + // MARK: - UITableViewDataSource + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + return openCryptomatorCell + } + + // MARK: - UITableViewDelegate + + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + return UnauthorizedErrorHeaderView(vaultName: vaultName) + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + coordinator?.openCryptomatorApp() + } +} + +private class UnauthorizedErrorHeaderView: LargeHeaderFooterView { + init(vaultName: String) { + let config = UIImage.SymbolConfiguration(pointSize: 120) + let symbolImage = UIImage(systemName: "exclamationmark.triangle.fill", withConfiguration: config)?.withTintColor(.systemYellow, renderingMode: .alwaysOriginal) + + let infoText = String(format: LocalizedString.getValue("fileprovider.error.unauthorized.text"), vaultName) + + super.init(image: symbolImage, infoText: infoText) + } +} diff --git a/README.md b/README.md index 7580457f1..d68fa6649 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ If you are building with Xcode, create a `.cloud-access-secrets.sh` file in the ```sh #!/bin/sh +export BOX_CLIENT_ID=... +export BOX_CLIENT_SECRET=... export DROPBOX_APP_KEY=... export GOOGLE_DRIVE_CLIENT_ID=... export GOOGLE_DRIVE_REDIRECT_URL_SCHEME=... diff --git a/SharedResources/Assets.xcassets/AppIcon.appiconset/Contents.json b/SharedResources/Assets.xcassets/AppIcon.appiconset/Contents.json index b3f44eb29..b2630b9a9 100644 --- a/SharedResources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/SharedResources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,31 @@ { "images" : [ { - "filename" : "Icon.png", + "filename" : "LightIcon.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "DarkIcon.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "TintedIcon.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/SharedResources/Assets.xcassets/AppIcon.appiconset/DarkIcon.png b/SharedResources/Assets.xcassets/AppIcon.appiconset/DarkIcon.png new file mode 100644 index 000000000..2dc3b69ba Binary files /dev/null and b/SharedResources/Assets.xcassets/AppIcon.appiconset/DarkIcon.png differ diff --git a/SharedResources/Assets.xcassets/AppIcon.appiconset/Icon.png b/SharedResources/Assets.xcassets/AppIcon.appiconset/LightIcon.png similarity index 100% rename from SharedResources/Assets.xcassets/AppIcon.appiconset/Icon.png rename to SharedResources/Assets.xcassets/AppIcon.appiconset/LightIcon.png diff --git a/SharedResources/Assets.xcassets/AppIcon.appiconset/TintedIcon.png b/SharedResources/Assets.xcassets/AppIcon.appiconset/TintedIcon.png new file mode 100644 index 000000000..8ab243204 Binary files /dev/null and b/SharedResources/Assets.xcassets/AppIcon.appiconset/TintedIcon.png differ diff --git a/SharedResources/af.lproj/Localizable.strings b/SharedResources/af.lproj/Localizable.strings new file mode 100644 index 000000000..e69de29bb diff --git a/SharedResources/de.lproj/Localizable.strings b/SharedResources/de.lproj/Localizable.strings index 1bf251644..bfb7917cf 100644 --- a/SharedResources/de.lproj/Localizable.strings +++ b/SharedResources/de.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Das für %@ gespeicherte Passwort ist falsch. Bitte versuche es erneut und gib dein Passwort ein, um %@ wieder zu aktivieren."; "fileProvider.error.defaultLock.title" = "Entsperren erforderlich"; "fileProvider.error.defaultLock.message" = "Um auf den Inhalt deines Tresors zuzugreifen und ihn anzuzeigen, muss dieser entsperrt werden."; +"fileprovider.error.unauthorized.text" = "Der Zugriff auf deinen Tresor „%@“ wurde verweigert. Öffne die Haupt-App, um die Verbindung zu überprüfen, und authentifiziere dich bei Bedarf neu."; "fileProvider.error.unlockButton" = "Entsperren"; "fileProvider.clearFileFromCache.title" = "Datei aus Cache löschen"; "fileProvider.clearFileFromCache.message" = "Dies entfernt nur die lokale Datei von deinem Gerät und löscht nicht die Datei in der Cloud."; diff --git a/SharedResources/el.lproj/Localizable.strings b/SharedResources/el.lproj/Localizable.strings index 2c1eeef59..8de53b4d1 100644 --- a/SharedResources/el.lproj/Localizable.strings +++ b/SharedResources/el.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Ο κωδικός πρόσβασης που έχει αποθηκευτεί για %@ είναι λάθος. Παρακαλώ προσπαθήστε ξανά και εισάγετε τον κωδικό πρόσβασής σας για να ενεργοποιήσετε ξανά %@."; "fileProvider.error.defaultLock.title" = "Απαιτείται Ξεκλείδωμα"; "fileProvider.error.defaultLock.message" = "Για να αποκτήσετε πρόσβαση και να εμφανίσετε τα περιεχόμενα της κρύπτη σας, πρέπει να ξεκλειδωθεί."; +"fileprovider.error.unauthorized.text" = "Η πρόσβαση στην κρύπτη σας \"%@\" απαγορεύτηκε. Ανοίξτε την κύρια εφαρμογή για να ελέγξετε τη σύνδεσή σας και να επαληθεύσετε ξανά την ταυτότητα εάν χρειάζεται."; "fileProvider.error.unlockButton" = "Ξεκλείδωμα"; "fileProvider.clearFileFromCache.title" = "Εκκαθάριση αρχείου από την προσωρινή μνήμη"; "fileProvider.clearFileFromCache.message" = "Αυτό καταργεί μόνο το τοπικό αρχείο από τη συσκευή σας και δεν διαγράφει το αρχείο στο cloud."; diff --git a/SharedResources/en.lproj/Localizable.strings b/SharedResources/en.lproj/Localizable.strings index 5a1eb77f1..6445a8fd8 100644 --- a/SharedResources/en.lproj/Localizable.strings +++ b/SharedResources/en.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "The password that has been saved for %@ is wrong. Please try again and enter your password to re-enable %@."; "fileProvider.error.defaultLock.title" = "Unlock Required"; "fileProvider.error.defaultLock.message" = "To access and show the contents of your vault, it has to be unlocked."; +"fileprovider.error.unauthorized.text" = "Access to your vault \"%@\" was denied. Open the main app to check your connection and re-authenticate if needed."; "fileProvider.error.unlockButton" = "Unlock"; "fileProvider.clearFileFromCache.title" = "Clear File from Cache"; "fileProvider.clearFileFromCache.message" = "This only removes the local file from your device and does not delete the file in the cloud."; diff --git a/SharedResources/es.lproj/Localizable.strings b/SharedResources/es.lproj/Localizable.strings index d94a1104a..bc58e72fa 100644 --- a/SharedResources/es.lproj/Localizable.strings +++ b/SharedResources/es.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "La contraseña guardada para %@ es incorrecta. Intente de nuevo e ingrese su contraseña para volver a habilitar %@."; "fileProvider.error.defaultLock.title" = "Desbloqueo requerido"; "fileProvider.error.defaultLock.message" = "Para acceder y mostrar el contenido de su bóveda hay que desbloquearla."; +"fileprovider.error.unauthorized.text" = "Se ha denegado el acceso a tu bóveda \"%@\". Abre la aplicación principal para comprobar tu conexión y volver a autenticarte si es necesario."; "fileProvider.error.unlockButton" = "Desbloquear"; "fileProvider.clearFileFromCache.title" = "Borrar archivo de la caché"; "fileProvider.clearFileFromCache.message" = "Esto solo elimina el archivo local de su dispositivo y no lo elimina de la nube."; diff --git a/SharedResources/fa.lproj/Localizable.strings b/SharedResources/fa.lproj/Localizable.strings index 71b04e241..9b49d1bc6 100644 --- a/SharedResources/fa.lproj/Localizable.strings +++ b/SharedResources/fa.lproj/Localizable.strings @@ -15,6 +15,7 @@ "addVault.title" = "اضافه کردن گاوصندوق"; "addVault.createNewVault.title" = "ساخت گاوصندوق جدید"; "addVault.createNewVault.setVaultName.cells.name" = "نام گاوصندوق"; +"addVault.createNewVault.chooseCloud.header" = "کجا باید کریپتوماتور فایل های رمزگذاری شده گاوصندوق شما را نگهداری کند؟"; "addVault.createNewVault.password.confirmPassword.alert.message" = "مهم: اگر پسروردتان را از یاد ببرید، هیچ راهی برای بازگردانی اطلاعاتتان نیست."; "addVault.openExistingVault.title" = "باز کردن گاوصندوق موجود"; "fileProvider.error.unlockButton" = "بازکردن قفل"; diff --git a/SharedResources/fr.lproj/Localizable.strings b/SharedResources/fr.lproj/Localizable.strings index 807599568..28a28c427 100644 --- a/SharedResources/fr.lproj/Localizable.strings +++ b/SharedResources/fr.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Le mot de passe qui a été sauvegardé pour %@ est incorrect. Veuillez réessayer et entrer votre mot de passe pour réactiver %@."; "fileProvider.error.defaultLock.title" = "Déverrouillage requis"; "fileProvider.error.defaultLock.message" = "Pour accéder et afficher le contenu de votre coffre, il doit être déverrouillé."; +"fileprovider.error.unauthorized.text" = "Accès à votre coffre \"%@\" refusé. Ouvrez l'application principale pour vérifier votre connexion et réauthentifiez-vous si nécessaire."; "fileProvider.error.unlockButton" = "Déverrouiller"; "fileProvider.clearFileFromCache.title" = "Effacer le fichier du cache"; "fileProvider.clearFileFromCache.message" = "Cela ne supprime le fichier local que de votre appareil et ne supprime pas le fichier dans le cloud."; diff --git a/SharedResources/hu.lproj/Localizable.strings b/SharedResources/hu.lproj/Localizable.strings index dc87abdcc..8c56d712a 100644 --- a/SharedResources/hu.lproj/Localizable.strings +++ b/SharedResources/hu.lproj/Localizable.strings @@ -96,16 +96,24 @@ "fileProvider.onboarding.info" = "Köszönjük, hogy a Cryptomatort válaszotta fájlainak megvédéséhez. Kezdéshez menjen a fő appba és adjon hozzá egy széfet."; "fileProvider.onboarding.button.openCryptomator" = "Cryptomator megnyitása"; "fileProvider.error.biometricalAuthCanceled.title" = "Feloldás megszakítva"; +"fileProvider.error.biometricalAuthCanceled.message" = "A(z) %@ általi feloldás nem volt sikeres. Kérem, próbálja újra."; "fileProvider.error.biometricalAuthWrongPassword.title" = "Helytelen jelszó"; +"fileProvider.error.biometricalAuthWrongPassword.message" = "A(z) %@ számára mentett jelszó hibás. Kérem, próbálja újra, és adja meg a jelszavát a %@ újbóli engedélyezéséhez."; "fileProvider.error.defaultLock.title" = "Feloldás szükséges"; +"fileProvider.error.defaultLock.message" = "A széf tartalmának eléréséhez és megjelenítéséhez a széfet fel kell oldani."; +"fileprovider.error.unauthorized.text" = "A(z) „%@” széfhez való hozzáférés megtagadva. Nyissa meg a főalkalmazást a kapcsolat ellenőrzéséhez, és hitelesítse újra, ha szükséges."; "fileProvider.error.unlockButton" = "Feloldás"; "fileProvider.clearFileFromCache.title" = "Fájl törlése az átmeneti tárból"; "fileProvider.clearFileFromCache.message" = "Ez csak a helyi fájlt távolítja el az eszközről, de nem törli a fájlt a felhőben."; "fileProvider.fileImporting.error.missingPremium" = "Oldja fel a teljes verziót a Cryptomator alkalmazásban, hogy írási hozzáférést kapjon a széfjeihez."; "fileProvider.uploadProgress.connecting" = "Kapcsolódás…"; +"fileProvider.uploadProgress.message" = "Jelenlegi előrehaladás: %@\n\nHa azt észleli, hogy a feltöltési folyamat megakadt, megpróbálhatja újraindítani a feltöltést."; +"fileProvider.uploadProgress.missing" = "Az előrehaladás nem állapítható meg. Lehetséges, hogy a háttérben még fut."; "fileProvider.uploadProgress.title" = "Feltöltés…"; "fileProvider.uploadProgress.missingDomainError" = "A domain nem található."; "getFolderIntent.error.noVaultSelected" = "Nincs széf kiválasztva."; + +"hubAuthentication.title" = "Hub széf"; "hubAuthentication.accessNotGranted" = "Eszköze még nem kapott engedélyt ehhez a széfhez. Kérje a széf tulajdonosát, hogy engedélyezze a hozzáférést."; "hubAuthentication.licenseExceeded" = "Az Ön Cryptomator Hub példánya érvénytelen licenccel rendelkezik. Kérem, értesítsen egy Hub rendszergazdát hogy frissítse vagy újítsa meg a licencet."; "hubAuthentication.deviceRegistration.deviceName.cells.name" = "Készülék neve"; diff --git a/SharedResources/id.lproj/Localizable.strings b/SharedResources/id.lproj/Localizable.strings index 220d8f255..87eb92ee9 100644 --- a/SharedResources/id.lproj/Localizable.strings +++ b/SharedResources/id.lproj/Localizable.strings @@ -39,11 +39,11 @@ "accountList.signOut.alert.title" = "Hapus Vault Terkait?"; "accountList.signOut.alert.message" = "Dengan keluar, semua vault terkait akan dihapus dari daftar vault. Tidak ada data terenkripsi yang akan dihapus. Anda dapat masuk lagi dan menambahkan kembali vault nanti."; -"addVault.title" = "Tambah Vault"; +"addVault.title" = "Tambah Brankas"; "addVault.createNewVault.title" = "Buat Vault Baru"; "addVault.createNewVault.purchase" = "Membuat vault baru memerlukan Cryptomator versi lengkap."; "addVault.createNewVault.setVaultName.header.title" = "Pilih nama vault."; -"addVault.createNewVault.setVaultName.cells.name" = "Nama Vault"; +"addVault.createNewVault.setVaultName.cells.name" = "Nama Brankas"; "addVault.createNewVault.setVaultName.error.emptyVaultName" = "Nama Vault tidak boleh kosong."; "addVault.createNewVault.chooseCloud.header" = "Dimana Cryptomator seharusnya menyimpan vault berisi file enkripsi Anda?"; "addVault.createNewVault.chooseFolder.error.vaultNameCollision" = "\"%@\" sudah ada di lokasi ini. Pilih nama vault atau lokasi berbeda."; diff --git a/SharedResources/it.lproj/Localizable.strings b/SharedResources/it.lproj/Localizable.strings index 818c4139c..abd840d25 100644 --- a/SharedResources/it.lproj/Localizable.strings +++ b/SharedResources/it.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "La password salvata per %@ è errata. Riprova e inserisci la password per riabilitare %@."; "fileProvider.error.defaultLock.title" = "Richiesto Sblocco"; "fileProvider.error.defaultLock.message" = "La cassaforte deve essere sbloccata per accedere e vedere il contenuto."; +"fileprovider.error.unauthorized.text" = "L'accesso alla cassaforte \"%@\" è stato negato. Apri l'app principale per controllare la tua connessione e autenticati nuovamente, se necessario."; "fileProvider.error.unlockButton" = "Sblocca"; "fileProvider.clearFileFromCache.title" = "Cancella file dalla cache"; "fileProvider.clearFileFromCache.message" = "Questo rimuove solo il file locale dal dispositivo e non elimina il file nel cloud."; diff --git a/SharedResources/ja.lproj/Localizable.strings b/SharedResources/ja.lproj/Localizable.strings index 8f4ecf5eb..67b762d97 100644 --- a/SharedResources/ja.lproj/Localizable.strings +++ b/SharedResources/ja.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "%@ に保存されたパスワードが間違っています。 %@ を再度有効にするには、再度パスワードを入力してください。"; "fileProvider.error.defaultLock.title" = "ロック解除が必要です"; "fileProvider.error.defaultLock.message" = "保管庫の内容にアクセスして表示するには、ロックを解除する必要があります。"; +"fileprovider.error.unauthorized.text" = "金庫「%@」へのアクセスが拒否されました。メインアプリを開いて接続を確認し、必要なら再認証してください。"; "fileProvider.error.unlockButton" = "解錠"; "fileProvider.clearFileFromCache.title" = "キャッシュからファイルをクリア"; "fileProvider.clearFileFromCache.message" = "これにより、デバイスからローカルファイルが削除されるだけで、クラウド内のファイルは削除されません。"; @@ -113,10 +114,14 @@ "getFolderIntent.error.missingPath" = "パスがありません。妥当なフォルダーのパスを指定してください。"; "getFolderIntent.error.noVaultSelected" = "金庫は選択されていません。"; + +"hubAuthentication.title" = ""; "hubAuthentication.accessNotGranted" = "お使いのデバイスはまだこの金庫にアクセスする権限がありません。金庫のオーナーに権限を与えてもらってください。"; "hubAuthentication.licenseExceeded" = "Cryptomator Hub インスタンスのライセンスが無効です。ライセンスをアップグレードまたは更新するには、Hub の管理者にご連絡ください。"; "hubAuthentication.deviceRegistration.deviceName.cells.name" = "デバイス名"; +"hubAuthentication.deviceRegistration.deviceName.footer.title" = ""; "hubAuthentication.deviceRegistration.accountKey.footer.title" = "アカウントキーは新しいアプリやブラウザからログインするために必要です。プロフィール中に記載されています。"; +"hubAuthentication.deviceRegistration.needsAuthorization.alert.title" = ""; "hubAuthentication.deviceRegistration.needsAuthorization.alert.message" = "金庫にアクセスするためには,金庫のオーナーが端末を認証する必要があります。"; "hubAuthentication.requireAccountInit.alert.title" = "要対応"; "hubAuthentication.requireAccountInit.alert.message" = "続行するにはHubユーザープロフィールで必要な手順を完了してください。"; diff --git a/SharedResources/nl.lproj/Localizable.strings b/SharedResources/nl.lproj/Localizable.strings index ea47789dd..fbb960740 100644 --- a/SharedResources/nl.lproj/Localizable.strings +++ b/SharedResources/nl.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Het wachtwoord dat is opgeslagen voor %@ is verkeerd. Probeer het opnieuw en voer uw wachtwoord in om %@ opnieuw in te schakelen."; "fileProvider.error.defaultLock.title" = "Ontgrendeling vereist"; "fileProvider.error.defaultLock.message" = "Om toegang te krijgen tot de inhoud van je kluis, moet deze worden ontgrendeld."; +"fileprovider.error.unauthorized.text" = "De toegang tot uw kluis \"%@\" is geweigerd. Open de hoofdapplicatie om uw verbinding te controleren en indien nodig opnieuw te verifiëren."; "fileProvider.error.unlockButton" = "Ontgrendel"; "fileProvider.clearFileFromCache.title" = "Wissen uit cache"; "fileProvider.clearFileFromCache.message" = "Dit verwijdert alleen het lokale bestand van uw apparaat en verwijdert het bestand in de cloud niet."; diff --git a/SharedResources/no.lproj/Localizable.strings b/SharedResources/no.lproj/Localizable.strings new file mode 100644 index 000000000..e69de29bb diff --git a/SharedResources/pa.lproj/Localizable.strings b/SharedResources/pa.lproj/Localizable.strings index 53612f1c8..51e55de05 100644 --- a/SharedResources/pa.lproj/Localizable.strings +++ b/SharedResources/pa.lproj/Localizable.strings @@ -97,6 +97,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "%@ ਲਈ ਸੰਭਾਲਿਆ ਗਿਆ ਪਾਸਵਰਡ ਗਲਤ ਹੈ। ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਅਤੇ %@ ਨੂੰ ਮੁੜ-ਸਮਰੱਥ ਕਰਨ ਵਾਸਤੇ ਆਪਣਾ ਪਾਸਵਰਡ ਦਿਓ।"; "fileProvider.error.defaultLock.title" = "ਅਣ-ਲਾਕ ਕਰਨ ਦੀ ਲੋੜ ਹੈ"; "fileProvider.error.defaultLock.message" = "ਤੁਹਾਡੇ ਵਾਲਟ ਦੀ ਸਮੱਗਰੀ ਨੂੰ ਵਰਤਣ ਅਤੇ ਵੇਖਣ ਲਈ ਇਸ ਨੂੰ ਅਣ-ਲਾਕ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"; +"fileprovider.error.unauthorized.text" = "\"%@\" ਵਾਲਟ ਲਈ ਤੁਹਾਡੀ ਪਹੁੰਚ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਗਿਆ। ਆਪਣੇ ਕਨੈਕਸ਼ਨਾਂ ਦੀ ਜਾਂਚ ਕਰਨ ਅਤੇ ਜੇ ਲੋੜ ਹੋਵੇ ਤਾਂ ਮੁੜ-ਪਰਮਾਣਿਤ ਕਰਨ ਲਈ ਮੁੱਖ ਐਪ ਨੂੰ ਖੋਲ੍ਹੋ।"; "fileProvider.error.unlockButton" = "ਅਣ-ਲਾਕ ਕਰੋ"; "fileProvider.clearFileFromCache.title" = "ਕੈਸ਼ ਤੋਂ ਫਾਇਲ ਮਿਟਾਓ"; "fileProvider.clearFileFromCache.message" = "ਇਹ ਸਿਰਫ਼ ਤੁਹਾਡੇ ਡਿਵਾਈਸ ਤੋਂ ਲੋਕਲ ਫਾਇਲ ਹੀ ਹਟਾਉਂਦਾ ਹੈ ਅਤੇ ਕਲਾਉਡ ਤੋਂ ਫਾਇਲ ਨੂੰ ਨਹੀਂ ਹਟਾਉਂਦਾ ਹੈ।"; @@ -104,6 +105,7 @@ "fileProvider.uploadProgress.connecting" = "…ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"; "fileProvider.uploadProgress.title" = "…ਅੱਪਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"; "getFolderIntent.error.noVaultSelected" = "ਕੋਈ ਵਾਲਟ ਨਹੀਂ ਚੁਣਿਆ ਹੈ।"; +"hubAuthentication.deviceRegistration.deviceName.cells.name" = "ਡਿਵਾਈਸ ਦਾ ਨਾਂ"; "hubAuthentication.deviceRegistration.accountKey.footer.title" = "ਤੁਹਾਡੀ Account Key ਨਵੀਆਂ ਐਪਾਂ ਜਾਂ ਬਰਾਊਜ਼ਰਾਂ ਵਿੱਚ ਲਾਗਇਨ ਕਰਨ ਸਮੇਂ ਚਾਹੀਦੀ ਹੈ। ਇਸ ਨੂੰ ਤੁਹਾਡੇ ਪਰੋਫਾਇਲ ਵਿੱਚ ਲੱਭਿਆ ਜਾ ਸਕਦਾ ਹੈ।"; "hubAuthentication.requireAccountInit.alert.title" = "ਕਾਰਵਾਈ ਦੀ ਲੋੜ ਹੈ"; "hubAuthentication.requireAccountInit.alert.message" = "ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ Hub ਵਰਤੋਂਕਾਰ ਪਰੋਫਾਇਲ ਵਿੱਚ ਲੋੜੀਂਦੇ ਪੜਾਅ ਪੂਰੇ ਕਰੋ।"; diff --git a/SharedResources/pl.lproj/Localizable.strings b/SharedResources/pl.lproj/Localizable.strings index cb489bbfd..7c8487d77 100644 --- a/SharedResources/pl.lproj/Localizable.strings +++ b/SharedResources/pl.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Hasło zapisane dla %@ jest błędne. Spróbuj ponownie i wpisz hasło, aby ponownie włączyć %@."; "fileProvider.error.defaultLock.title" = "Wymagane odblokowanie"; "fileProvider.error.defaultLock.message" = "Sejf musi być odblokowany aby zobaczyć i mieć dostęp do jego zawartości."; +"fileprovider.error.unauthorized.text" = "Dostęp do Twojego sejfu \"%@\" został odrzucony. Otwórz aplikację, aby sprawdzić połączenie i w razie konieczności ponownie się uwierzytelnić."; "fileProvider.error.unlockButton" = "Odblokuj"; "fileProvider.clearFileFromCache.title" = "Usuń plik z pamięci podręcznej"; "fileProvider.clearFileFromCache.message" = "Powoduje to jedynie usunięcie pliku lokalnego z urządzenia, a nie usunięcie pliku w chmurze."; diff --git a/SharedResources/pt.lproj/Localizable.strings b/SharedResources/pt.lproj/Localizable.strings index 768d62a3d..35f56530f 100644 --- a/SharedResources/pt.lproj/Localizable.strings +++ b/SharedResources/pt.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "A senha que foi salva para %@ está errada. Por favor, tente novamente e digite sua senha para reativar %@."; "fileProvider.error.defaultLock.title" = "Desbloqueio necessário"; "fileProvider.error.defaultLock.message" = "Para aceder e mostrar o conteúdo do seu cofre, ele tem de estar desbloqueado."; +"fileprovider.error.unauthorized.text" = "O acesso ao seu cofre \"%@\" foi negado. Abra a aplicação principal para verificar a sua ligação e autentique-se de novo, se necessário."; "fileProvider.error.unlockButton" = "Desbloquear"; "fileProvider.clearFileFromCache.title" = "Limpar Arquivo de Cache"; "fileProvider.clearFileFromCache.message" = "Isto apenas remove o ficheiro local do dispositivo e não o apaga na nuvem."; diff --git a/SharedResources/ru.lproj/Localizable.strings b/SharedResources/ru.lproj/Localizable.strings index ab803903f..c006e6656 100644 --- a/SharedResources/ru.lproj/Localizable.strings +++ b/SharedResources/ru.lproj/Localizable.strings @@ -30,7 +30,7 @@ "common.cells.openInFilesApp" = "Открыть в \"Файлах\""; "common.cells.password" = "Пароль"; "common.cells.url" = "URL"; -"common.cells.username" = "Имя пользователя"; +"common.cells.username" = "Логин"; "common.footer.learnMore" = "Подробнее."; "common.hud.authenticating" = "Аутентификация…"; @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Сохранённый пароль для %@ неверный. Попробуйте ещё раз и введите пароль для активации %@."; "fileProvider.error.defaultLock.title" = "Требуется разблокировка"; "fileProvider.error.defaultLock.message" = "Хранилище должно быть разблокировано, чтобы получить доступ к его содержимому."; +"fileprovider.error.unauthorized.text" = "Отказано в доступе к хранилищу \"%@\". Откройте главное приложение, чтобы проверить соединение и при необходимости заново авторизоваться."; "fileProvider.error.unlockButton" = "Разблокировать"; "fileProvider.clearFileFromCache.title" = "Удалить файл из кэша"; "fileProvider.clearFileFromCache.message" = "Эта операция удаляет только локальный файл с вашего устройства, но не из облака."; @@ -225,7 +226,7 @@ "s3Authentication.displayName" = "Отображаемое имя"; "s3Authentication.accessKey" = "Ключ доступа"; "s3Authentication.secretKey" = "Секретный ключ"; -"s3Authentication.existingBucket" = "Существующий бакет"; +"s3Authentication.existingBucket" = "Имеющийся бакет"; "s3Authentication.endpoint" = "Конечная точка"; "s3Authentication.region" = "Регион"; "s3Authentication.error.invalidCredentials" = "Неверные учётные данные."; diff --git a/SharedResources/sk.lproj/Localizable.strings b/SharedResources/sk.lproj/Localizable.strings index c39c398e9..7bb0625b5 100644 --- a/SharedResources/sk.lproj/Localizable.strings +++ b/SharedResources/sk.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Heslo ktoré bolo uložené pre %@ je nesprávne. Prosím skúste znovu a vložte Vaše heslo pre znovu povolenie %@."; "fileProvider.error.defaultLock.title" = "Odomknutie požadované"; "fileProvider.error.defaultLock.message" = "K prístupu a videniu obsahu Vášho trezora, musí byť odomknutý."; +"fileprovider.error.unauthorized.text" = "Pristúp k Vášmu trezoru „%@\" bol zamietnutý. Otvorte hlavnú aplikáciu pre kontrolu pripojenia a v prípade potreby znovu sa autentizujte."; "fileProvider.error.unlockButton" = "Odomknúť"; "fileProvider.clearFileFromCache.title" = "Vymazať súbor z Cache"; "fileProvider.clearFileFromCache.message" = "Toto odstráni lokálny súbor iba z Vašeho zariadenia a neodstráni súbor v cloude."; diff --git a/SharedResources/sv.lproj/Localizable.strings b/SharedResources/sv.lproj/Localizable.strings index d6b537e0e..5c1329420 100644 --- a/SharedResources/sv.lproj/Localizable.strings +++ b/SharedResources/sv.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "Lösenordet som har sparats för %@ är felaktigt. Försök igen och ange ditt lösenord för att återaktivera %@."; "fileProvider.error.defaultLock.title" = "Upplåsning krävs"; "fileProvider.error.defaultLock.message" = "För att komma åt och visa innehållet i ditt valv måste det låsas upp."; +"fileprovider.error.unauthorized.text" = "Åtkomst till ditt valv \"%@\" nekades. Öppna huvudappen för att kontrollera din anslutning och återautentisera om det behövs."; "fileProvider.error.unlockButton" = "Lås upp"; "fileProvider.clearFileFromCache.title" = "Ta bort fil från buffert"; "fileProvider.clearFileFromCache.message" = "Detta tar bara bort den lokala filen från din enhet och tar inte bort filen i molnet."; diff --git a/SharedResources/th.lproj/Localizable.strings b/SharedResources/th.lproj/Localizable.strings index 2b7fe343a..34e765761 100644 --- a/SharedResources/th.lproj/Localizable.strings +++ b/SharedResources/th.lproj/Localizable.strings @@ -1,14 +1,207 @@ +/* + Localizable.strings + Cryptomator + + Copyright © 2021 Skymatic GmbH. All rights reserved. +*/ + +"common.alert.error.title" = "ข้อผิดพลาด"; +"common.alert.attention.title" = "โปรดทราบ"; "common.button.cancel" = "ยกเลิก"; "common.button.change" = "เปลี่ยน"; +"common.button.choose" = "เลือก"; +"common.button.clear" = "ล้าง"; "common.button.close" = "ปิด"; +"common.button.confirm" = "ยืนยัน"; +"common.button.create" = "สร้าง"; +"common.button.createFolder" = "สร้างโฟลเดอร์"; "common.button.done" = "เสร็จสิ้น"; +"common.button.download" = "ดาวน์โหลด"; +"common.button.edit" = "แก้ไข"; +"common.button.enable" = "เปิดใช้งาน"; "common.button.next" = "ถัดไป"; +"common.button.ok" = "ตกลง"; +"common.button.refresh" = "รีเฟรช"; +"common.button.register" = "ลงทะเบียน"; +"common.button.remove" = "ลบ"; +"common.button.retry" = "ลอง​ใหม่"; +"common.button.signOut" = "ลงชื่อออก"; +"common.button.verify" = "ยืนยัน"; +"common.cells.openInFilesApp" = "เปิดในแอปไฟล์"; +"common.cells.password" = "รหัสผ่าน"; +"common.cells.url" = "URL"; +"common.cells.username" = "ชื่อผู้ใช้"; +"common.footer.learnMore" = "เรียนรู้เพิ่มเติม"; +"common.hud.authenticating" = "กำลังตรวจสอบ…"; + +"accountList.header.title" = "การรับรอง"; +"accountList.emptyList.message" = "แตะที่นี่เพื่อเพิ่มบัญชี"; "addVault.title" = "เพิ่ม Vault"; "addVault.createNewVault.title" = "สร้าง Vault ใหม่"; +"addVault.createNewVault.setVaultName.header.title" = "เลือกชื่อให้ Vault"; "addVault.createNewVault.setVaultName.cells.name" = "ชื่อ Vault"; +"addVault.createNewVault.setVaultName.error.emptyVaultName" = "ชื่อ vault ไม่สามารถเว้นว่างได้"; "addVault.createNewVault.chooseCloud.header" = "Cryptomator ควรเก็บไฟล์ของคุณไว้ที่ไหน?"; +"addVault.createNewVault.password.enterPassword.header" = "ป้อนรหัสผ่านใหม่ของคุณ"; +"addVault.createNewVault.password.confirmPassword.header" = "ป้อนรหัสผ่านใหม่ของคุณอีกครั้ง"; +"addVault.createNewVault.password.confirmPassword.alert.title" = "ยืนยันรหัสผ่าน?"; +"addVault.createNewVault.password.error.emptyPassword" = "รหัสผ่านไม่สามารถเว้นว่างได้"; +"addVault.createNewVault.password.error.nonMatchingPasswords" = "รหัสผ่านไม่ตรงกัน"; +"addVault.createNewVault.password.error.tooShortPassword" = "รหัสผ่านต้องมีตัวอักษรอย่างน้อย 8 ตัว"; +"addVault.createNewVault.progress" = "กำลังสร้าง Vault…"; "addVault.openExistingVault.title" = "เปิด Vault"; +"addVault.openExistingVault.detectedMasterkey.add" = "เพิ่ม Vault นี้"; +"addVault.openExistingVault.downloadVault.progress" = "กำลังดาวน์โหลด Vault…"; +"addVault.openExistingVault.password.footer" = "ใส่รหัสผ่านสำหรับ \"%@\""; +"addVault.openExistingVault.progress" = "กำลังเพิ่ม Vault…"; + +"biometryType.faceID" = "Face ID"; +"biometryType.touchID" = "Touch ID"; + +"changePassword.error.invalidOldPassword" = "รหัสผ่านปัจจุบันผิด กรุญาลองใหม่อีกครั้ง"; +"changePassword.header.currentPassword.title" = "ใส่รหัสผ่านปัจจุบัน"; +"changePassword.header.newPassword.title" = "ป้อนรหัสผ่านใหม่ของคุณ"; +"changePassword.header.newPasswordConfirmation.title" = "ป้อนรหัสผ่านใหม่ของคุณอีกครั้ง"; +"changePassword.progress" = "กำลังเปลี่ยนรหัสผ่าน…"; + +"chooseFolder.emptyFolder.footer" = "โฟลเดอร์นี้ว่างเปล่า"; +"chooseFolder.createNewFolder.header.title" = "เลือกชื่อให้โฟลเดอร์"; +"chooseFolder.createNewFolder.cells.name" = "ชื่อโฟลเดอร์"; +"chooseFolder.createNewFolder.error.emptyFolderName" = "ชื่อโฟลเดอร์ไม่สามารถเว้นว่างได้"; +"chooseFolder.createNewFolder.progress" = "กำลังสร้างโฟลเดอร์…"; + +"cloudProvider.error.itemNotFound" = "ไม่พบ \"%@\""; +"cloudProvider.error.itemAlreadyExists" = "\"%@\" มีอยู่แล้ว"; +"cloudProvider.error.quotaInsufficient" = "คุณมีพื้นที่ว่างไม่เพียงพอ"; + +"cloudProviderType.localFileSystem" = "ผู้ให้บริการอื่น"; + +"fileProvider.onboarding.title" = "ยินดีต้อนรับ"; +"fileProvider.onboarding.button.openCryptomator" = "เปิด Cryptomator"; +"fileProvider.error.biometricalAuthCanceled.title" = "การปลดล็อกถูกยกเลิก"; +"fileProvider.error.biometricalAuthWrongPassword.title" = "รหัสผ่านไม่ถูกต้อง"; +"fileProvider.error.defaultLock.title" = "ต้องปลดล็อก"; "fileProvider.error.unlockButton" = "ปลดล็อก"; +"fileProvider.clearFileFromCache.title" = "ล้างไฟล์จากแคช"; +"fileProvider.uploadProgress.connecting" = "เชื่อมต่อ..."; +"fileProvider.uploadProgress.title" = "กำลังอัปโหลด..."; +"fileProvider.uploadProgress.missingDomainError" = "ไม่พบโดเมน"; + +"hubAuthentication.title" = "Hub Vault"; +"hubAuthentication.accessNotGranted" = "อุปกรณ์ของคุณยังไม่ได้รับการอนุญาตให้เข้าถึง vault นี้ โปรดขอให้เจ้าของ vault อนุญาต"; +"hubAuthentication.licenseExceeded" = "ระบบ Cryptomator Hub ของคุณมีสิทธิ์การใช้ไม่ถูกต้อง โปรดแจ้งผู้ดูแล Hub เพื่ออัปเกรดหรือต่ออายุสิทธิ์การใช้"; +"hubAuthentication.deviceRegistration.deviceName.cells.name" = "ชื่ออุปกรณ์"; +"hubAuthentication.deviceRegistration.needsAuthorization.alert.title" = "ลงทะเบียนอุปกรณ์สำเร็จ"; +"hubAuthentication.requireAccountInit.alert.title" = "โปรดดำเนินการ"; +"hubAuthentication.requireAccountInit.alert.actionButton" = "ไปยังโปร์ไฟล์"; + +"keepUnlocked.alert.title" = "ล็อก Vault?"; +"keepUnlocked.alert.confirm" = "ยืนยันและล็อกทันที"; +"keepUnlocked.footer.on" = "เมื่อใช้ตัวเลือกที่เลือกไว้ จะต้องเก็บสำเนาของกุญแจของคุณไว้ในพวงกุญแจ iOS ตราบใดที่ vault ถูกปลดล็อก"; +"keepUnlockedDuration.auto.shortDisplayName" = "อัตโนมัติ"; +"keepUnlockedDuration.indefinite" = "ไม่มีกำหนด"; +"localFileSystemAuthentication.createNewVault.button" = "เลือกตำแหน่งพื้นที่จัดเก็บ"; +"localFileSystemAuthentication.openExistingVault.button" = "เลือกโฟลเดอร์ Vault"; + +"onboarding.title" = "ยินดีต้อนรับ"; +"onboarding.button.continue" = "ดำเนินการต่อ"; + +"purchase.beginFreeTrial.alert.title" = "ปลดล็อกการทดลองการใช้งานแล้ว"; +"purchase.expiredTrial" = "ระยะเวลาทดลองใช้งานของคุณหมดอายุแล้ว"; +"purchase.footer.privacyPolicy" = "นโยบายความเป็นส่วนตัว"; +"purchase.footer.termsOfUse" = "เงื่อนไขการใช้งาน"; +"purchase.header.feature.familySharing" = "การแชร์กันในครอบครัว"; +"purchase.header.feature.openSource" = "การพัฒนาแบบ Open-source"; +"purchase.product.donateAndUpgrade" = "บริจาค & อัปเกรด"; +"purchase.product.freeUpgrade" = "อัปเกรดฟรี"; +"purchase.product.lifetimeLicense" = "สิทธิ์ใช้งานตลอดชีพ"; +"purchase.product.lifetimeLicense.duration" = "ครั้งเดียว"; +"purchase.product.pricing.free" = "ฟรี"; +"purchase.product.trial" = "ทดลองใช้ 30 วัน"; +"purchase.product.trial.expirationDate" = "วันหมดอายุ: %@"; +"purchase.product.trial.duration" = "30 วัน"; +"purchase.product.yearlySubscription" = "สมาชิกรายปี"; +"purchase.product.yearlySubscription.duration" = "รายปี"; +"purchase.readOnlyMode.alert.title" = "โหมดอ่านอย่างเดียว"; +"purchase.restorePurchase.button" = "กู้คืนการซื้อ"; +"purchase.restorePurchase.validTrialFound.alert.title" = "การทดลองใช้งานดำเนินการต่อ"; +"purchase.restorePurchase.fullVersionFound.alert.title" = "การกู้คืนสำเร็จ"; +"purchase.restorePurchase.fullVersionNotFound.alert.title" = "ไม่มีเวอร์ชันเต็ม"; +"purchase.restorePurchase.eligibleForUpgrade.alert.title" = "สามารถอัปเกรดได้"; +"purchase.retry.button" = "ลอง​ใหม่"; +"purchase.title" = "ปลดล็อกเวอร์ชั่นเต็ม"; +"purchase.unlockedFullVersion.title" = "ขอบคุณ"; + +"settings.title" = "การตั้งค่า"; +"settings.aboutCryptomator" = "เกี่ยวกับ Cryptomator"; +"settings.aboutCryptomator.title" = "เวอร์ชัน %@ (%@)"; +"settings.cacheSize" = "ขนาดแคช"; +"settings.clearCache" = "ล้างแคช"; +"settings.cloudServices" = "บริการคลาวด์"; +"settings.contact" = "ติดต่อ"; +"settings.debugMode" = "โหมดดีบัก"; +"settings.manageSubscriptions" = "จัดการการสมัครสมาชิก"; +"settings.rateApp" = "ให้คะแนนแอป"; +"settings.sendLogFile" = "ส่งไฟล์บันทึก"; +"settings.shortcutsGuide" = "คู่มือทางลัด"; +"settings.unlockFullVersion" = "ปลดล็อกเวอร์ชั่นเต็ม"; + +"snapshots.fileprovider.file1" = "/การบัญชี.numbers"; +"snapshots.fileprovider.file2" = "/การนำเสนอครั้งสุดท้าย.key"; +"snapshots.fileprovider.file3" = "/ตัวอย่างผลิตภัณฑ์.mov"; +"snapshots.fileprovider.file4" = "/ข้อเสนอ.docx"; +"snapshots.fileprovider.file5" = "/รายงาน.pdf"; +"snapshots.fileprovider.folder3" = "/โปรเจ็คลับ"; +"snapshots.fileprovider.folder2" = "/ใบแจ้งหนี้"; +"snapshots.main.vault1" = "/งาน"; +"snapshots.main.vault2" = "/ครอบครัว"; +"snapshots.main.vault3" = "/เอกสาร"; +"snapshots.main.vault4" = "/การเดินทางไปแคลิฟอร์เนีย"; + +"s3Authentication.displayName" = "ชื่อที่ใช้แสดง"; +"s3Authentication.accessKey" = "Access Key"; +"s3Authentication.secretKey" = "Secret key"; +"s3Authentication.endpoint" = "Endpoint"; +"s3Authentication.region" = "ภูมิภาค"; + +"trialStatus.active" = "ใช้งานอยู่"; +"trialStatus.expired" = "หมดอายุ"; "unlockVault.button.unlock" = "ปลดล็อก"; +"unlockVault.password.footer" = "ใส่รหัสผ่านสำหรับ \"%@\""; +"unlockVault.enableBiometricalUnlock.switch" = "เปิดใช้งาน %@"; +"unlockVault.evaluatePolicy.reason" = "ปลดล็อกก vault ของคุณ"; +"unlockVault.progress" = "กำลังปลดล็อค..."; + +"untrustedTLSCertificate.title" = "TLS Certificate ไม่ถูกต้อง"; +"untrustedTLSCertificate.add" = "เชื่อถือ"; +"untrustedTLSCertificate.dismiss" = "ไม่เชื่อถือ"; + +"upgrade.title" = "ข้อเสนอการอัปเกรด"; +"upgrade.notEligible.alert.title" = "การอัปเกรดล้มเหลว"; + +"vaultDetail.button.changeVaultPassword" = "เปลี่ยนรหัสผ่าน"; +"vaultDetail.button.lock" = "ล็อกตอนนี้"; +"vaultDetail.button.moveVault" = "ย้าย"; +"vaultDetail.button.removeVault" = "ลบออกจากรายชื่อ Vault"; +"vaultDetail.button.renameVault" = "เปลี่ยนชื่อ"; +"vaultDetail.disabledBiometricalUnlock.footer" = "หากคุณเปิดใช้งาน %@ รหัสผ่าน vault ของคุณจะถูกเก็บไว้ในพวงกุญแจ iOS"; +"vaultDetail.info.footer.accountInfo" = "เข้าสู่ระบบในฐานะ %@ ผ่าน %@"; +"vaultDetail.keepUnlocked.title" = "ระยะเวลาในการปลดล็อก"; +"vaultDetail.locked.footer" = "Vault ของคุณถูกล็อกอยู่"; +"vaultDetail.moveVault.progress" = "กำลังย้าย…"; +"vaultDetail.removeVault.footer" = "ขั้นตอนนี้เพียงลบ vault ออกจากรายการทั้งหมด แต่ไม่ลบไฟล์ที่ถูกเข้ารหัสใดๆ"; +"vaultDetail.renameVault.progress" = "กำลังเปลี่ยนชื่อ…"; +"vaultDetail.unlockVault.footer" = "กรอกรหัสผ่านสำหรับ \"%@\" เพื่อเก็บไว้ในพวงกุญแจ iOS และเปิดใช้งาน %@"; + +"vaultList.header.title" = "Vaults"; +"vaultList.emptyList.message" = "แตะที่นี่เพื่อเพิ่ม vault"; +"vaultList.remove.alert.title" = "ลบ Vault?"; + +"webDAVAuthentication.httpConnection.alert.title" = "ใช้ HTTPS?"; +"webDAVAuthentication.httpConnection.change" = "เปลี่ยนเป็น HTTPS"; +"webDAVAuthentication.httpConnection.continue" = "ใช้ HTTPS"; + +"Retry Upload" = "ลองอัปโหลดอีกครั้ง"; +"Clear from Cache" = "ล้างจากแคช"; diff --git a/SharedResources/tr.lproj/Localizable.strings b/SharedResources/tr.lproj/Localizable.strings index b2f1c4e2f..7e627aaf6 100644 --- a/SharedResources/tr.lproj/Localizable.strings +++ b/SharedResources/tr.lproj/Localizable.strings @@ -117,7 +117,7 @@ "hubAuthentication.title" = "Hub Kasası"; "hubAuthentication.accessNotGranted" = "Cihazınıza henüz bu kasaya erişim yetkisi verilmedi. Kasa sahibinden yetkilendirmesini isteyin."; "hubAuthentication.licenseExceeded" = "Cryptomator Hub örneğinizde geçersiz bir lisans var. Lisansı yükseltmesi veya yenilemesi için lütfen bir Hub yöneticisini bilgilendirin."; -"hubAuthentication.deviceRegistration.deviceName.cells.name" = "Cihaz adı"; +"hubAuthentication.deviceRegistration.deviceName.cells.name" = "Cihaz Adı"; "hubAuthentication.deviceRegistration.deviceName.footer.title" = "Bu cihazdan ilk Hub erişimi gibi görünüyor. Erişim yetkilendirmesini tanımlamak için bu cihazı isimlendirmeniz gerekir."; "hubAuthentication.deviceRegistration.accountKey.footer.title" = "Yeni uygulamalardan veya tarayıcılardan giriş yapmak için Hesap Anahtarınız gereklidir. Profilinizde bulunabilir."; "hubAuthentication.deviceRegistration.needsAuthorization.alert.title" = "Cihaz Kaydı Başarılı"; @@ -225,7 +225,7 @@ "s3Authentication.displayName" = "Görünen Ad"; "s3Authentication.accessKey" = "Erişim Anahtarı"; "s3Authentication.secretKey" = "Gizli Anahtar"; -"s3Authentication.existingBucket" = "Mevcut kova"; +"s3Authentication.existingBucket" = "Mevcut Kova"; "s3Authentication.endpoint" = "Uç nokta"; "s3Authentication.region" = "Bölge"; "s3Authentication.error.invalidCredentials" = "Geçersiz kimlik bilgileri."; diff --git a/SharedResources/ug.lproj/Localizable.strings b/SharedResources/ug.lproj/Localizable.strings new file mode 100644 index 000000000..3da84fa43 --- /dev/null +++ b/SharedResources/ug.lproj/Localizable.strings @@ -0,0 +1,18 @@ +"common.button.cancel" = "بىكار قىل"; +"common.button.change" = "ئۆزگەرت"; +"common.button.close" = "ياپ"; +"common.button.done" = "تامام"; +"common.button.next" = "كېيىنكى"; +"common.cells.password" = "پارول"; + +"addVault.title" = "ئامبار قوش"; +"addVault.createNewVault.setVaultName.cells.name" = "ئامبار ئىسمى"; +"addVault.createNewVault.chooseCloud.header" = "Cryptomator ئامبىرىڭىزنىڭ شىفىرلانغان ھۆججەتلىرىنى قەيەردە ساقلىشى كېرەك?"; +"fileProvider.error.unlockButton" = "قۇلۇپنى ئاچ"; +"hubAuthentication.accessNotGranted" = "ئۈسكۈنىڭىز تېخى بۇ ئامبارغا كىرىشكە ھوقۇقلاندۇرۇلمىغان. ئامبار ئىگىسىدىن ھوقۇقلاندۇرۇشنى تەلەپ قىلىڭ."; +"hubAuthentication.licenseExceeded" = "سىزنىڭ Cryptomator Hub نۇسخىڭىزنىڭ ئىناۋەتسىز ئىجازەتنامىسى بار. Hub باشقۇرغۇچىسىغا ئىجازەتنامىنى يۇقىرى دەرىجىگە كۆتۈر."; +"hubAuthentication.deviceRegistration.deviceName.cells.name" = "ئۈسكۈنە ئىسمى"; + +"unlockVault.button.unlock" = "قۇلۇپنى ئاچ"; + +"vaultDetail.button.changeVaultPassword" = "پارولنى ئۆزگەرتىش"; diff --git a/SharedResources/zh-Hans.lproj/Localizable.strings b/SharedResources/zh-Hans.lproj/Localizable.strings index 1cbfd61fb..dc88e98f4 100644 --- a/SharedResources/zh-Hans.lproj/Localizable.strings +++ b/SharedResources/zh-Hans.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "已保存至 %@ 的密码不正确,请重新输入您的密码以重新启用 %@。"; "fileProvider.error.defaultLock.title" = "需要解锁"; "fileProvider.error.defaultLock.message" = "需解锁才能访问和显示您的保险库中的内容。"; +"fileprovider.error.unauthorized.text" = "无法访问您的密码库 \"%@\" 。打开主应用程序以检查您的连接,并在需要时重新验证。"; "fileProvider.error.unlockButton" = "解锁"; "fileProvider.clearFileFromCache.title" = "清除缓存文件"; "fileProvider.clearFileFromCache.message" = "该操作仅会删除设备中的本地文件,不会删除云存储中的文件"; diff --git a/SharedResources/zh-Hant.lproj/Localizable.strings b/SharedResources/zh-Hant.lproj/Localizable.strings index 92da4dca3..0a7db6f86 100644 --- a/SharedResources/zh-Hant.lproj/Localizable.strings +++ b/SharedResources/zh-Hant.lproj/Localizable.strings @@ -101,6 +101,7 @@ "fileProvider.error.biometricalAuthWrongPassword.message" = "保存在%@的密碼錯誤,請重試並輸入你的密碼來重新啟用%@"; "fileProvider.error.defaultLock.title" = "需要解鎖"; "fileProvider.error.defaultLock.message" = "要存取加密庫和顯示其內容,需要先解鎖。"; +"fileprovider.error.unauthorized.text" = "存取檔案庫%@被拒絕。請開啟主程式並確認您的網路連線以重新取得授權。"; "fileProvider.error.unlockButton" = "解鎖"; "fileProvider.clearFileFromCache.title" = "清除快取檔䅁"; "fileProvider.clearFileFromCache.message" = "這只會從您的本機上刪除文件,而不會刪除雲端的文件。"; diff --git a/fastlane/.env.freemium b/fastlane/.env.freemium index 0cabe60da..a93f4e649 100644 --- a/fastlane/.env.freemium +++ b/fastlane/.env.freemium @@ -1,5 +1,7 @@ APP_GROUP=group.org.cryptomator.ios -APP_ICON_FILE_PATH=config/freemium/Icon.png +APP_ICON_DARK_FILE_PATH=config/freemium/DarkIcon.png +APP_ICON_LIGHT_FILE_PATH=config/freemium/LightIcon.png +APP_ICON_TINTED_FILE_PATH=config/freemium/TintedIcon.png BUNDLE_IDENTIFIER=org.cryptomator.ios CONFIG_NAME=freemium DISPLAY_NAME=Cryptomator diff --git a/fastlane/.env.premium b/fastlane/.env.premium index 1159f8eff..170e4de01 100644 --- a/fastlane/.env.premium +++ b/fastlane/.env.premium @@ -1,5 +1,7 @@ APP_GROUP=group.org.cryptomator.ios.premium -APP_ICON_FILE_PATH=config/premium/Icon.png +APP_ICON_DARK_FILE_PATH=config/premium/DarkIcon.png +APP_ICON_LIGHT_FILE_PATH=config/premium/LightIcon.png +APP_ICON_TINTED_FILE_PATH=config/premium/TintedIcon.png BUNDLE_IDENTIFIER=org.cryptomator.ios.premium CONFIG_NAME=premium DISPLAY_NAME=Cryptomator diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 0fc8c2dd9..df015d3f5 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -40,7 +40,7 @@ platform :ios do desc "Requires --env to be set to 'freemium' or 'premium'" lane :beta do ensure_env_vars( - env_vars: ['APP_GROUP', 'APP_ICON_FILE_PATH', 'BUNDLE_IDENTIFIER', 'DISPLAY_NAME', 'ENCRYPTION_EXPORT_COMPLIANCE_CODE', 'KEYCHAIN_GROUP', 'METADATA_PATH'] + env_vars: ['APP_GROUP', 'APP_ICON_DARK_FILE_PATH', 'APP_ICON_LIGHT_FILE_PATH', 'APP_ICON_TINTED_FILE_PATH', 'BUNDLE_IDENTIFIER', 'DISPLAY_NAME', 'ENCRYPTION_EXPORT_COMPLIANCE_CODE', 'KEYCHAIN_GROUP', 'METADATA_PATH'] ) apply_config scan @@ -77,7 +77,7 @@ platform :ios do private_lane :_app_store do |options| Dotenv.overload(options[:env_file]) ensure_env_vars( - env_vars: ['APP_GROUP', 'APP_ICON_FILE_PATH', 'BUNDLE_IDENTIFIER', 'DISPLAY_NAME', 'ENCRYPTION_EXPORT_COMPLIANCE_CODE', 'KEYCHAIN_GROUP', 'METADATA_PATH'] + env_vars: ['APP_GROUP', 'APP_ICON_DARK_FILE_PATH', 'APP_ICON_LIGHT_FILE_PATH', 'APP_ICON_TINTED_FILE_PATH', 'BUNDLE_IDENTIFIER', 'DISPLAY_NAME', 'ENCRYPTION_EXPORT_COMPLIANCE_CODE', 'KEYCHAIN_GROUP', 'METADATA_PATH'] ) apply_config scan @@ -89,14 +89,16 @@ platform :ios do desc "Requires --env to be set to 'freemium' or 'premium'" lane :apply_config do ensure_env_vars( - env_vars: ['APP_GROUP', 'APP_ICON_FILE_PATH', 'BUNDLE_IDENTIFIER', 'CONFIG_NAME', 'DISPLAY_NAME', 'KEYCHAIN_GROUP'] + env_vars: ['APP_GROUP', 'APP_ICON_DARK_FILE_PATH', 'APP_ICON_LIGHT_FILE_PATH', 'APP_ICON_TINTED_FILE_PATH', 'BUNDLE_IDENTIFIER', 'CONFIG_NAME', 'DISPLAY_NAME', 'KEYCHAIN_GROUP'] ) config_name = ENV['CONFIG_NAME'] bundle_identifier = ENV['BUNDLE_IDENTIFIER'] display_name = ENV['DISPLAY_NAME'] app_group = ENV['APP_GROUP'] keychain_group = ENV['KEYCHAIN_GROUP'] - app_icon_file_path = ENV['APP_ICON_FILE_PATH'] + app_icon_light_file_path = ENV['APP_ICON_LIGHT_FILE_PATH'] + app_icon_dark_file_path = ENV['APP_ICON_DARK_FILE_PATH'] + app_icon_tinted_file_path = ENV['APP_ICON_TINTED_FILE_PATH'] encryption_export_compliance_code = ENV['ENCRYPTION_EXPORT_COMPLIANCE_CODE'] # Update the app identifier @@ -265,7 +267,7 @@ platform :ios do sh("./scripts/create-app-constants.sh #{bundle_identifier}") # Update the app icon - sh("./scripts/create-app-icon.sh #{app_icon_file_path}") + sh("./scripts/create-app-icon.sh #{app_icon_light_file_path} #{app_icon_dark_file_path} #{app_icon_tinted_file_path}") # Update the cloud access secrets file sh("./scripts/create-cloud-access-secrets.sh #{config_name}") diff --git a/fastlane/changelog.txt b/fastlane/changelog.txt index 9b6abc4c5..e3fdadb74 100644 --- a/fastlane/changelog.txt +++ b/fastlane/changelog.txt @@ -1,4 +1,3 @@ -- Introducing support for Box, allowing secure access to vaults stored on Box (#319, #352). -- Added Slovenian and Ukrainian translations. -- Fixed an issue that caused "Unauthorized" errors when uploading files to OneDrive (#358). -- Removed the "Change Password" option for Hub vaults (#360, #367). \ No newline at end of file +- Added app icon variants for dark mode and tinted mode (#385). +- Introduced a clearer error screen when access to a vault is denied and a re-authentication is required (#18, #384). +- Fixed missing lock icon in the vaults list (#346, #386). \ No newline at end of file diff --git a/fastlane/config/freemium/DarkIcon.png b/fastlane/config/freemium/DarkIcon.png new file mode 100644 index 000000000..2dc3b69ba Binary files /dev/null and b/fastlane/config/freemium/DarkIcon.png differ diff --git a/fastlane/config/freemium/Icon.png b/fastlane/config/freemium/LightIcon.png similarity index 100% rename from fastlane/config/freemium/Icon.png rename to fastlane/config/freemium/LightIcon.png diff --git a/fastlane/config/freemium/TintedIcon.png b/fastlane/config/freemium/TintedIcon.png new file mode 100644 index 000000000..8ab243204 Binary files /dev/null and b/fastlane/config/freemium/TintedIcon.png differ diff --git a/fastlane/config/freemium/metadata/de-DE/release_notes.txt b/fastlane/config/freemium/metadata/de-DE/release_notes.txt index 7396aafeb..da0c1b2d3 100644 --- a/fastlane/config/freemium/metadata/de-DE/release_notes.txt +++ b/fastlane/config/freemium/metadata/de-DE/release_notes.txt @@ -1,4 +1,3 @@ -- Einführung der Unterstützung für Box, um sicheren Zugriff auf in Box gespeicherte Tresore zu ermöglichen (#319, #352). -- Übersetzungen ins Slowenische und Ukrainische hinzugefügt. -- Fehler behoben, der beim Hochladen von Dateien auf OneDrive die Fehlermeldung „Unautorisiert“ verursachte (#358). -- Option „Passwort ändern“ für Hub-Tresore entfernt (#360, #367). \ No newline at end of file +- Varianten von App-Icons für den dunklen und den eingefärbten Modus hinzugefügt (#385). +- Verständlicheren Fehlerdialog eingeführt, wenn der Zugriff auf einen Tresor verweigert wird und eine erneute Authentifizierung erforderlich ist (#18, #384). +- Fehlendes Schloss-Symbol in der Tresorliste behoben (#346, #386). \ No newline at end of file diff --git a/fastlane/config/freemium/metadata/en-US/release_notes.txt b/fastlane/config/freemium/metadata/en-US/release_notes.txt index 9b6abc4c5..e3fdadb74 100644 --- a/fastlane/config/freemium/metadata/en-US/release_notes.txt +++ b/fastlane/config/freemium/metadata/en-US/release_notes.txt @@ -1,4 +1,3 @@ -- Introducing support for Box, allowing secure access to vaults stored on Box (#319, #352). -- Added Slovenian and Ukrainian translations. -- Fixed an issue that caused "Unauthorized" errors when uploading files to OneDrive (#358). -- Removed the "Change Password" option for Hub vaults (#360, #367). \ No newline at end of file +- Added app icon variants for dark mode and tinted mode (#385). +- Introduced a clearer error screen when access to a vault is denied and a re-authentication is required (#18, #384). +- Fixed missing lock icon in the vaults list (#346, #386). \ No newline at end of file diff --git a/fastlane/config/premium/DarkIcon.png b/fastlane/config/premium/DarkIcon.png new file mode 100644 index 000000000..1354d3bd9 Binary files /dev/null and b/fastlane/config/premium/DarkIcon.png differ diff --git a/fastlane/config/premium/Icon.png b/fastlane/config/premium/Icon.png deleted file mode 100644 index 520b18c34..000000000 Binary files a/fastlane/config/premium/Icon.png and /dev/null differ diff --git a/fastlane/config/premium/LightIcon.png b/fastlane/config/premium/LightIcon.png new file mode 100644 index 000000000..b04de8e9c Binary files /dev/null and b/fastlane/config/premium/LightIcon.png differ diff --git a/fastlane/config/premium/TintedIcon.png b/fastlane/config/premium/TintedIcon.png new file mode 100644 index 000000000..22c52ba09 Binary files /dev/null and b/fastlane/config/premium/TintedIcon.png differ diff --git a/fastlane/config/premium/metadata/de-DE/release_notes.txt b/fastlane/config/premium/metadata/de-DE/release_notes.txt index 7396aafeb..da0c1b2d3 100644 --- a/fastlane/config/premium/metadata/de-DE/release_notes.txt +++ b/fastlane/config/premium/metadata/de-DE/release_notes.txt @@ -1,4 +1,3 @@ -- Einführung der Unterstützung für Box, um sicheren Zugriff auf in Box gespeicherte Tresore zu ermöglichen (#319, #352). -- Übersetzungen ins Slowenische und Ukrainische hinzugefügt. -- Fehler behoben, der beim Hochladen von Dateien auf OneDrive die Fehlermeldung „Unautorisiert“ verursachte (#358). -- Option „Passwort ändern“ für Hub-Tresore entfernt (#360, #367). \ No newline at end of file +- Varianten von App-Icons für den dunklen und den eingefärbten Modus hinzugefügt (#385). +- Verständlicheren Fehlerdialog eingeführt, wenn der Zugriff auf einen Tresor verweigert wird und eine erneute Authentifizierung erforderlich ist (#18, #384). +- Fehlendes Schloss-Symbol in der Tresorliste behoben (#346, #386). \ No newline at end of file diff --git a/fastlane/config/premium/metadata/en-US/release_notes.txt b/fastlane/config/premium/metadata/en-US/release_notes.txt index 9b6abc4c5..e3fdadb74 100644 --- a/fastlane/config/premium/metadata/en-US/release_notes.txt +++ b/fastlane/config/premium/metadata/en-US/release_notes.txt @@ -1,4 +1,3 @@ -- Introducing support for Box, allowing secure access to vaults stored on Box (#319, #352). -- Added Slovenian and Ukrainian translations. -- Fixed an issue that caused "Unauthorized" errors when uploading files to OneDrive (#358). -- Removed the "Change Password" option for Hub vaults (#360, #367). \ No newline at end of file +- Added app icon variants for dark mode and tinted mode (#385). +- Introduced a clearer error screen when access to a vault is denied and a re-authentication is required (#18, #384). +- Fixed missing lock icon in the vaults list (#346, #386). \ No newline at end of file diff --git a/fastlane/scripts/create-app-icon.sh b/fastlane/scripts/create-app-icon.sh index 048c543ab..9f16a78de 100755 --- a/fastlane/scripts/create-app-icon.sh +++ b/fastlane/scripts/create-app-icon.sh @@ -1,4 +1,6 @@ #!/bin/sh # Path is relative to the fastlane folder -cp "${1}" ../SharedResources/Assets.xcassets/AppIcon.appiconset/Icon.png +cp "${1}" ../SharedResources/Assets.xcassets/AppIcon.appiconset/LightIcon.png +cp "${2}" ../SharedResources/Assets.xcassets/AppIcon.appiconset/DarkIcon.png +cp "${3}" ../SharedResources/Assets.xcassets/AppIcon.appiconset/TintedIcon.png