desktop forgot password flow and functionality

This commit is contained in:
julian 2022-12-05 13:55:50 -06:00
parent 6a17ddffdf
commit 7436709fa7
11 changed files with 813 additions and 115 deletions

View file

@ -40,8 +40,6 @@ class DB {
String boxNameUsedSerialsCache({required Coin coin}) =>
"${coin.name}_usedSerialsCache";
static bool _initialized = false;
Box<dynamic>? _boxAddressBook;
Box<String>? _boxDebugInfo;
Box<NodeModel>? _boxNodeModels;
@ -88,69 +86,65 @@ class DB {
// open hive boxes
Future<void> init() async {
if (!_initialized) {
if (Hive.isBoxOpen(boxNameDBInfo)) {
_boxDBInfo = Hive.box<dynamic>(boxNameDBInfo);
} else {
_boxDBInfo = await Hive.openBox<dynamic>(boxNameDBInfo);
}
await Hive.openBox<String>(boxNameWalletsToDeleteOnStart);
if (Hive.isBoxOpen(boxNamePrefs)) {
_boxPrefs = Hive.box<dynamic>(boxNamePrefs);
} else {
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
}
_boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook);
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
if (Hive.isBoxOpen(boxNameNodeModels)) {
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
} else {
_boxNodeModels = await Hive.openBox<NodeModel>(boxNameNodeModels);
}
if (Hive.isBoxOpen(boxNamePrimaryNodes)) {
_boxPrimaryNodes = Hive.box<NodeModel>(boxNamePrimaryNodes);
} else {
_boxPrimaryNodes = await Hive.openBox<NodeModel>(boxNamePrimaryNodes);
}
if (Hive.isBoxOpen(boxNameAllWalletsData)) {
_boxAllWalletsData = Hive.box<dynamic>(boxNameAllWalletsData);
} else {
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
}
if (Hive.isBoxOpen(boxNameDesktopData)) {
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
} else {
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
}
_boxNotifications =
await Hive.openBox<NotificationModel>(boxNameNotifications);
_boxWatchedTransactions =
await Hive.openBox<NotificationModel>(boxNameWatchedTransactions);
_boxWatchedTrades =
await Hive.openBox<NotificationModel>(boxNameWatchedTrades);
_boxTrades = await Hive.openBox<ExchangeTransaction>(boxNameTrades);
_boxTradesV2 = await Hive.openBox<Trade>(boxNameTradesV2);
_boxTradeNotes = await Hive.openBox<String>(boxNameTradeNotes);
_boxTradeLookup =
await Hive.openBox<TradeWalletLookup>(boxNameTradeLookup);
_walletInfoSource =
await Hive.openBox<xmr.WalletInfo>(xmr.WalletInfo.boxName);
_boxFavoriteWallets = await Hive.openBox<String>(boxNameFavoriteWallets);
await Future.wait([
Hive.openBox<dynamic>(boxNamePriceCache),
_loadWalletBoxes(),
_loadSharedCoinCacheBoxes(),
]);
_initialized = true;
if (Hive.isBoxOpen(boxNameDBInfo)) {
_boxDBInfo = Hive.box<dynamic>(boxNameDBInfo);
} else {
_boxDBInfo = await Hive.openBox<dynamic>(boxNameDBInfo);
}
await Hive.openBox<String>(boxNameWalletsToDeleteOnStart);
if (Hive.isBoxOpen(boxNamePrefs)) {
_boxPrefs = Hive.box<dynamic>(boxNamePrefs);
} else {
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
}
_boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook);
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
if (Hive.isBoxOpen(boxNameNodeModels)) {
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
} else {
_boxNodeModels = await Hive.openBox<NodeModel>(boxNameNodeModels);
}
if (Hive.isBoxOpen(boxNamePrimaryNodes)) {
_boxPrimaryNodes = Hive.box<NodeModel>(boxNamePrimaryNodes);
} else {
_boxPrimaryNodes = await Hive.openBox<NodeModel>(boxNamePrimaryNodes);
}
if (Hive.isBoxOpen(boxNameAllWalletsData)) {
_boxAllWalletsData = Hive.box<dynamic>(boxNameAllWalletsData);
} else {
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
}
if (Hive.isBoxOpen(boxNameDesktopData)) {
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
} else {
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
}
_boxNotifications =
await Hive.openBox<NotificationModel>(boxNameNotifications);
_boxWatchedTransactions =
await Hive.openBox<NotificationModel>(boxNameWatchedTransactions);
_boxWatchedTrades =
await Hive.openBox<NotificationModel>(boxNameWatchedTrades);
_boxTrades = await Hive.openBox<ExchangeTransaction>(boxNameTrades);
_boxTradesV2 = await Hive.openBox<Trade>(boxNameTradesV2);
_boxTradeNotes = await Hive.openBox<String>(boxNameTradeNotes);
_boxTradeLookup = await Hive.openBox<TradeWalletLookup>(boxNameTradeLookup);
_walletInfoSource =
await Hive.openBox<xmr.WalletInfo>(xmr.WalletInfo.boxName);
_boxFavoriteWallets = await Hive.openBox<String>(boxNameFavoriteWallets);
await Future.wait([
Hive.openBox<dynamic>(boxNamePriceCache),
_loadWalletBoxes(),
_loadSharedCoinCacheBoxes(),
]);
}
Future<void> _loadWalletBoxes() async {

View file

@ -2,12 +2,14 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/stack_privacy_calls.dart';
import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:url_launcher/url_launcher.dart';
class IntroView extends StatefulWidget {
@ -135,6 +137,20 @@ class _IntroViewState extends State<IntroView> {
GetStartedButton(
isDesktop: isDesktop,
),
if (isDesktop)
const SizedBox(
height: 20,
),
if (isDesktop)
SecondaryButton(
label: "Restore from Stack backup",
onPressed: () {
Navigator.of(context).pushNamed(
CreatePasswordView.routeName,
arguments: true,
);
},
),
const Spacer(
flex: 65,
),
@ -257,7 +273,7 @@ class GetStartedButton extends StatelessWidget {
),
)
: SizedBox(
width: 328,
width: double.infinity,
height: 70,
child: TextButton(
style: Theme.of(context)
@ -270,7 +286,7 @@ class GetStartedButton extends StatelessWidget {
);
},
child: Text(
"Get started",
"Create new Stack",
style: STextStyles.button(context).copyWith(fontSize: 20),
),
),

View file

@ -12,7 +12,6 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';

View file

@ -9,6 +9,9 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_wallet_card.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_menu.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/stack_restore/stack_restoring_ui_state_provider.dart';
@ -20,25 +23,23 @@ import 'package:stackwallet/utilities/theme/stack_colors.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/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import '../../../../../pages_desktop_specific/desktop_home_view.dart';
import '../../../../../pages_desktop_specific/desktop_menu.dart';
import '../../../../../providers/desktop/current_desktop_menu_item.dart';
import '../../../../../widgets/desktop/primary_button.dart';
class StackRestoreProgressView extends ConsumerStatefulWidget {
const StackRestoreProgressView({
Key? key,
required this.jsonString,
this.fromFile = false,
this.shouldPushToHome = false,
}) : super(key: key);
final String jsonString;
final bool fromFile;
final bool shouldPushToHome;
@override
ConsumerState<StackRestoreProgressView> createState() =>
@ -696,11 +697,21 @@ class _StackRestoreProgressViewState
.state)
.state = keyID;
Navigator.of(context, rootNavigator: true)
.popUntil(
ModalRoute.withName(
DesktopHomeView.routeName),
);
if (widget.shouldPushToHome) {
unawaited(
Navigator.of(context)
.pushNamedAndRemoveUntil(
DesktopHomeView.routeName,
(route) => false,
),
);
} else {
Navigator.of(context, rootNavigator: true)
.popUntil(
ModalRoute.withName(
DesktopHomeView.routeName),
);
}
},
)
: SecondaryButton(

View file

@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
@ -23,9 +24,11 @@ import 'package:zxcvbn/zxcvbn.dart';
class CreatePasswordView extends ConsumerStatefulWidget {
const CreatePasswordView({
Key? key,
this.restoreFromSWB = false,
}) : super(key: key);
static const String routeName = "/createPasswordDesktop";
final bool restoreFromSWB;
@override
ConsumerState<CreatePasswordView> createState() => _CreatePasswordViewState();
@ -84,7 +87,9 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
// load default nodes now as node service requires storage handler to exist
await ref.read(nodeServiceChangeNotifierProvider).updateDefaults();
if (!widget.restoreFromSWB) {
await ref.read(nodeServiceChangeNotifierProvider).updateDefaults();
}
} catch (e) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
@ -95,15 +100,28 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
}
if (mounted) {
unawaited(Navigator.of(context)
.pushReplacementNamed(DesktopHomeView.routeName));
if (widget.restoreFromSWB) {
unawaited(
Navigator.of(context).pushNamed(
ForgottenPassphraseRestoreFromSWB.routeName,
),
);
} else {
unawaited(
Navigator.of(context).pushReplacementNamed(
DesktopHomeView.routeName,
),
);
}
}
unawaited(showFloatingFlushBar(
type: FlushBarType.success,
message: "Your password is set up",
context: context,
));
if (!widget.restoreFromSWB) {
unawaited(showFloatingFlushBar(
type: FlushBarType.success,
message: "Your password is set up",
context: context,
));
}
}
@override

