From 1569b51dd6ceaa249dfed311015b64f8f4efc1bc Mon Sep 17 00:00:00 2001 From: Peter Jankuliak Date: Wed, 18 Sep 2024 11:29:16 +0200 Subject: [PATCH] Fix incorrect "unsaved changes" popup when leaving the security screen Happened even if no changes in the security screen were made. The precondition was that the repo had the "Store password" option set to false. --- lib/app/cubits/repo_security.dart | 97 +++++++++++++++++++----------- lib/app/widgets/repo_security.dart | 2 +- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/lib/app/cubits/repo_security.dart b/lib/app/cubits/repo_security.dart index d2b1e694..d4fc82cc 100644 --- a/lib/app/cubits/repo_security.dart +++ b/lib/app/cubits/repo_security.dart @@ -17,20 +17,21 @@ class RepoSecurityState { required this.oldLocalSecretMode, required this.oldLocalSecret, SecretKeyOrigin? origin, - bool? store, + // Reflects the user's preference on storing a password for + // `LocalSecretMode`s where storing is not implicit. + this.userPrefersToStoreSecret = const None(), bool? secureWithBiometrics, this.localPassword = const None(), this.updatedLocalPassword = const None(), this.isBiometricsAvailable = false, }) : origin = origin ?? oldLocalSecretMode.origin, - store = store ?? _initialStore(oldLocalSecretMode), secureWithBiometrics = secureWithBiometrics ?? oldLocalSecretMode.store.isSecuredWithBiometrics; final LocalSecretMode oldLocalSecretMode; final LocalSecret oldLocalSecret; final SecretKeyOrigin origin; - final bool store; + final Option userPrefersToStoreSecret; final bool secureWithBiometrics; final Option localPassword; // This is set to the above `localPassword` once the user clicks the "UPDATE" @@ -39,12 +40,13 @@ class RepoSecurityState { // page. final Option updatedLocalPassword; final bool isBiometricsAvailable; + final bool _defaultStoreIfOriginIsManual = false; RepoSecurityState copyWith({ LocalSecretMode? oldLocalSecretMode, LocalSecret? oldLocalSecret, SecretKeyOrigin? origin, - bool? store, + bool? userPrefersToStoreSecret, bool? secureWithBiometrics, Option? localPassword, Option? updatedLocalPassword, @@ -54,7 +56,9 @@ class RepoSecurityState { oldLocalSecretMode: oldLocalSecretMode ?? this.oldLocalSecretMode, oldLocalSecret: oldLocalSecret ?? this.oldLocalSecret, origin: origin ?? this.origin, - store: store ?? this.store, + userPrefersToStoreSecret: userPrefersToStoreSecret != null + ? Some(userPrefersToStoreSecret) + : this.userPrefersToStoreSecret, secureWithBiometrics: secureWithBiometrics ?? this.secureWithBiometrics, localPassword: localPassword ?? this.localPassword, updatedLocalPassword: updatedLocalPassword ?? this.updatedLocalPassword, @@ -75,41 +79,61 @@ class RepoSecurityState { }; bool get isSecureWithBiometricsEnabled => - isBiometricsAvailable && (origin == SecretKeyOrigin.random || store); + isBiometricsAvailable && secretWillBeStored; bool get isValid => newLocalSecretInput != null; + bool get secretWillBeStored => + origin == SecretKeyOrigin.random || + switch (userPrefersToStoreSecret) { + Some(value: final store) => store, + None() => _defaultStoreIfOriginIsManual + }; + bool get hasPendingChanges { - return origin != oldLocalSecretMode.origin || - store != oldLocalSecretMode.store.isStored || - secureWithBiometrics != - oldLocalSecretMode.store.isSecuredWithBiometrics || - (localPassword is Some && localPassword != updatedLocalPassword); + final originChanged = origin != oldLocalSecretMode.origin; + + final localPasswordChanged = + localPassword is Some && localPassword != updatedLocalPassword; + + final storeChanged = + secretWillBeStored != oldLocalSecretMode.store.isStored; + + final biometricsChanged = secureWithBiometrics != + oldLocalSecretMode.store.isSecuredWithBiometrics; + + return originChanged || + storeChanged || + biometricsChanged || + localPasswordChanged; } - LocalSecretInput? get newLocalSecretInput => - switch ((localPassword, origin, store, secureWithBiometrics)) { - (Some(value: final password), SecretKeyOrigin.manual, false, _) => - LocalSecretManual( - password: password, - store: SecretKeyStore.notStored, - ), - (Some(value: final password), SecretKeyOrigin.manual, true, false) => - LocalSecretManual( - password: password, - store: SecretKeyStore.stored, - ), - (Some(value: final password), SecretKeyOrigin.manual, true, true) => - LocalSecretManual( - password: password, - store: SecretKeyStore.securedWithBiometrics, - ), - (None(), SecretKeyOrigin.manual, _, _) => null, - (_, SecretKeyOrigin.random, _, false) => - LocalSecretRandom(secureWithBiometrics: false), - (_, SecretKeyOrigin.random, _, true) => - LocalSecretRandom(secureWithBiometrics: true), - }; + LocalSecretInput? get newLocalSecretInput { + final willStore = secretWillBeStored; + + return switch ((localPassword, origin, willStore, secureWithBiometrics)) { + (Some(value: final password), SecretKeyOrigin.manual, false, _) => + LocalSecretManual( + password: password, + store: SecretKeyStore.notStored, + ), + (Some(value: final password), SecretKeyOrigin.manual, true, false) => + LocalSecretManual( + password: password, + store: SecretKeyStore.stored, + ), + (Some(value: final password), SecretKeyOrigin.manual, true, true) => + LocalSecretManual( + password: password, + store: SecretKeyStore.securedWithBiometrics, + ), + (None(), SecretKeyOrigin.manual, _, _) => null, + (_, SecretKeyOrigin.random, _, false) => + LocalSecretRandom(secureWithBiometrics: false), + (_, SecretKeyOrigin.random, _, true) => + LocalSecretRandom(secureWithBiometrics: true), + }; + } LocalPassword? get newLocalPassword => switch ((localPassword, origin)) { (Some(value: final value), SecretKeyOrigin.manual) => value, @@ -117,7 +141,8 @@ class RepoSecurityState { }; @override - String toString() => '$runtimeType(origin: $origin, store: $store, ...)'; + String toString() => + '$runtimeType(origin: $origin, userPrefersToStoreSecret: $userPrefersToStoreSecret, ...)'; } class RepoSecurityCubit extends Cubit with AppLogger { @@ -143,7 +168,7 @@ class RepoSecurityCubit extends Cubit with AppLogger { } void setStore(bool value) { - emit(state.copyWith(store: value)); + emit(state.copyWith(userPrefersToStoreSecret: value)); } void setSecureWithBiometrics(bool value) { diff --git a/lib/app/widgets/repo_security.dart b/lib/app/widgets/repo_security.dart index 8a53a7b4..aad05fea 100644 --- a/lib/app/widgets/repo_security.dart +++ b/lib/app/widgets/repo_security.dart @@ -62,7 +62,7 @@ class RepoSecurity extends StatelessWidget { Widget _buildStoreSwitch(RepoSecurityState state) => switch (state.origin) { SecretKeyOrigin.manual => _buildSwitch( - value: state.store, + value: state.secretWillBeStored, title: S.current.labelRememberPassword, onChanged: cubit.setStore, ),