Skip to content

Commit

Permalink
Improve Hub error handling and updates
Browse files Browse the repository at this point in the history
  • Loading branch information
iammajid committed Jan 18, 2025
1 parent c3ca8a6 commit 05bddd1
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 32 deletions.
13 changes: 8 additions & 5 deletions Cryptomator/VaultDetail/UnlockSectionFooterViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import CryptomatorCommonCore
import CryptomatorCloudAccessCore
import Foundation

class UnlockSectionFooterViewModel: HeaderFooterViewModel {
Expand All @@ -31,21 +32,23 @@ class UnlockSectionFooterViewModel: HeaderFooterViewModel {
}

var biometryTypeName: String?
var vaultInfo: VaultInfo

init(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration) {
init(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration, vaultInfo: VaultInfo) {
self.vaultUnlocked = vaultUnlocked
self.biometricalUnlockEnabled = biometricalUnlockEnabled
self.biometryTypeName = biometryTypeName
let titleText = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration)
let titleText = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration, vaultInfo: vaultInfo)
self.title = Bindable(titleText)
self.keepUnlockedDuration = keepUnlockedDuration
self.vaultInfo = vaultInfo
}

private func updateTitle() {
title.value = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration)
title.value = UnlockSectionFooterViewModel.getTitleText(vaultUnlocked: vaultUnlocked, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: biometryTypeName, keepUnlockedDuration: keepUnlockedDuration, vaultInfo: vaultInfo)
}

