fix delete frost wallet on mobile

This commit is contained in:
julian 2024-05-14 09:49:20 -06:00
parent 33c78a4b42
commit 738cb55a40
4 changed files with 338 additions and 115 deletions

View file

@ -17,6 +17,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -24,23 +25,35 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/simple_copy_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/detail_item.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class DeleteWalletRecoveryPhraseView extends ConsumerStatefulWidget {
const DeleteWalletRecoveryPhraseView({
Key? key,
super.key,
required this.walletId,
required this.mnemonic,
this.frostWalletData,
this.clipboardInterface = const ClipboardWrapper(),
}) : super(key: key);
});
static const routeName = "/deleteWalletRecoveryPhrase";
final String walletId;
final List<String> mnemonic;
final ({
String myName,
String config,
String keys,
({String config, String keys})? prevGen,
})? frostWalletData;
final ClipboardInterface clipboardInterface;
@ -54,6 +67,62 @@ class _DeleteWalletRecoveryPhraseViewState
late List<String> _mnemonic;
late ClipboardInterface _clipboardInterface;
bool _lock = false;
void _continuePressed() {
if (_lock) {
return;
}
_lock = true;
try {
showDialog<dynamic>(
barrierDismissible: true,
context: context,
builder: (_) => StackDialog(
title: "Thanks! Your wallet will be deleted.",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.accentColorDark,
),
),
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
await ref.read(pWallets).deleteWallet(
ref.read(pWalletInfo(widget.walletId)),
ref.read(secureStoreProvider),
);
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(HomeView.routeName),
);
}
},
child: Text(
"Ok",
style: STextStyles.button(context),
),
),
),
);
} finally {
_lock = false;
}
}
@override
void initState() {
_mnemonic = widget.mnemonic;
@ -65,6 +134,9 @@ class _DeleteWalletRecoveryPhraseViewState
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final bool frost = widget.frostWalletData != null;
final prevGen = widget.frostWalletData?.prevGen != null;
return Background(
child: Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
@ -93,7 +165,7 @@ class _DeleteWalletRecoveryPhraseViewState
onPressed: () async {
await _clipboardInterface
.setData(ClipboardData(text: _mnemonic.join(" ")));
if (mounted) {
if (context.mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
@ -111,116 +183,207 @@ class _DeleteWalletRecoveryPhraseViewState
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(
height: 4,
),
Text(
ref.watch(pWalletName(widget.walletId)),
textAlign: TextAlign.center,
style: STextStyles.label(context).copyWith(
fontSize: 12,
),
),
const SizedBox(
height: 4,
),
Text(
"Recovery Phrase",
textAlign: TextAlign.center,
style: STextStyles.pageTitleH1(context),
),
const SizedBox(
height: 16,
),
Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
style: STextStyles.label(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
),
const SizedBox(
height: 8,
),
Expanded(
child: SingleChildScrollView(
child: MnemonicTable(
words: _mnemonic,
isDesktop: false,
),
),
),
const SizedBox(
height: 16,
),
TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () {
showDialog<dynamic>(
barrierDismissible: true,
context: context,
builder: (_) => StackDialog(
title: "Thanks! Your wallet will be deleted.",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
child: frost
? LayoutBuilder(
builder: (builderContext, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
RoundedWhiteContainer(
child: Text(
"Please write down your backup data. Keep it safe and "
"never share it with anyone. "
"Your backup data is the only way you can access your "
"funds if you forget your PIN, lose your phone, etc."
"\n\n"
"Stack Wallet does not keep nor is able to restore "
"your backup data. "
"Only you have access to your wallet.",
style: STextStyles.label(context),
),
),
const SizedBox(
height: 24,
),
// DetailItem(
// title: "My name",
// detail: frostWalletData!.myName,
// button: Util.isDesktop
// ? IconCopyButton(
// data: frostWalletData!.myName,
// )
// : SimpleCopyButton(
// data: frostWalletData!.myName,
// ),
// ),
// const SizedBox(
// height: 16,
// ),
DetailItem(
title: "Multisig config",
detail: widget.frostWalletData!.config,
button: Util.isDesktop
? IconCopyButton(
data: widget.frostWalletData!.config,
)
: SimpleCopyButton(
data: widget.frostWalletData!.config,
),
),
const SizedBox(
height: 16,
),
DetailItem(
title: "Keys",
detail: widget.frostWalletData!.keys,
button: Util.isDesktop
? IconCopyButton(
data: widget.frostWalletData!.keys,
)
: SimpleCopyButton(
data: widget.frostWalletData!.keys,
),
),
if (prevGen)
const SizedBox(
height: 24,
),
if (prevGen)
RoundedWhiteContainer(
child: Text(
"Previous generation info",
style: STextStyles.label(context),
),
),
if (prevGen)
const SizedBox(
height: 12,
),
if (prevGen)
DetailItem(
title: "Previous multisig config",
detail:
widget.frostWalletData!.prevGen!.config,
button: Util.isDesktop
? IconCopyButton(
data: widget
.frostWalletData!.prevGen!.config,
)
: SimpleCopyButton(
data: widget
.frostWalletData!.prevGen!.config,
),
),
if (prevGen)
const SizedBox(
height: 16,
),
if (prevGen)
DetailItem(
title: "Previous keys",
detail: widget.frostWalletData!.prevGen!.keys,
button: Util.isDesktop
? IconCopyButton(
data: widget
.frostWalletData!.prevGen!.keys,
)
: SimpleCopyButton(
data: widget
.frostWalletData!.prevGen!.keys,
),
),
const Spacer(),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Continue",
onPressed: _continuePressed,
),
],
),
),
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
await ref.read(pWallets).deleteWallet(
ref.read(pWalletInfo(widget.walletId)),
ref.read(secureStoreProvider),
);
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(HomeView.routeName));
}
},
);
},
)
: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(
height: 4,
),
Text(
ref.watch(pWalletName(widget.walletId)),
textAlign: TextAlign.center,
style: STextStyles.label(context).copyWith(
fontSize: 12,
),
),
const SizedBox(
height: 4,
),
Text(
"Recovery Phrase",
textAlign: TextAlign.center,
style: STextStyles.pageTitleH1(context),
),
const SizedBox(
height: 16,
),
Container(
decoration: BoxDecoration(
color:
Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
"Ok",
style: STextStyles.button(context),
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
style: STextStyles.label(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
);
},
child: Text(
"Continue",
style: STextStyles.button(context),
const SizedBox(
height: 8,
),
Expanded(
child: SingleChildScrollView(
child: MnemonicTable(
words: _mnemonic,
isDesktop: false,
),
),
),
const SizedBox(
height: 16,
),
TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: _continuePressed,
child: Text(
"Continue",
style: STextStyles.button(context),
),
),
],
),
),
],
),
),
),
);

