mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 09:47:37 +00:00
commit
23d2de26d7
8 changed files with 737 additions and 130 deletions
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -315,42 +316,45 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
}
|
||||
|
||||
bool shouldPop = false;
|
||||
await showDialog<dynamic>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
return shouldPop;
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
style:
|
||||
STextStyles.pageTitleH2(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textWhite,
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
return shouldPop;
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
style: STextStyles.pageTitleH2(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textWhite,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
const Center(
|
||||
child: LoadingIndicator(
|
||||
width: 100,
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
],
|
||||
const Center(
|
||||
child: LoadingIndicator(
|
||||
width: 100,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -424,43 +428,47 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
}
|
||||
|
||||
bool shouldPop = false;
|
||||
await showDialog<dynamic>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
return shouldPop;
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
style: STextStyles.pageTitleH2(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textWhite,
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
return shouldPop;
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
style:
|
||||
STextStyles.pageTitleH2(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
const Center(
|
||||
child: LoadingIndicator(
|
||||
width: 100,
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
],
|
||||
const Center(
|
||||
child: LoadingIndicator(
|
||||
width: 100,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -475,7 +483,10 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
if (mounted) {
|
||||
// pop LoadingIndicator
|
||||
shouldPop = true;
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop();
|
||||
|
||||
passwordController.text = "";
|
||||
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
class DesktopAddressBook extends ConsumerStatefulWidget {
|
||||
const DesktopAddressBook({Key? key}) : super(key: key);
|
||||
|
||||
static const String routeName = "/desktopAddressBook";
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopAddressBook> createState() => _DesktopAddressBook();
|
||||
}
|
||||
|
||||
class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
|
||||
late final TextEditingController _searchController;
|
||||
|
||||
late final FocusNode _searchFocusNode;
|
||||
|
||||
String filter = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
_searchFocusNode = FocusNode();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_searchFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
final hasWallets = ref.watch(walletsChangeNotifierProvider).hasWallets;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DesktopAppBar(
|
||||
isCompactHeight: true,
|
||||
leading: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 24,
|
||||
),
|
||||
Text(
|
||||
"Address Book",
|
||||
style: STextStyles.desktopH3(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 53),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 60,
|
||||
width: 489,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (newString) {
|
||||
setState(() => filter = newString);
|
||||
},
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search...",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: STextStyles.fieldLabel(context)
|
||||
.copyWith(fontSize: 16),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
filter = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: hasWallets ? const MyWallets() : const EmptyWallets(),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/desktop_address_book.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_view.dart';
|
||||
|
@ -31,8 +32,10 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
|
|||
Container(
|
||||
color: Colors.red,
|
||||
),
|
||||
Container(
|
||||
color: Colors.orange,
|
||||
const Navigator(
|
||||
key: Key("desktopAddressBookHomeKey"),
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
initialRoute: DesktopAddressBook.routeName,
|
||||
),
|
||||
const Navigator(
|
||||
key: Key("desktopSettingHomeKey"),
|
||||
|
|
|
@ -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: () {},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,12 +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';
|
||||
|
@ -16,21 +28,31 @@ 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 StatefulWidget {
|
||||
const CreateAutoBackup({Key? key}) : super(key: key);
|
||||
class CreateAutoBackup extends ConsumerStatefulWidget {
|
||||
const CreateAutoBackup({
|
||||
Key? key,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CreateAutoBackup();
|
||||
ConsumerState<CreateAutoBackup> createState() => _CreateAutoBackup();
|
||||
}
|
||||
|
||||
class _CreateAutoBackup extends State<CreateAutoBackup> {
|
||||
class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
||||
late final TextEditingController fileLocationController;
|
||||
late final TextEditingController passphraseController;
|
||||
late final TextEditingController passphraseRepeatController;
|
||||
|
||||
late final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
late final StackFileSystem stackFileSystem;
|
||||
late final FocusNode passphraseFocusNode;
|
||||
late final FocusNode passphraseRepeatFocusNode;
|
||||
|
@ -52,16 +74,18 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
bool get fieldsMatch =>
|
||||
passphraseController.text == passphraseRepeatController.text;
|
||||
|
||||
String _currentDropDownValue = "Every 10 minutes";
|
||||
BackupFrequencyType _currentDropDownValue =
|
||||
BackupFrequencyType.everyTenMinutes;
|
||||
|
||||
final List<String> _dropDownItems = [
|
||||
"Every 10 minutes",
|
||||
"Every 20 minutes",
|
||||
"Every 30 minutes",
|
||||
final List<BackupFrequencyType> _dropDownItems = [
|
||||
BackupFrequencyType.everyTenMinutes,
|
||||
BackupFrequencyType.everyAppStart,
|
||||
BackupFrequencyType.afterClosingAWallet,
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
secureStore = widget.secureStore;
|
||||
stackFileSystem = StackFileSystem();
|
||||
|
||||
fileLocationController = TextEditingController();
|
||||
|
@ -101,6 +125,9 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType ");
|
||||
|
||||
bool isEnabledAutoBackup = ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.isAutoBackupEnabled));
|
||||
|
||||
String? selectedItem = "Every 10 minutes";
|
||||
final isDesktop = Util.isDesktop;
|
||||
return DesktopDialog(
|
||||
|
@ -225,9 +252,7 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
paste: false,
|
||||
selectAll: false,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
// ref.read(addressEntryDataProvider(widget.id)).address = newValue;
|
||||
},
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
@ -361,7 +386,7 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
),
|
||||
child: ProgressBar(
|
||||
key: const Key("createStackBackUpProgressBar"),
|
||||
width: 510,
|
||||
width: 512,
|
||||
height: 5,
|
||||
fillColor: passwordStrength < 0.51
|
||||
? Theme.of(context)
|
||||
|
@ -465,38 +490,83 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
left: 32,
|
||||
right: 32,
|
||||
),
|
||||
child: DropdownButtonFormField(
|
||||
isExpanded: true,
|
||||
elevation: 0,
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 10,
|
||||
height: 5,
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
dropdownColor:
|
||||
Theme.of(context).extension<StackColors>()!.textFieldActiveBG,
|
||||
// focusColor: ,
|
||||
value: _currentDropDownValue,
|
||||
items: _dropDownItems
|
||||
.map(
|
||||
(e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(e),
|
||||
child: isDesktop
|
||||
? DropdownButtonHideUnderline(
|
||||
child: DropdownButton2(
|
||||
offset: 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
if (value is String) {
|
||||
setState(() {
|
||||
_currentDropDownValue = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
: null,
|
||||
),
|
||||
const Spacer(),
|
||||
Padding(
|
||||
|
@ -518,8 +588,164 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Enable Auto Backup",
|
||||
enabled: false,
|
||||
onPressed: () {},
|
||||
enabled: shouldEnableCreate,
|
||||
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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
@ -86,6 +86,7 @@ import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
|||
import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/forgot_password_desktop_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/desktop_address_book.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_view.dart';
|
||||
|
@ -1105,6 +1106,12 @@ class RouteGenerator {
|
|||
builder: (_) => const DesktopAboutView(),
|
||||
settings: RouteSettings(name: settings.name));
|
||||
|
||||
case DesktopAddressBook.routeName:
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => const DesktopAddressBook(),
|
||||
settings: RouteSettings(name: settings.name));
|
||||
|
||||
case WalletKeysDesktopPopup.routeName:
|
||||
if (args is List<String>) {
|
||||
return FadePageRoute(
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -11,7 +11,7 @@ description: Stack Wallet
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.5.14+86
|
||||
version: 1.5.16+88
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
|
Loading…
Reference in a new issue