Skip to content

Commit 482112a

Browse files
Improve Hub error handling and updates (#404)
Co-authored-by: Tobias Hagemann <[email protected]>
1 parent 6b3e79a commit 482112a

8 files changed

+49
-31
lines changed

Cryptomator/VaultDetail/UnlockSectionFooterViewModel.swift

+8-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// Copyright © 2021 Skymatic GmbH. All rights reserved.
77
//
88

9+
import CryptomatorCloudAccessCore
910
import CryptomatorCommonCore
1011
import Foundation
1112

@@ -31,21 +32,23 @@ class UnlockSectionFooterViewModel: HeaderFooterViewModel {
3132
}
3233

3334
var biometryTypeName: String?
35+
var vaultInfo: VaultInfo
3436

35-
init(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration) {
37+
init(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration, vaultInfo: VaultInfo) {
3638
self.vaultUnlocked = vaultUnlocked
3739
self.biometricalUnlockEnabled = biometricalUnlockEnabled
3840
self.biometryTypeName = biometryTypeName
39-
let titleText = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration)
41+
let titleText = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration, vaultInfo: vaultInfo)
4042
self.title = Bindable(titleText)
4143
self.keepUnlockedDuration = keepUnlockedDuration
44+
self.vaultInfo = vaultInfo
4245
}
4346

4447
private func updateTitle() {
45-
title.value = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration)
48+
title.value = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration, vaultInfo: vaultInfo)
4649
}
4750

