Skip to content

Commit

Permalink
Fix incorrect "unsaved changes" popup when leaving the security screen
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
inetic committed Sep 18, 2024
1 parent 434e3d9 commit 1569b51
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 37 deletions.
97 changes: 61 additions & 36 deletions lib/app/cubits/repo_security.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> userPrefersToStoreSecret;
final bool secureWithBiometrics;
final Option<LocalPassword> localPassword;
// This is set to the above `localPassword` once the user clicks the "UPDATE"
Expand All @@ -39,12 +40,13 @@ class RepoSecurityState {
// page.
final Option<LocalPassword> updatedLocalPassword;
final bool isBiometricsAvailable;
final bool _defaultStoreIfOriginIsManual = false;

RepoSecurityState copyWith({
LocalSecretMode? oldLocalSecretMode,
LocalSecret? oldLocalSecret,
SecretKeyOrigin? origin,
bool? store,
bool? userPrefersToStoreSecret,
bool? secureWithBiometrics,
Option<LocalPassword>? localPassword,
Option<LocalPassword>? updatedLocalPassword,
Expand All @@ -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,
Expand All @@ -75,49 +79,70 @@ 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,
(None(), SecretKeyOrigin.manual) || (_, SecretKeyOrigin.random) => null,
};

@override
String toString() => '$runtimeType(origin: $origin, store: $store, ...)';
String toString() =>
'$runtimeType(origin: $origin, userPrefersToStoreSecret: $userPrefersToStoreSecret, ...)';
}

class RepoSecurityCubit extends Cubit<RepoSecurityState> with AppLogger {
Expand All @@ -143,7 +168,7 @@ class RepoSecurityCubit extends Cubit<RepoSecurityState> with AppLogger {
}

void setStore(bool value) {
emit(state.copyWith(store: value));
emit(state.copyWith(userPrefersToStoreSecret: value));
}

void setSecureWithBiometrics(bool value) {
Expand Down
2 changes: 1 addition & 1 deletion lib/app/widgets/repo_security.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
Expand Down

0 comments on commit 1569b51

Please sign in to comment.