private static func getTitleText(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration) -> String {
private static func getTitleText(vaultUnlocked: Bool, biometricalUnlockEnabled: Bool, biometryTypeName: String?, keepUnlockedDuration: KeepUnlockedDuration, vaultInfo: VaultInfo) -> String {
let unlockedText: String
if vaultUnlocked {
unlockedText = LocalizedString.getValue("vaultDetail.unlocked.footer")
Expand All @@ -62,7 +65,7 @@ class UnlockSectionFooterViewModel: HeaderFooterViewModel {
keepUnlockedText = String(format: LocalizedString.getValue("vaultDetail.keepUnlocked.footer.limitedDuration"), keepUnlockedDuration.description ?? "")
}
var footerText = "\(unlockedText)\n\n\(keepUnlockedText)"
if let biometryTypeName = biometryTypeName {
if vaultInfo.vaultConfigType != .hub, let biometryTypeName = biometryTypeName {
let biometricalUnlockText: String
if biometricalUnlockEnabled {
biometricalUnlockText = String(format: LocalizedString.getValue("vaultDetail.enabledBiometricalUnlock.footer"), biometryTypeName)
Expand Down
4 changes: 2 additions & 2 deletions Cryptomator/VaultDetail/VaultDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {

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

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

private lazy var vaultInfoCellViewModel = BindableTableViewCellViewModel(title: vaultInfo.vaultName, detailTitle: vaultInfo.vaultPath.path, detailTitleTextColor: .secondaryLabel, image: UIImage(vaultIconFor: vaultInfo.cloudProviderType, state: .normal), selectionStyle: .none)
private lazy var renameVaultCellViewModel = ButtonCellViewModel.createDisclosureButton(action: VaultDetailButtonAction.showRenameVault, title: LocalizedString.getValue("vaultDetail.button.renameVault"), detailTitle: vaultName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public enum HubAuthenticationFlow {
case needsDeviceRegistration
case licenseExceeded
case requiresAccountInitialization(at: URL)
case vaultArchived
}

public struct HubAuthenticationFlowSuccess {
Expand Down Expand Up @@ -79,7 +80,9 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
return .requiresAccountInitialization(at: profileURL)
case .legacyHubVersion:
throw CryptomatorHubAuthenticatorError.incompatibleHubVersion
}
case .vaultArchived:
return .vaultArchived
}

let retrieveUserPrivateKeyResponse = try await getUserKey(apiBaseURL: apiBaseURL, authState: authState)

Expand Down Expand Up @@ -240,8 +243,10 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
return .success(encryptedVaultKey: body, header: httpResponse?.allHeaderFields ?? [:])
case 402:
return .licenseExceeded
case 403, 410:
case 403:
return .accessNotGranted
case 410:
return .vaultArchived
case 404:
return .legacyHubVersion
case 449:
Expand Down Expand Up @@ -305,6 +310,8 @@ public class CryptomatorHubAuthenticator: HubDeviceRegistering, HubKeyReceiving
case requiresAccountInitialization(at: URL)
// 404
case legacyHubVersion
// 410
case vaultArchived
}

private struct DeviceDto: Codable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public struct HubAuthenticationView: View {
onRegisterTap: { Task { await viewModel.register() }}
)
case .accessNotGranted:
HubAccessNotGrantedView(onRefresh: { Task { await viewModel.refresh() }})
CryptomatorErrorWithRefreshView(headerTitle: LocalizedString.getValue("hubAuthentication.accessNotGranted"), onRefresh: { Task { await viewModel.refresh() }})
case .vaultArchived:
CryptomatorErrorWithRefreshView(headerTitle: LocalizedString.getValue("hubAuthentication.vaultArchived"), onRefresh: { Task { await viewModel.refresh() }})
case .licenseExceeded:
CryptomatorErrorView(text: LocalizedString.getValue("hubAuthentication.licenseExceeded"))
case let .error(description):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public final class HubAuthenticationViewModel: ObservableObject {
case licenseExceeded
case deviceRegistration(DeviceRegistration)
case error(description: String)
case vaultArchived
}

public enum DeviceRegistration: Equatable {
Expand Down Expand Up @@ -108,7 +109,9 @@ public final class HubAuthenticationViewModel: ObservableObject {
await setState(to: .licenseExceeded)
case let .requiresAccountInitialization(profileURL):
await delegate?.hubAuthenticationViewModelWantsToShowNeedsAccountInitAlert(profileURL: profileURL)
}
case .vaultArchived:
await setState(to: .vaultArchived)
}
}

private func receivedExistingKey(_ flowResponse: HubAuthenticationFlowSuccess) async {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SwiftUI

struct CryptomatorErrorWithRefreshView: View {
var headerTitle: String
var onRefresh: () -> Void

var body: some View {
CryptomatorSimpleButtonView(
buttonTitle: LocalizedString.getValue("common.button.refresh"),
onButtonTap: onRefresh,
headerTitle: headerTitle
)
}
}

struct CryptomatorErrorWithRefreshView_Previews: PreviewProvider {
static var previews: some View {
CryptomatorErrorWithRefreshView(headerTitle: "Example Header Title", onRefresh: {})
}
}
5 changes: 3 additions & 2 deletions SharedResources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,17 @@
"getFolderIntent.error.noVaultSelected" = "No vault has been selected.";

"hubAuthentication.title" = "Hub Vault";
"hubAuthentication.accessNotGranted" = "Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.";
"hubAuthentication.accessNotGranted" = "Your user has not yet been authorized to access this vault. Ask the vault owner to authorize it.";
"hubAuthentication.licenseExceeded" = "Your Cryptomator Hub instance has an invalid license. Please inform a Hub administrator to upgrade or renew the license.";
"hubAuthentication.deviceRegistration.deviceName.cells.name" = "Device Name";
"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.";
"hubAuthentication.deviceRegistration.accountKey.footer.title" = "Your Account Key is required to login from new apps or browsers. It can be found in your profile.";
"hubAuthentication.deviceRegistration.needsAuthorization.alert.title" = "Register Device Successful";
"hubAuthentication.deviceRegistration.needsAuthorization.alert.message" = "To access the vault, your device needs to be authorized by the vault owner.";
"hubAuthentication.deviceRegistration.needsAuthorization.alert.message" = "To access the vault";
"hubAuthentication.requireAccountInit.alert.title" = "Action Required";
"hubAuthentication.requireAccountInit.alert.message" = "To proceed, please complete the steps required in your Hub user profile.";
"hubAuthentication.requireAccountInit.alert.actionButton" = "Go to Profile";
"hubAuthentication.vaultArchived" = "This vault has been archived. Please ask the vault owner to unarchive it.";

"intents.saveFile.missingFile" = "The provided file is not valid.";
"intents.saveFile.invalidFolder" = "The provided folder is not valid.";
Expand Down

0 comments on commit 05bddd1

Please sign in to comment.