48-
private static func getTitleText(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration) -> String {
51+
private static func getTitleText(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration, vaultInfo: VaultInfo) -> String {
4952
let unlockedText: String
5053
if vaultUnlocked {
5154
unlockedText = LocalizedString.getValue("vaultDetail.unlocked.footer")
@@ -62,7 +65,7 @@ class UnlockSectionFooterViewModel: HeaderFooterViewModel {
6265
keepUnlockedText = String(format: LocalizedString.getValue("vaultDetail.keepUnlocked.footer.limitedDuration"), keepUnlockedDuration.description ?? "")
6366
}
6467
var footerText = "\(unlockedText)\n\n\(keepUnlockedText)"
65-
if let biometryTypeName = biometryTypeName {
68+
if vaultInfo.vaultConfigType != .hub, let biometryTypeName = biometryTypeName {
6669
let biometricalUnlockText: String
6770
if biometricalUnlockEnabled {
6871
biometricalUnlockText = String(format: LocalizedString.getValue("vaultDetail.enabledBiometricalUnlock.footer"), biometryTypeName)

Cryptomator/VaultDetail/VaultDetailViewModel.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {
121121

122122
private var lockSectionCells: [BindableTableViewCellViewModel] {
123123
var cells: [BindableTableViewCellViewModel] = [lockButton, keepUnlockedCellViewModel]
124-
if let biometryTypeName = context.enrolledBiometricsAuthenticationName() {
124+
if vaultInfo.vaultConfigType != .hub, let biometryTypeName = context.enrolledBiometricsAuthenticationName() {
125125
let switchCellViewModel = getSwitchCellViewModel(biometryTypeName: biometryTypeName)
126126
cells.append(switchCellViewModel)
127127
}
@@ -146,7 +146,7 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {
146146
.lockingSection: unlockSectionFooterViewModel,
147147
.removeVaultSection: BaseHeaderFooterViewModel(title: LocalizedString.getValue("vaultDetail.removeVault.footer"))]
148148

149-
private lazy var unlockSectionFooterViewModel = UnlockSectionFooterViewModel(vaultUnlocked: vaultInfo.vaultIsUnlocked.value, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: context.enrolledBiometricsAuthenticationName(), keepUnlockedDuration: currentKeepUnlockedDuration.value)
149+
private lazy var unlockSectionFooterViewModel = UnlockSectionFooterViewModel(vaultUnlocked: vaultInfo.vaultIsUnlocked.value, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: context.enrolledBiometricsAuthenticationName(), keepUnlockedDuration: currentKeepUnlockedDuration.value, vaultInfo: vaultInfo)
150150

151151
private lazy var vaultInfoCellViewModel = BindableTableViewCellViewModel(title: vaultInfo.vaultName, detailTitle: vaultInfo.vaultPath.path, detailTitleTextColor: .secondaryLabel, image: UIImage(vaultIconFor: vaultInfo.cloudProviderType, state: .normal), selectionStyle: .none)
152152
private lazy var renameVaultCellViewModel = ButtonCellViewModel.createDisclosureButton(action: VaultDetailButtonAction.showRenameVault, title: LocalizedString.getValue("vaultDetail.button.renameVault"), detailTitle: vaultName)

CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/CryptomatorHubAuthenticator.swift

+10-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public enum HubAuthenticationFlow {
2020
case needsDeviceRegistration
2121
case licenseExceeded
2222
case requiresAccountInitialization(at: URL)
23+
case vaultArchived
2324
}
2425

2526
public struct HubAuthenticationFlowSuccess {
@@ -48,6 +49,7 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
4849

4950
public init() {}
5051

52+
// swiftlint:disable:next cyclomatic_complexity
5153
public func receiveKey(authState: OIDAuthState, vaultConfig: UnverifiedVaultConfig) async throws -> HubAuthenticationFlow {
5254
guard let hubConfig = vaultConfig.allegedHubConfig, let vaultBaseURL = getVaultBaseURL(from: vaultConfig) else {
5355
throw CryptomatorHubAuthenticatorError.invalidVaultConfig
@@ -79,6 +81,8 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
7981
return .requiresAccountInitialization(at: profileURL)
8082
case .legacyHubVersion:
8183
throw CryptomatorHubAuthenticatorError.incompatibleHubVersion
84+
case .vaultArchived:
85+
return .vaultArchived
8286
}
8387

8488
let retrieveUserPrivateKeyResponse = try await getUserKey(apiBaseURL: apiBaseURL, authState: authState)
@@ -240,8 +244,10 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
240244
return .success(encryptedVaultKey: body, header: httpResponse?.allHeaderFields ?? [:])
241245
case 402:
242246
return .licenseExceeded
243-
case 403, 410:
247+
case 403:
244248
return .accessNotGranted
249+
case 410:
250+
return .vaultArchived
245251
case 404:
246252
return .legacyHubVersion
247253
case 449:
@@ -297,14 +303,16 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
297303
private enum RetrieveVaultMasterkeyEncryptedForUserResponse {
298304
// 200
299305
case success(encryptedVaultKey: String, header: [AnyHashable: Any])
300-
// 403, 410
306+
// 403
301307
case accessNotGranted
302308
// 402
303309
case licenseExceeded
304310
// 449
305311
case requiresAccountInitialization(at: URL)
306312
// 404
307313
case legacyHubVersion
314+
// 410
315+
case vaultArchived
308316
}
309317

310318
private struct DeviceDto: Codable {

CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubAuthenticationView.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ public struct HubAuthenticationView: View {
2020
onRegisterTap: { Task { await viewModel.register() }}
2121
)
2222
case .accessNotGranted:
23-
HubAccessNotGrantedView(onRefresh: { Task { await viewModel.refresh() }})
23+
CryptomatorErrorWithRefreshView(headerTitle: LocalizedString.getValue("hubAuthentication.accessNotGranted"), onRefresh: { Task { await viewModel.refresh() }})
24+
case .vaultArchived:
25+
CryptomatorErrorWithRefreshView(headerTitle: LocalizedString.getValue("hubAuthentication.vaultArchived"), onRefresh: { Task { await viewModel.refresh() }})
2426
case .licenseExceeded:
2527
CryptomatorErrorView(text: LocalizedString.getValue("hubAuthentication.licenseExceeded"))
2628
case let .error(description):

CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubAuthenticationViewModel.swift

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public final class HubAuthenticationViewModel: ObservableObject {
3232
case licenseExceeded
3333
case deviceRegistration(DeviceRegistration)
3434
case error(description: String)
35+
case vaultArchived
3536
}
3637

3738
public enum DeviceRegistration: Equatable {
@@ -108,6 +109,8 @@ public final class HubAuthenticationViewModel: ObservableObject {
108109
await setState(to: .licenseExceeded)
109110
case let .requiresAccountInitialization(profileURL):
110111
await delegate?.hubAuthenticationViewModelWantsToShowNeedsAccountInitAlert(profileURL: profileURL)
112+
case .vaultArchived:
113+
await setState(to: .vaultArchived)
111114
}
112115
}
113116

CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubDeviceRegisteredSuccessfullyView.swift

-19
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import SwiftUI
2+
3+
struct CryptomatorErrorWithRefreshView: View {
4+
var headerTitle: String
5+
var onRefresh: () -> Void
6+
7+
var body: some View {
8+
CryptomatorSimpleButtonView(
9+
buttonTitle: LocalizedString.getValue("common.button.refresh"),
10+
onButtonTap: onRefresh,
11+
headerTitle: headerTitle
12+
)
13+
}
14+
}
15+
16+
struct CryptomatorErrorWithRefreshView_Previews: PreviewProvider {
17+
static var previews: some View {
18+
CryptomatorErrorWithRefreshView(headerTitle: "Example Header Title", onRefresh: {})
19+
}
20+
}

SharedResources/en.lproj/Localizable.strings

+3-2
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,17 @@
116116
"getFolderIntent.error.noVaultSelected" = "No vault has been selected.";
117117

118118
"hubAuthentication.title" = "Hub Vault";
119-
"hubAuthentication.accessNotGranted" = "Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.";
119+
"hubAuthentication.accessNotGranted" = "You do not have permission to access this vault. Ask the vault owner to authorize you.";
120120
"hubAuthentication.licenseExceeded" = "Your Cryptomator Hub instance has an invalid license. Please inform a Hub administrator to upgrade or renew the license.";
121121
"hubAuthentication.deviceRegistration.deviceName.cells.name" = "Device Name";
122122
"hubAuthentication.deviceRegistration.deviceName.footer.title" = "This seems to be the first Hub access from this device. In order to identify it for access authorization, you need to name this device.";
123123
"hubAuthentication.deviceRegistration.accountKey.footer.title" = "Your Account Key is required to login from new apps or browsers. It can be found in your profile.";
124124
"hubAuthentication.deviceRegistration.needsAuthorization.alert.title" = "Register Device Successful";
125-
"hubAuthentication.deviceRegistration.needsAuthorization.alert.message" = "To access the vault, your device needs to be authorized by the vault owner.";
125+
"hubAuthentication.deviceRegistration.needsAuthorization.alert.message" = "To access the vault, the vault owner needs to grant you permission.";
126126
"hubAuthentication.requireAccountInit.alert.title" = "Action Required";
127127
"hubAuthentication.requireAccountInit.alert.message" = "To proceed, please complete the steps required in your Hub user profile.";
128128
"hubAuthentication.requireAccountInit.alert.actionButton" = "Go to Profile";
129+
"hubAuthentication.vaultArchived" = "This vault has been archived. Please ask the vault owner to unarchive it.";
129130

130131
"intents.saveFile.missingFile" = "The provided file is not valid.";
131132
"intents.saveFile.invalidFolder" = "The provided folder is not valid.";

0 commit comments

Comments
 (0)