Skip to content

Commit

Permalink
Allow user to delete staged update file if corrupt
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyrat committed Mar 19, 2024
1 parent 488197b commit 12ee232
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
8 changes: 8 additions & 0 deletions lib/cubit/vault_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,14 @@ class VaultCubit extends Cubit<VaultState> {
l.d('vault cubit started');
}

Future<bool> hasPendingUpdateFile(User user) async {
return await _localVaultRepo.hasStagedUpdate(user);
}

Future<void> deletePendingUpdateFile(User user) async {
return await _localVaultRepo.removeStagedUpdate(user);
}

Future<void> emitVaultLoaded(LocalVaultFile vault, User? user,
{bool immediateRemoteRefresh = true, required bool safe}) async {
if (user?.subscriptionStatus == AccountSubscriptionStatus.expired ||
Expand Down
22 changes: 22 additions & 0 deletions lib/local_vault_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,28 @@ class LocalVaultRepository {
);
}

Future<void> removeStagedUpdate(User user) async {
final directory = await getStorageDirectory();
final stagedFile = File('${directory.path}/${user.idB64url}/staged.kdbx');
try {
await stagedFile.delete();
} on Exception {
// can fail if OS/hardware failure has caused the file to be already deleted but that's OK
}
}

Future<bool> hasStagedUpdate(User user) async {
final directory = await getStorageDirectory();
final stagedFile = File('${directory.path}/${user.idB64url}/staged.kdbx');
try {
final exists = await stagedFile.exists();
return exists;
} on Exception {
// maybe can fail but that's OK, just assume does not exist since are unlikely to be able to do anything with it anyway
}
return false;
}

remove(User user) async {
final directory = await getStorageDirectory();
final file = File('${directory.path}/${user.idB64url}/current.kdbx');
Expand Down
104 changes: 104 additions & 0 deletions lib/widgets/help.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:keevault/widgets/bottom.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:package_info_plus/package_info_plus.dart';

import '../config/app.dart';
import '../config/routes.dart';
import '../cubit/account_cubit.dart';
import '../cubit/vault_cubit.dart';
import '../generated/l10n.dart';

import 'coloured_safe_area_widget.dart';
import 'dialog_utils.dart';
import '../logging/logger.dart';

class HelpWidget extends StatefulWidget {
const HelpWidget({
Expand Down Expand Up @@ -124,6 +128,7 @@ class _HelpWidgetState extends State<HelpWidget> with TraceableClientMixin {
child: Text('Share / view logs'),
),
),
PendingUpdateErrorRecoveryWidget(theme: theme),
],
),
),
Expand All @@ -134,3 +139,102 @@ class _HelpWidgetState extends State<HelpWidget> with TraceableClientMixin {
);
}
}

class PendingUpdateErrorRecoveryWidget extends StatefulWidget {
const PendingUpdateErrorRecoveryWidget({
super.key,
required this.theme,
});

final ThemeData theme;

@override
State<PendingUpdateErrorRecoveryWidget> createState() => _PendingUpdateErrorRecoveryWidgetState();
}

class _PendingUpdateErrorRecoveryWidgetState extends State<PendingUpdateErrorRecoveryWidget> {
bool? _errorAndPendingUpdateKdbxExists;

@override
void initState() {
super.initState();
unawaited(_detectPendingUpdateKdbx());
}

Future<void> _detectPendingUpdateKdbx() async {
final accountCubit = BlocProvider.of<AccountCubit>(context);
final currentUser = accountCubit.currentUserIfKnown;
// Only bother if user is signed in account holder, not a free local only user
// and if we might care whether the file exists or not
final vaultCubit = BlocProvider.of<VaultCubit>(context);
if (vaultCubit.state is! VaultError || accountCubit.state is AccountLocalOnly || currentUser == null) {
setState(() {
_errorAndPendingUpdateKdbxExists = false;
});
return;
}
final pendingUpdateKdbxExists = await vaultCubit.hasPendingUpdateFile(currentUser);
setState(() {
_errorAndPendingUpdateKdbxExists = pendingUpdateKdbxExists;
});
}

@override
Widget build(BuildContext context) {
return BlocBuilder<VaultCubit, VaultState>(
builder: (context, state) {
final vc = BlocProvider.of<VaultCubit>(context);
final ac = BlocProvider.of<AccountCubit>(context);
final currentUser = ac.currentUserIfKnown;
return Visibility(
visible: _errorAndPendingUpdateKdbxExists ?? false,
child: Column(
children: [
Divider(),
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 16.0),
child: Text(
'Error recovery',
style: widget.theme.textTheme.titleLarge,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
'There is a version of your Kee Vault pending merge with your current local version. This is a rare but expected behaviour in some cases of a failed download from the internet but once any fault with your device has been resolved, Kee Vault should automatically complete the merge when you sign-in to your Kee Vault.'),
),
Text(
'If you are experiencing an error that prevents the opening of your Kee Vault because this downloaded data is in some way corrupt or incompatible with your Kee Vault on this device, the button below may help. If the underlying fault with the previous download attempt has not been resolved, the error may re-appear and in very rare circumstances you may lose data (e.g. during certain offline modifications across multiple devices).'),
Text(
'Ensure you understand the cause of your error situation and the implications of clicking the button - we recommend discussing the problem in the community forum before taking any action.'),
Padding(
padding: const EdgeInsets.all(16.0),
child: ElevatedButton(
onPressed: () async {
final proceed = await DialogUtils.showConfirmDialog(
context: context,
params: ConfirmDialogParams(
content:
'If you delete this data we will attempt to re-download the latest version shortly after you next sign-in. After deleting this data, you must use your task manager to kill Kee Vault and then start it again (or restart your device if you are unsure how to do this). Ensure that you have resolved the underlying fault before proceeding (e.g. made more disk space available on your device or repaired the hardware fault on the device) and that you have a good network connection. Are you sure you want to delete the data?',
negativeButtonText: 'Keep',
positiveButtonText: 'Delete',
title: 'Delete version of your Kee Vault data that is pending merge',
));
if (proceed) {
l.w('deletePendingUpdateFile');
await vc.deletePendingUpdateFile(currentUser!);
await _detectPendingUpdateKdbx();
}
l.i('deletePendingUpdateFile skipped by user');
},
style: ElevatedButton.styleFrom(backgroundColor: widget.theme.buttonTheme.colorScheme!.error),
child: Text('Delete pending Kee Vault data'),
),
),
],
),
);
},
);
}
}

0 comments on commit 12ee232

Please sign in to comment.