View file

@ -0,0 +1,193 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hive/hive.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/intro_view.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
class DeletePasswordWarningView extends ConsumerStatefulWidget {
const DeletePasswordWarningView({
Key? key,
required this.shouldCreateNew,
}) : super(key: key);
static const String routeName = "/deletePasswordWarning";
final bool shouldCreateNew;
@override
ConsumerState<DeletePasswordWarningView> createState() =>
_ForgotPasswordDesktopViewState();
}
class _ForgotPasswordDesktopViewState
extends ConsumerState<DeletePasswordWarningView> {
bool _deleteInProgress = false;
Future<bool> _deleteStack() async {
final appRoot = await StackFileSystem.applicationRootDirectory();
try {
await Hive.close();
await appRoot.delete(recursive: true);
await DB.instance.init();
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
return false;
}
return true;
}
@override
Widget build(BuildContext context) {
return DesktopScaffold(
appBar: DesktopAppBar(
leading: AppBarBackButton(
onPressed: () async {
if (mounted && !_deleteInProgress) {
Navigator.of(context).pop();
}
},
),
isCompactHeight: false,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 480,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
Assets.svg.stackIcon(context),
width: 100,
),
const SizedBox(
height: 42,
),
Text(
"Warning!",
style: STextStyles.desktopH1(context),
),
const SizedBox(
height: 24,
),
SizedBox(
width: 480,
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: [
TextSpan(
text: "To ",
style: STextStyles.desktopTextSmall(context),
),
TextSpan(
text: "create a new Stack",
style: STextStyles.desktopTextSmallBold(context),
),
TextSpan(
text: ", we need to ",
style: STextStyles.desktopTextSmall(context),
),
TextSpan(
text: "delete your old wallets",
style: STextStyles.desktopTextSmallBold(context),
),
TextSpan(
text:
". All wallets will be lost. If you have not written down your recovery phrase for EACH wallet, you may be in danger of losing funds. Continue?",
style: STextStyles.desktopTextSmall(context),
),
],
),
),
),
const SizedBox(
height: 48,
),
PrimaryButton(
label: "Delete and continue",
enabled: !_deleteInProgress,
onPressed: () async {
final shouldDelete = !_deleteInProgress;
setState(() {
_deleteInProgress = true;
});
if (shouldDelete) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Deleting wallet...",
context: context,
),
);
final success = await _deleteStack();
if (success) {
await showFloatingFlushBar(
type: FlushBarType.success,
message: "Wallet deleted",
context: context,
);
if (mounted) {
await Navigator.of(context).pushNamedAndRemoveUntil(
IntroView.routeName,
(_) => false,
);
}
} else {
await showFloatingFlushBar(
type: FlushBarType.warning,
message: "Something broke badly. Contact developer",
context: context,
);
setState(() {
_deleteInProgress = false;
});
}
}
},
),
const SizedBox(
height: 24,
),
SecondaryButton(
label: "Take me back!",
enabled: !_deleteInProgress,
onPressed: () {
Navigator.of(context).pop();
},
),
const SizedBox(
height: kDesktopAppBarHeight,
),
],
),
),
],
),
);
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -74,18 +75,26 @@ class _ForgotPasswordDesktopViewState extends State<ForgotPasswordDesktopView> {
height: 48,
),
PrimaryButton(
label: "Create new wallet",
label: "Create new Stack",
onPressed: () {
// // todo delete everything and start fresh?
const shouldCreateNew = true;
Navigator.of(context).pushNamed(
DeletePasswordWarningView.routeName,
arguments: shouldCreateNew,
);
},
),
const SizedBox(
height: 24,
),
SecondaryButton(
label: "Restore from backup",
label: "Restore from Stack backup",
onPressed: () {
// todo SWB restore
const shouldCreateNew = false;
Navigator.of(context).pushNamed(
DeletePasswordWarningView.routeName,
arguments: shouldCreateNew,
);
},
),
const SizedBox(

View file

@ -0,0 +1,406 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:stackwallet/hive/db.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/swb_file_system.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.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/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:tuple/tuple.dart';
class ForgottenPassphraseRestoreFromSWB extends ConsumerStatefulWidget {
const ForgottenPassphraseRestoreFromSWB({Key? key}) : super(key: key);
static const String routeName = "/forgottenPassphraseRestoreFromSWB";
@override
ConsumerState<ForgottenPassphraseRestoreFromSWB> createState() =>
_ForgottenPassphraseRestoreFromSWBState();
}
class _ForgottenPassphraseRestoreFromSWBState
extends ConsumerState<ForgottenPassphraseRestoreFromSWB> {
late final TextEditingController fileLocationController;
late final TextEditingController passwordController;
late final FocusNode passwordFocusNode;
late final SWBFileSystem stackFileSystem;
bool hidePassword = true;
bool _enableButton = false;
Future<void> restore() async {
final String fileToRestore = fileLocationController.text;
final String passphrase = passwordController.text;
if (!(await File(fileToRestore).exists())) {
await showFloatingFlushBar(
type: FlushBarType.warning,
message: "Backup file does not exist",
context: context,
);
return;
}
bool shouldPop = false;
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,
),
),
],
),
),
),
);
final String? jsonString = await compute(
SWB.decryptStackWalletWithPassphrase,
Tuple2(fileToRestore, passphrase),
debugLabel: "stack wallet decryption compute",
);
if (mounted) {
// pop LoadingIndicator
shouldPop = true;
Navigator.of(context).pop();
passwordController.text = "";
if (jsonString == null) {
await showFloatingFlushBar(
type: FlushBarType.warning,
message: "Failed to decrypt backup file",
context: context,
);
return;
}
ref.read(walletsChangeNotifierProvider);
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Padding(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Restoring Stack wallet",
style: STextStyles.desktopH3(context),
),
],
),
const SizedBox(
height: 44,
),
StackRestoreProgressView(
jsonString: jsonString,
shouldPushToHome: true,
),
],
),
),
);
},
);
// await Navigator.of(context).push(
// RouteGenerator.getRoute(
// builder: (_) => StackRestoreProgressView(
// jsonString: jsonString,
// ),
// ),
// );
}
}
@override
void initState() {
stackFileSystem = SWBFileSystem();
fileLocationController = TextEditingController();
passwordController = TextEditingController();
passwordFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
fileLocationController.dispose();
passwordController.dispose();
passwordFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DesktopScaffold(
appBar: DesktopAppBar(
leading: AppBarBackButton(
onPressed: () async {
await (ref.read(secureStoreProvider).store as DesktopSecureStore)
.close();
ref.refresh(secureStoreProvider);
ref.refresh(storageCryptoHandlerProvider);
await Hive.deleteBoxFromDisk(DB.boxNameDesktopData);
await DB.instance.init();
if (mounted) {
Navigator.of(context)
.popUntil(ModalRoute.withName(CreatePasswordView.routeName));
Navigator.of(context).pop();
}
},
),
isCompactHeight: false,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 480,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Restore from backup",
style: STextStyles.desktopH1(context),
),
const SizedBox(
height: 32,
),
Text(
"Use your Stack wallet backup file to restore your wallets, address book, and wallet preferences.",
textAlign: TextAlign.center,
style: STextStyles.desktopTextSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
const SizedBox(
height: 40,
),
GestureDetector(
onTap: () async {
try {
await stackFileSystem.prepareStorage();
if (mounted) {
await stackFileSystem.openFile(context);
}
if (mounted) {
setState(() {
fileLocationController.text =
stackFileSystem.filePath ?? "";
});
}
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Error);
}
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: IgnorePointer(
child: TextField(
autocorrect: false,
enableSuggestions: false,
controller: fileLocationController,
style: STextStyles.field(context),
decoration: InputDecoration(
hintText: "Choose file...",
hintStyle: STextStyles.desktopTextFieldLabel(context),
suffixIcon: SizedBox(
height: 70,
child: UnconstrainedBox(
child: Row(
children: [
const SizedBox(
width: 24,
),
SvgPicture.asset(
Assets.svg.folder,
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
width: 24,
height: 24,
),
const SizedBox(
width: 12,
),
],
),
),
),
),
key: const Key("restoreFromFileLocationTextFieldKey"),
readOnly: true,
toolbarOptions: const ToolbarOptions(
copy: true,
cut: false,
paste: false,
selectAll: false,
),
onChanged: (newValue) {
setState(() {
_enableButton =
passwordController.text.isNotEmpty &&
fileLocationController.text.isNotEmpty;
});
},
),
),
),
),
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("restoreFromFilePasswordFieldKey"),
focusNode: passwordFocusNode,
controller: passwordController,
style: STextStyles.desktopTextMedium(context).copyWith(
height: 2,
),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
decoration: standardInputDecoration(
"Enter passphrase",
passwordFocusNode,
context,
).copyWith(
suffixIcon: UnconstrainedBox(
child: SizedBox(
height: 70,
child: Row(
children: [
const SizedBox(
width: 24,
),
GestureDetector(
key: const Key(
"restoreFromFilePasswordFieldShowPasswordButtonKey"),
onTap: () async {
setState(() {
hidePassword = !hidePassword;
});
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: SvgPicture.asset(
hidePassword
? Assets.svg.eye
: Assets.svg.eyeSlash,
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
width: 24,
height: 24,
),
),
),
const SizedBox(
width: 12,
),
],
),
),
),
),
onChanged: (newValue) {
setState(() {
_enableButton = passwordController.text.isNotEmpty &&
fileLocationController.text.isNotEmpty;
});
},
),
),
const SizedBox(
height: 24,
),
PrimaryButton(
label: "Restore",
enabled: _enableButton,
onPressed: () {
restore();
},
),
const SizedBox(
height: kDesktopAppBarHeight,
),
],
),
),
],
),
);
}
}

