feat: Reset recovery key flow

This commit is contained in:
Christian Kußowski
2026-06-25 13:06:09 +02:00
parent 1cdd13db0f
commit 4773a4a25d
7 changed files with 54 additions and 22 deletions
@@ -48,5 +48,17 @@ extension on FluffyChatTester {
// Restore with passphrase:
await ensureLoggedIn();
// Reset passphrase
await tapOn(Key('accounts_and_settings_buttons'));
await tapOn('Settings');
await tapOn('Chat backup');
await tapOn('Reset recovery key');
await enterText(TextField, passphrase1, index: 0);
await enterText(TextField, passphrase1, index: 1);
await tapOn('Continue', pumpAndSettle: false);
await waitFor('You are ready to start!');
await tapOn('Continue');
AuthFlows.userPassphrases[user1Name] = passphrase1;
}
}
@@ -4,9 +4,9 @@ FLUTTER_FRAMEWORK_SWIFT_PACKAGE_PATH=/Users/christianpauly/develop/fluffychat/io
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=/Users/christianpauly/develop/fluffychat/lib/main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=2.6.0
FLUTTER_BUILD_NUMBER=3553
DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuNDQuMQ==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049OTI0MTM0YTQ0Yw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YzQxNmFjZmViOA==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMi4x
FLUTTER_BUILD_NAME=2.7.0
FLUTTER_BUILD_NUMBER=3554
DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuNDQuMg==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049YzlhNmM0ODQyMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NzdlMmU5NDc3Mg==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMi4y
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
+5 -2
View File
@@ -110,8 +110,11 @@ abstract class AppRoutes {
GoRoute(
path: '/backup',
redirect: loggedOutRedirect,
pageBuilder: (context, state) =>
defaultPageBuilder(context, state, BootstrapPage()),
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
BootstrapPage(reset: state.uri.queryParameters['reset'] == 'true'),
),
),
ShellRoute(
// Never use a transition on the shell route. Changing the PageBuilder
+2 -1
View File
@@ -2887,5 +2887,6 @@
"uploading": "Uploading",
"edited": "(edited)",
"couldNotBeSent": "Could not be sent",
"yesterday": "Yesterday"
"yesterday": "Yesterday",
"resetRecoveryKey": "Reset recovery key"
}
+16 -12
View File
@@ -16,16 +16,18 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class BootstrapPage extends StatelessWidget {
const BootstrapPage({super.key});
final bool reset;
const BootstrapPage({required this.reset, super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return ViewModelBuilder(
create: () => BootstrapViewModel(client: Matrix.of(context).client),
create: () =>
BootstrapViewModel(client: Matrix.of(context).client, reset: reset),
builder: (context, viewModel, _) {
final cryptoIdentityState = viewModel.value.cryptoIdentityState;
if (cryptoIdentityState?.connected == true) {
if (cryptoIdentityState?.connected == true && !reset) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => viewModel.goToRoomsPageAfterSuccess(context),
);
@@ -48,15 +50,17 @@ class BootstrapPage extends StatelessWidget {
? null
: CloseButton(
onPressed: () async {
final consent = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).skipChatBackup,
message: L10n.of(context).skipChatBackupWarning,
okLabel: L10n.of(context).skip,
isDestructive: true,
);
if (consent != OkCancelResult.ok) return;
if (!context.mounted) return;
if (!reset) {
final consent = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).skipChatBackup,
message: L10n.of(context).skipChatBackupWarning,
okLabel: L10n.of(context).skip,
isDestructive: true,
);
if (consent != OkCancelResult.ok) return;
if (!context.mounted) return;
}
context.go('/rooms');
},
),
@@ -19,6 +19,7 @@ import 'bootstrap_state.dart';
class BootstrapViewModel extends ValueNotifier<BootstrapViewModelState> {
final Client client;
final bool reset;
final TextEditingController enterPassphraseOrRecovController =
TextEditingController();
@@ -26,8 +27,8 @@ class BootstrapViewModel extends ValueNotifier<BootstrapViewModelState> {
final TextEditingController repeatPassphraseController =
TextEditingController();
BootstrapViewModel({required this.client})
: super(BootstrapViewModelState()) {
BootstrapViewModel({required this.client, required this.reset})
: super(BootstrapViewModelState()..reset = reset) {
_init();
}
@@ -146,6 +147,12 @@ class BootstrapViewModel extends ValueNotifier<BootstrapViewModelState> {
try {
value.recoveryKey = await client.initCryptoIdentity(
passphrase: passphrase,
wipeCrossSigning: !reset,
wipeKeyBackup: !reset,
wipeSecureStorage: !reset,
setupMasterKey: !reset,
setupSelfSigningKey: !reset,
setupUserSigningKey: !reset,
);
} catch (e, s) {
if (!context.mounted) return;
+7 -2
View File
@@ -193,12 +193,17 @@ class SettingsController extends State<Settings> {
Future<void> firstRunBootstrapAction([_]) async {
if (cryptoIdentityConnected == true) {
showOkAlertDialog(
final action = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).chatBackup,
message: L10n.of(context).onlineKeyBackupEnabled,
okLabel: L10n.of(context).close,
okLabel: L10n.of(context).resetRecoveryKey,
cancelLabel: L10n.of(context).close,
isDestructive: true,
);
if (action != OkCancelResult.ok) return;
if (!mounted) return;
await context.push('/backup?reset=true');
return;
}
await context.push('/backup');