enable and disable auto back up

This commit is contained in:
ryleedavis 2022-11-09 15:32:39 -07:00
parent 61f945aa98
commit cc779be460
3 changed files with 415 additions and 18 deletions

View file

@ -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,17 +270,80 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(
10,
),
child: PrimaryButton(
desktopMed: true,
width: 200,
label: "Enable auto backup",
onPressed: () {
enableAutoBackup(context);
},
),
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: () {},
),
],
)
],
),
),
],
),

View file

@ -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"),
);
}
}
},
),
)
],

View file

@ -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,10 +181,16 @@ class StackOkDialog extends StatelessWidget {
),
Expanded(
child: TextButton(
onPressed: () {
Navigator.of(context).pop();
onOkPressed?.call("OK");
},
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>()!
.getPrimaryEnabledButtonColor(context),