View file

@ -98,7 +98,9 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
import 'package:stackwallet/pages_desktop_specific/notifications/desktop_notifications_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/forgot_password_desktop_view.dart';
import 'package:stackwallet/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart';
import 'package:stackwallet/pages_desktop_specific/settings/desktop_settings_view.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings.dart';
@ -999,6 +1001,17 @@ class RouteGenerator {
// == Desktop specific routes ============================================
case CreatePasswordView.routeName:
if (args is bool) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => CreatePasswordView(
restoreFromSWB: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const CreatePasswordView(),
@ -1010,6 +1023,26 @@ class RouteGenerator {
builder: (_) => const ForgotPasswordDesktopView(),
settings: RouteSettings(name: settings.name));
case ForgottenPassphraseRestoreFromSWB.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const ForgottenPassphraseRestoreFromSWB(),
settings: RouteSettings(name: settings.name));
case DeletePasswordWarningView.routeName:
if (args is bool) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => DeletePasswordWarningView(
shouldCreateNew: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DesktopHomeView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,

View file

@ -997,6 +997,32 @@ class STextStyles {
}
}
static TextStyle desktopTextSmallBold(BuildContext context) {
switch (_theme(context).themeType) {
case ThemeType.light:
return GoogleFonts.inter(
color: _theme(context).textDark,
fontWeight: FontWeight.w700,
fontSize: 18,
height: 27 / 18,
);
case ThemeType.oceanBreeze:
return GoogleFonts.inter(
color: _theme(context).textDark,
fontWeight: FontWeight.w700,
fontSize: 18,
height: 27 / 18,
);
case ThemeType.dark:
return GoogleFonts.inter(
color: _theme(context).buttonTextPrimaryDisabled,
fontWeight: FontWeight.w700,
fontSize: 18,
height: 27 / 18,
);
}
}
static TextStyle desktopTextExtraSmall(BuildContext context) {
switch (_theme(context).themeType) {
case ThemeType.light:

View file

@ -42,7 +42,7 @@ packages:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.11"
version: "3.3.0"
args:
dependency: transitive
description:
@ -63,7 +63,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
version: "2.9.0"
barcode_scan2:
dependency: "direct main"
description:
@ -190,14 +190,7 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.2.1"
checked_yaml:
dependency: transitive
description:
@ -218,7 +211,7 @@ packages:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
code_builder:
dependency: transitive
description:
@ -288,7 +281,7 @@ packages:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.5.0"
cross_file:
dependency: transitive
description:
@ -442,7 +435,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
ffi:
dependency: "direct main"
description:
@ -871,21 +864,21 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.11"
version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.8.0"
mime:
dependency: transitive
description:
@ -997,7 +990,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
path_drawing:
dependency: transitive
description:
@ -1373,7 +1366,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
version: "1.9.0"
stack_trace:
dependency: transitive
description:
@ -1417,7 +1410,7 @@ packages:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
string_validator:
dependency: "direct main"
description:
@ -1431,35 +1424,35 @@ packages:
name: sync_http
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.3.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.1"
test:
dependency: transitive
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.21.1"
version: "1.21.4"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.12"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.13"
version: "0.4.16"
time:
dependency: transitive
description:
@ -1508,7 +1501,7 @@ packages:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
universal_io:
dependency: transitive
description:
@ -1592,7 +1585,7 @@ packages:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "8.2.2"
version: "9.0.0"
wakelock:
dependency: "direct main"
description: