mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 03:49:22 +00:00
enable and disable auto back up
This commit is contained in:
parent
61f945aa98
commit
cc779be460
3 changed files with 415 additions and 18 deletions
|
@ -2,15 +2,27 @@ import 'package:flutter/gestures.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/enable_backup_dialog.dart';
|
||||
import 'package:stackwallet/providers/global/locale_provider.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../providers/global/auto_swb_service_provider.dart';
|
||||
import '../../../../widgets/custom_buttons/blue_text_button.dart';
|
||||
|
||||
class BackupRestoreSettings extends ConsumerStatefulWidget {
|
||||
const BackupRestoreSettings({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -24,6 +36,49 @@ class BackupRestoreSettings extends ConsumerStatefulWidget {
|
|||
class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
|
||||
late bool createBackup = false;
|
||||
late bool restoreBackup = false;
|
||||
// late bool isEnabledAutoBackup;
|
||||
|
||||
final toggleController = DSBController();
|
||||
|
||||
late final TextEditingController fileLocationController;
|
||||
late final TextEditingController passwordController;
|
||||
late final TextEditingController frequencyController;
|
||||
|
||||
late final FocusNode fileLocationFocusNode;
|
||||
late final FocusNode passwordFocusNode;
|
||||
|
||||
String prettySinceLastBackupString(DateTime? time) {
|
||||
if (time == null) {
|
||||
return "-";
|
||||
}
|
||||
final difference = DateTime.now().difference(time);
|
||||
int value;
|
||||
String postfix;
|
||||
if (difference < const Duration(seconds: 60)) {
|
||||
value = difference.inSeconds;
|
||||
postfix = "seconds";
|
||||
} else if (difference < const Duration(minutes: 60)) {
|
||||
value = difference.inMinutes;
|
||||
postfix = "minutes";
|
||||
} else if (difference < const Duration(hours: 24)) {
|
||||
value = difference.inHours;
|
||||
postfix = "hours";
|
||||
} else if (difference.inDays < 8) {
|
||||
value = difference.inDays;
|
||||
postfix = "days";
|
||||
} else {
|
||||
// if greater than a week return the actual date
|
||||
return DateFormat.yMMMMd(
|
||||
ref.read(localeServiceChangeNotifierProvider).locale)
|
||||
.format(time);
|
||||
}
|
||||
|
||||
if (value == 1) {
|
||||
postfix = postfix.substring(0, postfix.length - 1);
|
||||
}
|
||||
|
||||
return "$value $postfix ago";
|
||||
}
|
||||
|
||||
Future<void> enableAutoBackup(BuildContext context) async {
|
||||
await showDialog<dynamic>(
|
||||
|
@ -36,10 +91,105 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> attemptDisable() async {
|
||||
final result = await showDialog<bool?>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return StackDialog(
|
||||
title: "Disable Auto Backup",
|
||||
message:
|
||||
"You are turning off Auto Backup. You can turn it back on at any time. Your previous Auto Backup file will not be deleted. Remember to backup your wallets manually so you don't lose important information.",
|
||||
leftButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Back",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
rightButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Disable",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
ref.watch(prefsChangeNotifierProvider).isAutoBackupEnabled =
|
||||
false;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
if (mounted) {
|
||||
if (result is bool && result) {
|
||||
ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled = false;
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
toggleController.activate?.call();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
fileLocationController = TextEditingController();
|
||||
passwordController = TextEditingController();
|
||||
frequencyController = TextEditingController();
|
||||
|
||||
passwordController.text = "---------------";
|
||||
fileLocationController.text =
|
||||
ref.read(prefsChangeNotifierProvider).autoBackupLocation ?? " ";
|
||||
frequencyController.text = Format.prettyFrequencyType(
|
||||
ref.read(prefsChangeNotifierProvider).backupFrequencyType);
|
||||
|
||||
fileLocationFocusNode = FocusNode();
|
||||
passwordFocusNode = FocusNode();
|
||||
|
||||
// _toggle = ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
fileLocationController.dispose();
|
||||
passwordController.dispose();
|
||||
frequencyController.dispose();
|
||||
|
||||
fileLocationFocusNode.dispose();
|
||||
passwordFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
bool isEnabledAutoBackup = ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.isAutoBackupEnabled));
|
||||
|
||||
ref.listen(
|
||||
prefsChangeNotifierProvider
|
||||
.select((value) => value.backupFrequencyType),
|
||||
(previous, BackupFrequencyType next) {
|
||||
frequencyController.text = Format.prettyFrequencyType(next);
|
||||
});
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
|
@ -120,16 +270,79 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(
|
||||
10,
|
||||
),
|
||||
child: PrimaryButton(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: !isEnabledAutoBackup
|
||||
? PrimaryButton(
|
||||
desktopMed: true,
|
||||
width: 200,
|
||||
label: "Enable auto backup",
|
||||
onPressed: () {
|
||||
enableAutoBackup(context);
|
||||
},
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 403,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Backed up ${prettySinceLastBackupString(ref.watch(prefsChangeNotifierProvider.select((value) => value.lastAutoBackup)))}",
|
||||
style: STextStyles
|
||||
.itemSubtitle(
|
||||
context),
|
||||
),
|
||||
BlueTextButton(
|
||||
text: "Back up now",
|
||||
onTap: () {
|
||||
ref
|
||||
.read(
|
||||
autoSWBServiceProvider)
|
||||
.doBackup();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
PrimaryButton(
|
||||
desktopMed: true,
|
||||
width: 190,
|
||||
label: "Disable auto backup",
|
||||
onPressed: () {
|
||||
attemptDisable();
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
SecondaryButton(
|
||||
desktopMed: true,
|
||||
width: 190,
|
||||
label: "Edit auto backup",
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/enums/log_level_enum.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
@ -19,11 +28,19 @@ import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
|||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/progress_bar.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
class CreateAutoBackup extends ConsumerStatefulWidget {
|
||||
const CreateAutoBackup({Key? key}) : super(key: key);
|
||||
const CreateAutoBackup({
|
||||
Key? key,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
ConsumerState<CreateAutoBackup> createState() => _CreateAutoBackup();
|
||||
|
@ -34,6 +51,8 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
late final TextEditingController passphraseController;
|
||||
late final TextEditingController passphraseRepeatController;
|
||||
|
||||
late final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
late final StackFileSystem stackFileSystem;
|
||||
late final FocusNode passphraseFocusNode;
|
||||
late final FocusNode passphraseRepeatFocusNode;
|
||||
|
@ -66,6 +85,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
secureStore = widget.secureStore;
|
||||
stackFileSystem = StackFileSystem();
|
||||
|
||||
fileLocationController = TextEditingController();
|
||||
|
@ -569,7 +589,163 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
child: PrimaryButton(
|
||||
label: "Enable Auto Backup",
|
||||
enabled: shouldEnableCreate,
|
||||
onPressed: () {},
|
||||
onPressed: !shouldEnableCreate
|
||||
? null
|
||||
: () async {
|
||||
final String pathToSave =
|
||||
fileLocationController.text;
|
||||
final String passphrase = passphraseController.text;
|
||||
final String repeatPassphrase =
|
||||
passphraseRepeatController.text;
|
||||
|
||||
if (pathToSave.isEmpty) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Directory not chosen",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!(await Directory(pathToSave).exists())) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Directory does not exist",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (passphrase.isEmpty) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "A passphrase is required",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (passphrase != repeatPassphrase) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Passphrase does not match",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackDialog(
|
||||
title: "Encrypting initial backup",
|
||||
message: "This shouldn't take long",
|
||||
),
|
||||
);
|
||||
|
||||
// make sure the dialog is able to be displayed for at least some time
|
||||
final fut = Future<void>.delayed(
|
||||
const Duration(milliseconds: 300));
|
||||
|
||||
String adkString;
|
||||
int adkVersion;
|
||||
try {
|
||||
final adk =
|
||||
await compute(generateAdk, passphrase);
|
||||
adkString = Format.uint8listToString(adk.item2);
|
||||
adkVersion = adk.item1;
|
||||
} on Exception catch (e, s) {
|
||||
String err = getErrorMessageFromSWBException(e);
|
||||
Logging.instance
|
||||
.log("$err\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: err,
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "$e",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await secureStore.write(
|
||||
key: "auto_adk_string", value: adkString);
|
||||
await secureStore.write(
|
||||
key: "auto_adk_version_string",
|
||||
value: adkVersion.toString());
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final String fileToSave =
|
||||
createAutoBackupFilename(pathToSave, now);
|
||||
|
||||
final backup = await SWB.createStackWalletJSON();
|
||||
|
||||
bool result = await SWB.encryptStackWalletWithADK(
|
||||
fileToSave,
|
||||
adkString,
|
||||
jsonEncode(backup),
|
||||
adkVersion: adkVersion,
|
||||
);
|
||||
|
||||
// this future should already be complete unless there was an error encrypting
|
||||
await Future.wait([fut]);
|
||||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
int count = 0;
|
||||
Navigator.of(context)
|
||||
.popUntil((_) => count++ >= 2);
|
||||
|
||||
if (result) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.autoBackupLocation = pathToSave;
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.lastAutoBackup = now;
|
||||
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.isAutoBackupEnabled = true;
|
||||
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup enabled and saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title: "Stack Auto Backup enabled!"),
|
||||
);
|
||||
if (mounted) {
|
||||
passphraseController.text = "";
|
||||
passphraseRepeatController.text = "";
|
||||
|
||||
int count = 0;
|
||||
Navigator.of(context)
|
||||
.popUntil((_) => count++ >= 2);
|
||||
}
|
||||
} else {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackOkDialog(
|
||||
title: "Failed to enable Auto Backup"),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class StackDialogBase extends StatelessWidget {
|
||||
const StackDialogBase({
|
||||
|
@ -17,7 +18,8 @@ class StackDialogBase extends StatelessWidget {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisAlignment:
|
||||
!Util.isDesktop ? MainAxisAlignment.end : MainAxisAlignment.center,
|
||||
children: [
|
||||
Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
@ -179,9 +181,15 @@ class StackOkDialog extends StatelessWidget {
|
|||
),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
onPressed: !Util.isDesktop
|
||||
? () {
|
||||
Navigator.of(context).pop();
|
||||
onOkPressed?.call("OK");
|
||||
}
|
||||
: () {
|
||||
int count = 0;
|
||||
Navigator.of(context).popUntil((_) => count++ >= 2);
|
||||
// onOkPressed?.call("OK");
|
||||
},
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
|
Loading…
Reference in a new issue