mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 17:57:40 +00:00
desktop edit auto swb functionality
This commit is contained in:
parent
29ec03fb87
commit
bdf1008424
3 changed files with 691 additions and 523 deletions
|
@ -1,6 +1,8 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
@ -15,6 +17,7 @@ import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.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/flush_bar_type.dart';
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
|
@ -22,7 +25,10 @@ import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.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/progress_bar.dart';
|
||||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
|
@ -51,6 +57,14 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
late final SWBFileSystem stackFileSystem;
|
late final SWBFileSystem stackFileSystem;
|
||||||
final zxcvbn = Zxcvbn();
|
final zxcvbn = Zxcvbn();
|
||||||
|
|
||||||
|
late BackupFrequencyType _currentDropDownValue;
|
||||||
|
|
||||||
|
final List<BackupFrequencyType> _dropDownItems = [
|
||||||
|
BackupFrequencyType.everyTenMinutes,
|
||||||
|
BackupFrequencyType.everyAppStart,
|
||||||
|
BackupFrequencyType.afterClosingAWallet,
|
||||||
|
];
|
||||||
|
|
||||||
String passwordFeedback =
|
String passwordFeedback =
|
||||||
"Add another word or two. Uncommon words are better. Use a few words, avoid common phrases. No need for symbols, digits, or uppercase letters.";
|
"Add another word or two. Uncommon words are better. Use a few words, avoid common phrases. No need for symbols, digits, or uppercase letters.";
|
||||||
|
|
||||||
|
@ -66,6 +80,157 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
passwordRepeatController.text.isNotEmpty;
|
passwordRepeatController.text.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onSavePressed() async {
|
||||||
|
final String pathToSave = fileLocationController.text;
|
||||||
|
final String passphrase = passwordController.text;
|
||||||
|
final String repeatPassphrase = passwordRepeatController.text;
|
||||||
|
|
||||||
|
if (pathToSave.isEmpty) {
|
||||||
|
unawaited(
|
||||||
|
showFloatingFlushBar(
|
||||||
|
type: FlushBarType.warning,
|
||||||
|
message: "Directory not chosen",
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(await Directory(pathToSave).exists())) {
|
||||||
|
unawaited(
|
||||||
|
showFloatingFlushBar(
|
||||||
|
type: FlushBarType.warning,
|
||||||
|
message: "Directory does not exist",
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (passphrase.isEmpty) {
|
||||||
|
unawaited(
|
||||||
|
showFloatingFlushBar(
|
||||||
|
type: FlushBarType.warning,
|
||||||
|
message: "A passphrase is required",
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (passphrase != repeatPassphrase) {
|
||||||
|
unawaited(
|
||||||
|
showFloatingFlushBar(
|
||||||
|
type: FlushBarType.warning,
|
||||||
|
message: "Passphrase does not match",
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unawaited(
|
||||||
|
showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (_) => const StackDialog(
|
||||||
|
title: "Updating Auto Backup",
|
||||||
|
message: "This shouldn't take long",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// make sure the dialog is able to be displayed for at least 1 second
|
||||||
|
final fut = Future<void>.delayed(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
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();
|
||||||
|
unawaited(
|
||||||
|
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();
|
||||||
|
unawaited(
|
||||||
|
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(
|
||||||
|
secureStorage: ref.read(secureStoreProvider),
|
||||||
|
);
|
||||||
|
|
||||||
|
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
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
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 saved to:",
|
||||||
|
message: fileToSave,
|
||||||
|
)
|
||||||
|
: const StackOkDialog(title: "Stack Auto Backup saved"),
|
||||||
|
);
|
||||||
|
if (mounted) {
|
||||||
|
passwordController.text = "";
|
||||||
|
passwordRepeatController.text = "";
|
||||||
|
|
||||||
|
Navigator.of(context)
|
||||||
|
.popUntil(ModalRoute.withName(AutoBackupView.routeName));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (_) =>
|
||||||
|
const StackOkDialog(title: "Failed to update Auto Backup"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
secureStore = ref.read(secureStoreProvider);
|
secureStore = ref.read(secureStoreProvider);
|
||||||
|
@ -77,6 +242,9 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
fileLocationController.text =
|
fileLocationController.text =
|
||||||
ref.read(prefsChangeNotifierProvider).autoBackupLocation ?? "";
|
ref.read(prefsChangeNotifierProvider).autoBackupLocation ?? "";
|
||||||
|
|
||||||
|
_currentDropDownValue =
|
||||||
|
ref.read(prefsChangeNotifierProvider).backupFrequencyType;
|
||||||
|
|
||||||
passwordFocusNode = FocusNode();
|
passwordFocusNode = FocusNode();
|
||||||
passwordRepeatFocusNode = FocusNode();
|
passwordRepeatFocusNode = FocusNode();
|
||||||
|
|
||||||
|
@ -110,7 +278,11 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
debugPrint("BUILD: $runtimeType");
|
debugPrint("BUILD: $runtimeType");
|
||||||
|
|
||||||
return Scaffold(
|
final isDesktop = Util.isDesktop;
|
||||||
|
|
||||||
|
return ConditionalParent(
|
||||||
|
condition: !isDesktop,
|
||||||
|
builder: (child) => Scaffold(
|
||||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: AppBarBackButton(
|
leading: AppBarBackButton(
|
||||||
|
@ -132,13 +304,30 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
minHeight: constraints.maxHeight,
|
minHeight: constraints.maxHeight,
|
||||||
),
|
),
|
||||||
child: IntrinsicHeight(
|
child: IntrinsicHeight(
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment:
|
||||||
|
isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
|
if (!isDesktop)
|
||||||
Text(
|
Text(
|
||||||
"Create your backup",
|
"Create your backup",
|
||||||
style: STextStyles.smallMed12(context),
|
style: STextStyles.smallMed12(context),
|
||||||
),
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
Text(
|
||||||
|
"Choose file location",
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
|
@ -163,8 +352,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance
|
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||||
.log("$e\n$s", level: LogLevel.Error);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
controller: fileLocationController,
|
controller: fileLocationController,
|
||||||
|
@ -193,8 +381,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
key: const Key(
|
key: const Key("createBackupSaveToFileLocationTextFieldKey"),
|
||||||
"createBackupSaveToFileLocationTextFieldKey"),
|
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
toolbarOptions: const ToolbarOptions(
|
toolbarOptions: const ToolbarOptions(
|
||||||
copy: true,
|
copy: true,
|
||||||
|
@ -204,6 +391,18 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
),
|
),
|
||||||
onChanged: (newValue) {},
|
onChanged: (newValue) {},
|
||||||
),
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
Text(
|
||||||
|
"Create a passphrase",
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
if (!Platform.isAndroid)
|
if (!Platform.isAndroid)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
|
@ -240,9 +439,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
hidePassword
|
hidePassword ? Assets.svg.eye : Assets.svg.eyeSlash,
|
||||||
? Assets.svg.eye
|
|
||||||
: Assets.svg.eyeSlash,
|
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textDark3,
|
.textDark3,
|
||||||
|
@ -266,8 +463,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
}
|
}
|
||||||
final result = zxcvbn.evaluate(newValue);
|
final result = zxcvbn.evaluate(newValue);
|
||||||
String suggestionsAndTips = "";
|
String suggestionsAndTips = "";
|
||||||
for (var sug
|
for (var sug in result.feedback.suggestions!.toSet()) {
|
||||||
in result.feedback.suggestions!.toSet()) {
|
|
||||||
suggestionsAndTips += "$sug\n";
|
suggestionsAndTips += "$sug\n";
|
||||||
}
|
}
|
||||||
suggestionsAndTips += result.feedback.warning!;
|
suggestionsAndTips += result.feedback.warning!;
|
||||||
|
@ -284,8 +480,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (feedback.endsWith("\n")) {
|
if (feedback.endsWith("\n")) {
|
||||||
feedback =
|
feedback = feedback.substring(0, feedback.length - 2);
|
||||||
feedback.substring(0, feedback.length - 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -321,12 +516,12 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
),
|
),
|
||||||
child: ProgressBar(
|
child: ProgressBar(
|
||||||
key: const Key("createStackBackUpProgressBar"),
|
key: const Key("createStackBackUpProgressBar"),
|
||||||
width: MediaQuery.of(context).size.width - 32 - 24,
|
width: isDesktop
|
||||||
|
? 492
|
||||||
|
: MediaQuery.of(context).size.width - 32 - 24,
|
||||||
height: 5,
|
height: 5,
|
||||||
fillColor: passwordStrength < 0.51
|
fillColor: passwordStrength < 0.51
|
||||||
? Theme.of(context)
|
? Theme.of(context).extension<StackColors>()!.accentColorRed
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorRed
|
|
||||||
: passwordStrength < 1
|
: passwordStrength < 1
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -337,12 +532,11 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
backgroundColor: Theme.of(context)
|
backgroundColor: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.buttonBackSecondary,
|
.buttonBackSecondary,
|
||||||
percent:
|
percent: passwordStrength < 0.25 ? 0.03 : passwordStrength,
|
||||||
passwordStrength < 0.25 ? 0.03 : passwordStrength,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
height: 10,
|
height: isDesktop ? 16 : 10,
|
||||||
),
|
),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
|
@ -376,9 +570,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
hidePassword
|
hidePassword ? Assets.svg.eye : Assets.svg.eyeSlash,
|
||||||
? Assets.svg.eye
|
|
||||||
: Assets.svg.eyeSlash,
|
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textDark3,
|
.textDark3,
|
||||||
|
@ -399,16 +591,95 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
height: 32,
|
height: isDesktop ? 24 : 32,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Auto Backup frequency",
|
"Auto Backup frequency",
|
||||||
style: STextStyles.smallMed12(context),
|
style: isDesktop
|
||||||
|
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<StackColors>()!.textDark3,
|
||||||
|
)
|
||||||
|
: STextStyles.smallMed12(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton2(
|
||||||
|
offset: const Offset(0, -10),
|
||||||
|
isExpanded: true,
|
||||||
|
dropdownElevation: 0,
|
||||||
|
value: _currentDropDownValue,
|
||||||
|
items: [
|
||||||
|
..._dropDownItems.map(
|
||||||
|
(e) {
|
||||||
|
String message = "";
|
||||||
|
switch (e) {
|
||||||
|
case BackupFrequencyType.everyTenMinutes:
|
||||||
|
message = "Every 10 minutes";
|
||||||
|
break;
|
||||||
|
case BackupFrequencyType.everyAppStart:
|
||||||
|
message = "Every app startup";
|
||||||
|
break;
|
||||||
|
case BackupFrequencyType.afterClosingAWallet:
|
||||||
|
message = "After closing a cryptocurrency wallet";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DropdownMenuItem(
|
||||||
|
value: e,
|
||||||
|
child: Text(message),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value is BackupFrequencyType) {
|
||||||
|
if (ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.backupFrequencyType !=
|
||||||
|
value) {
|
||||||
|
ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.backupFrequencyType = value;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_currentDropDownValue = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
Assets.svg.chevronDown,
|
||||||
|
width: 10,
|
||||||
|
height: 5,
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||||
|
),
|
||||||
|
buttonPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
buttonDecoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldDefaultBG,
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dropdownDecoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldDefaultBG,
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!isDesktop)
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
|
@ -419,9 +690,8 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: RawMaterialButton(
|
child: RawMaterialButton(
|
||||||
splashColor: Theme.of(context)
|
splashColor:
|
||||||
.extension<StackColors>()!
|
Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
.highlight,
|
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
|
@ -436,22 +706,18 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
top: Radius.circular(20),
|
top: Radius.circular(20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (_) =>
|
builder: (_) => const BackupFrequencyTypeSelectSheet(),
|
||||||
const BackupFrequencyTypeSelectSheet(),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
const EdgeInsets.symmetric(horizontal: 12.0),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
Format.prettyFrequencyType(ref.watch(
|
Format.prettyFrequencyType(ref.watch(
|
||||||
prefsChangeNotifierProvider.select(
|
prefsChangeNotifierProvider.select(
|
||||||
(value) =>
|
(value) => value.backupFrequencyType))),
|
||||||
value.backupFrequencyType))),
|
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -472,10 +738,34 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Spacer(),
|
if (!isDesktop) const Spacer(),
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
height: 10,
|
height: isDesktop ? 24 : 10,
|
||||||
),
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SecondaryButton(
|
||||||
|
label: "Cancel",
|
||||||
|
desktopMed: true,
|
||||||
|
onPressed: Navigator.of(context).pop,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: PrimaryButton(
|
||||||
|
label: "Save",
|
||||||
|
desktopMed: true,
|
||||||
|
enabled: shouldEnableCreate,
|
||||||
|
onPressed: onSavePressed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (!isDesktop)
|
||||||
TextButton(
|
TextButton(
|
||||||
style: shouldEnableCreate
|
style: shouldEnableCreate
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
|
@ -484,162 +774,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getPrimaryDisabledButtonColor(context),
|
.getPrimaryDisabledButtonColor(context),
|
||||||
onPressed: !shouldEnableCreate
|
onPressed: !shouldEnableCreate ? null : onSavePressed,
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final String pathToSave =
|
|
||||||
fileLocationController.text;
|
|
||||||
final String passphrase = passwordController.text;
|
|
||||||
final String repeatPassphrase =
|
|
||||||
passwordRepeatController.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: "Updating Auto Backup",
|
|
||||||
message: "This shouldn't take long",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// make sure the dialog is able to be displayed for at least 1 second
|
|
||||||
final fut = Future<void>.delayed(
|
|
||||||
const Duration(seconds: 1));
|
|
||||||
|
|
||||||
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(
|
|
||||||
secureStorage: ref.read(secureStoreProvider),
|
|
||||||
);
|
|
||||||
|
|
||||||
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
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
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 saved to:",
|
|
||||||
message: fileToSave,
|
|
||||||
)
|
|
||||||
: const StackOkDialog(
|
|
||||||
title: "Stack Auto Backup saved"),
|
|
||||||
);
|
|
||||||
if (mounted) {
|
|
||||||
passwordController.text = "";
|
|
||||||
passwordRepeatController.text = "";
|
|
||||||
|
|
||||||
Navigator.of(context).popUntil(
|
|
||||||
ModalRoute.withName(
|
|
||||||
AutoBackupView.routeName));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await showDialog<dynamic>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (_) => const StackOkDialog(
|
|
||||||
title: "Failed to update Auto Backup"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
"Save",
|
"Save",
|
||||||
style: STextStyles.button(context),
|
style: STextStyles.button(context),
|
||||||
|
@ -647,11 +782,6 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:intl/intl.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/create_backup_view.dart';
|
||||||
|
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart';
|
||||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_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/create_auto_backup.dart';
|
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/create_auto_backup.dart';
|
||||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/enable_backup_dialog.dart';
|
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/enable_backup_dialog.dart';
|
||||||
|
@ -105,6 +106,44 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> editAutoBackup() async {
|
||||||
|
await showDialog<dynamic>(
|
||||||
|
context: context,
|
||||||
|
useSafeArea: false,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (context) => DesktopDialog(
|
||||||
|
maxWidth: 580,
|
||||||
|
maxHeight: double.infinity,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 32),
|
||||||
|
child: Text(
|
||||||
|
"Edit auto backup",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const DesktopDialogCloseButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 32,
|
||||||
|
right: 32,
|
||||||
|
bottom: 32,
|
||||||
|
),
|
||||||
|
child: EditAutoBackupView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> attemptDisable() async {
|
Future<void> attemptDisable() async {
|
||||||
final result = await showDialog<bool?>(
|
final result = await showDialog<bool?>(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -440,8 +479,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
|
||||||
width: 190,
|
width: 190,
|
||||||
label: "Edit auto backup",
|
label: "Edit auto backup",
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Navigator.of(context).pop();
|
editAutoBackup();
|
||||||
createAutoBackup();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -383,7 +383,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 16,
|
||||||
),
|
),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
|
|
Loading…
Reference in a new issue