View file

@ -14,6 +14,7 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_set
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -21,9 +22,9 @@ import 'package:stackwallet/widgets/rounded_container.dart';
class DeleteWalletWarningView extends ConsumerWidget {
const DeleteWalletWarningView({
Key? key,
super.key,
required this.walletId,
}) : super(key: key);
});
static const String routeName = "/deleteWalletWarning";
@ -100,14 +101,50 @@ class DeleteWalletWarningView extends ConsumerWidget {
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
final wallet = ref.read(pWallets).getWallet(walletId);
final mnemonic =
await (wallet as MnemonicInterface).getMnemonicAsWords();
// TODO: [prio=med] take wallets that don't have a mnemonic into account
List<String>? mnemonic;
({
String myName,
String config,
String keys,
({String config, String keys})? prevGen,
})? frostWalletData;
if (wallet is BitcoinFrostWallet) {
final futures = [
wallet.getSerializedKeys(),
wallet.getMultisigConfig(),
wallet.getSerializedKeysPrevGen(),
wallet.getMultisigConfigPrevGen(),
];
final results = await Future.wait(futures);
if (results.length == 4) {
frostWalletData = (
myName: wallet.frostInfo.myName,
config: results[1]!,
keys: results[0]!,
prevGen: results[2] == null || results[3] == null
? null
: (
config: results[3]!,
keys: results[2]!,
),
);
}
} else if (wallet is MnemonicInterface) {
mnemonic = await wallet.getMnemonicAsWords();
}
if (context.mounted) {
await Navigator.of(context).pushNamed(
DeleteWalletRecoveryPhraseView.routeName,
arguments: (
walletId: walletId,
mnemonicWords: mnemonic,
mnemonicWords: mnemonic ?? [],
frostWalletData: frostWalletData,
),
);
}

View file

@ -122,9 +122,10 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
rightButton: TextButton(
@ -150,7 +151,8 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
"Delete wallet",
),
settings: const RouteSettings(
name: "/deleteWalletLockscreen"),
name: "/deleteWalletLockscreen",
),
),
);
},

View file

@ -1745,6 +1745,27 @@ class RouteGenerator {
name: settings.name,
),
);
} else if (args is ({
String walletId,
List<String> mnemonicWords,
({
String myName,
String config,
String keys,
({String config, String keys})? prevGen,
})? frostWalletData,
})) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => DeleteWalletRecoveryPhraseView(
mnemonic: args.mnemonicWords,
walletId: args.walletId,
frostWalletData: args.frostWalletData,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");