wrap one way frost flow in sub navigation

This commit is contained in:
julian 2024-04-30 15:30:41 -06:00
parent 999df80e60
commit cb5f4e61de
31 changed files with 2719 additions and 3385 deletions

View file

@ -38,7 +38,7 @@ class _FrostScaffoldState extends ConsumerState<FrostStepScaffold> {
if (context.mounted) {
Navigator.of(context).pop();
ref.read(pFrostCreateNewArgs.state).state = null;
ref.read(pFrostScaffoldArgs.state).state = null;
}
_requestPopLock = false;
@ -46,7 +46,7 @@ class _FrostScaffoldState extends ConsumerState<FrostStepScaffold> {
@override
void initState() {
_routes = ref.read(pFrostCreateNewArgs)!.$2;
_routes = ref.read(pFrostScaffoldArgs)!.stepRoutes;
super.initState();
}

View file

@ -408,13 +408,14 @@ class _NewFrostMsWalletViewState
controllers.first.text.trim();
ref.read(pFrostMultisigConfig.notifier).state = config;
ref.read(pFrostCreateNewArgs.state).state = (
(
ref.read(pFrostScaffoldArgs.state).state = (
info: (
walletName: widget.walletName,
frostCurrency: widget.frostCurrency,
),
FrostRouteGenerator.createNewConfigStepRoutes,
() {
walletId: null,
stepRoutes: FrostRouteGenerator.createNewConfigStepRoutes,
onSuccess: () {
// successful completion of steps
if (Util.isDesktop) {
Navigator.of(context).popUntil(
@ -434,7 +435,7 @@ class _NewFrostMsWalletViewState
ref.read(pFrostMultisigConfig.state).state = null;
ref.read(pFrostStartKeyGenData.state).state = null;
ref.read(pFrostSecretSharesData.state).state = null;
ref.read(pFrostCreateNewArgs.state).state = null;
ref.read(pFrostScaffoldArgs.state).state = null;
unawaited(
showFloatingFlushBar(

View file

@ -7,7 +7,6 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/frost_scaffold.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_import_resharer_config_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
@ -141,13 +140,14 @@ class _SelectNewFrostImportTypeViewState
final String route;
switch (_selectedOption) {
case _ImportOption.multisigNew:
ref.read(pFrostCreateNewArgs.state).state = (
(
ref.read(pFrostScaffoldArgs.state).state = (
info: (
walletName: widget.walletName,
frostCurrency: widget.frostCurrency,
),
FrostRouteGenerator.importNewConfigStepRoutes,
() {
walletId: null, // no wallet id yet
stepRoutes: FrostRouteGenerator.importNewConfigStepRoutes,
onSuccess: () {
// successful completion of steps
if (Util.isDesktop) {
Navigator.of(context).popUntil(
@ -167,7 +167,7 @@ class _SelectNewFrostImportTypeViewState
ref.read(pFrostMultisigConfig.state).state = null;
ref.read(pFrostStartKeyGenData.state).state = null;
ref.read(pFrostSecretSharesData.state).state = null;
ref.read(pFrostCreateNewArgs.state).state = null;
ref.read(pFrostScaffoldArgs.state).state = null;
unawaited(
showFloatingFlushBar(
@ -179,22 +179,26 @@ class _SelectNewFrostImportTypeViewState
);
}
);
break;
case _ImportOption.resharerExisting:
ref.read(pFrostScaffoldArgs.state).state = (
info: (
walletName: widget.walletName,
frostCurrency: widget.frostCurrency,
),
walletId: null, // no wallet id yet
stepRoutes: FrostRouteGenerator.joinReshareStepRoutes,
onSuccess: () {
// successful completion of steps
}
);
break;
}
await Navigator.of(context).pushNamed(
FrostStepScaffold.routeName,
);
break;
case _ImportOption.resharerExisting:
await Navigator.of(context).pushNamed(
NewImportResharerConfigView.routeName,
arguments: (
walletName: widget.walletName,
frostCurrency: widget.frostCurrency,
),
);
break;
}
},
)
],

View file

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
@ -260,7 +259,10 @@ class _FrostCreateStep1aState extends ConsumerState<FrostCreateStep1a> {
ref.read(pFrostCreateCurrentStep.state).state = 2;
await Navigator.of(context).pushNamed(
FrostCreateStep2.routeName,
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
// FrostShareCommitmentsView.routeName,
);
},

View file

@ -4,7 +4,6 @@ import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
@ -356,8 +355,11 @@ class _FrostCreateStep1bState extends ConsumerState<FrostCreateStep1b> {
myName: ref.read(pFrostMyName.state).state!,
);
ref.read(pFrostCreateCurrentStep.state).state = 2;
Navigator.of(context).pushNamed(
FrostCreateStep2.routeName,
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
},
)

View file

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
@ -343,7 +342,10 @@ class _FrostCreateStep2State extends ConsumerState<FrostCreateStep2> {
ref.read(pFrostCreateCurrentStep.state).state = 3;
await Navigator.of(context).pushNamed(
FrostCreateStep3.routeName,
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
} catch (e, s) {
Logging.instance.log(

View file

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
@ -293,7 +292,10 @@ class _FrostCreateStep3State extends ConsumerState<FrostCreateStep3> {
ref.read(pFrostCreateCurrentStep.state).state = 4;
await Navigator.of(context).pushNamed(
FrostCreateStep4.routeName,
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
} catch (e, s) {
Logging.instance.log(

View file

@ -2,7 +2,6 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_5.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
@ -62,9 +61,12 @@ class _FrostCreateStep4State extends ConsumerState<FrostCreateStep4> {
PrimaryButton(
label: "Confirm",
onPressed: () {
ref.read(pFrostCreateCurrentStep.state).state = 4;
ref.read(pFrostCreateCurrentStep.state).state = 5;
Navigator.of(context).pushNamed(
FrostCreateStep5.routeName,
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
},
)

View file

@ -162,11 +162,11 @@ class _FrostCreateStep5State extends ConsumerState<FrostCreateStep5> {
),
);
final data = ref.read(pFrostCreateNewArgs)!;
final data = ref.read(pFrostScaffoldArgs)!;
final info = WalletInfo.createNew(
coin: data.$1.frostCurrency.coin,
name: data.$1.walletName,
coin: data.info.frostCurrency.coin,
name: data.info.walletName,
);
final wallet = await Wallet.create(
@ -206,7 +206,7 @@ class _FrostCreateStep5State extends ConsumerState<FrostCreateStep5> {
}
if (mounted) {
ref.read(pFrostCreateNewArgs)!.$3();
ref.read(pFrostScaffoldArgs)!.onSuccess();
}
} catch (e, s) {
Logging.instance.log(

View file

@ -7,18 +7,28 @@ import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_crea
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_5.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1a.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1b.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1c.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2abd.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2c.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_3abd.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_3c.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_4.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_5.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/wallets/crypto_currency/intermediate/frost_currency.dart';
typedef FrostStepRoute = ({String routeName, String title});
final pFrostCreateCurrentStep = StateProvider.autoDispose((ref) => 1);
final pFrostCreateNewArgs = StateProvider<
(
({String walletName, FrostCurrency frostCurrency}),
List<FrostStepRoute>,
VoidCallback,
)?>((ref) => null);
final pFrostScaffoldArgs = StateProvider<
({
({String walletName, FrostCurrency frostCurrency}) info,
String? walletId,
List<FrostStepRoute> stepRoutes,
VoidCallback onSuccess,
})?>((ref) => null);
abstract class FrostRouteGenerator {
static const bool useMaterialPageRoute = true;
@ -39,6 +49,42 @@ abstract class FrostRouteGenerator {
(routeName: FrostCreateStep5.routeName, title: FrostCreateStep5.title),
];
static const List<FrostStepRoute> initiateReshareStepRoutes = [
(routeName: FrostReshareStep1a.routeName, title: FrostReshareStep1a.title),
(
routeName: FrostReshareStep2abd.routeName,
title: FrostReshareStep2abd.title
),
(
routeName: FrostReshareStep3abd.routeName,
title: FrostReshareStep3abd.title
),
(routeName: FrostReshareStep4.routeName, title: FrostReshareStep4.title),
(routeName: FrostReshareStep5.routeName, title: FrostReshareStep5.title),
];
static const List<FrostStepRoute> importReshareStepRoutes = [
(routeName: FrostReshareStep1b.routeName, title: FrostReshareStep1b.title),
(
routeName: FrostReshareStep2abd.routeName,
title: FrostReshareStep2abd.title
),
(
routeName: FrostReshareStep3abd.routeName,
title: FrostReshareStep3abd.title
),
(routeName: FrostReshareStep4.routeName, title: FrostReshareStep4.title),
(routeName: FrostReshareStep5.routeName, title: FrostReshareStep5.title),
];
static const List<FrostStepRoute> joinReshareStepRoutes = [
(routeName: FrostReshareStep1c.routeName, title: FrostReshareStep1c.title),
(routeName: FrostReshareStep2c.routeName, title: FrostReshareStep2c.title),
(routeName: FrostReshareStep3c.routeName, title: FrostReshareStep3c.title),
(routeName: FrostReshareStep4.routeName, title: FrostReshareStep4.title),
(routeName: FrostReshareStep5.routeName, title: FrostReshareStep5.title),
];
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments;
@ -85,6 +131,69 @@ abstract class FrostRouteGenerator {
settings: settings,
);
case FrostReshareStep1a.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep1a(),
settings: settings,
);
case FrostReshareStep1b.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep1b(),
settings: settings,
);
case FrostReshareStep1c.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep1c(),
settings: settings,
);
case FrostReshareStep2abd.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep2abd(),
settings: settings,
);
case FrostReshareStep2c.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep2c(),
settings: settings,
);
case FrostReshareStep3abd.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep2abd(),
settings: settings,
);
case FrostReshareStep3c.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep2c(),
settings: settings,
);
case FrostReshareStep4.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep4(),
settings: settings,
);
case FrostReshareStep5.routeName:
return RouteGenerator.getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const FrostReshareStep5(),
settings: settings,
);
default:
return _routeError("");
}

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/begin_resharing_view.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
@ -15,12 +15,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.dart';
import 'package:stackwallet/widgets/background.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/simple_copy_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';
import 'package:stackwallet/widgets/detail_item.dart';
@ -28,23 +23,17 @@ import 'package:stackwallet/widgets/dialogs/simple_mobile_dialog.dart';
import 'package:stackwallet/widgets/frost_step_user_steps.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class DisplayReshareConfigView extends ConsumerStatefulWidget {
const DisplayReshareConfigView({
super.key,
required this.walletId,
});
class FrostReshareStep1a extends ConsumerStatefulWidget {
const FrostReshareStep1a({super.key});
static const String routeName = "/displayReshareConfigView";
final String walletId;
static const String routeName = "/frostReshareStep1a";
static const String title = "Resharer config";
@override
ConsumerState<DisplayReshareConfigView> createState() =>
_DisplayReshareConfigViewState();
ConsumerState<FrostReshareStep1a> createState() => _FrostReshareStep1aState();
}
class _DisplayReshareConfigViewState
extends ConsumerState<DisplayReshareConfigView> {
class _FrostReshareStep1aState extends ConsumerState<FrostReshareStep1a> {
static const info = [
"Share this config with the signing group participants as well as any new "
"participant.",
@ -67,7 +56,8 @@ class _DisplayReshareConfigViewState
try {
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as BitcoinFrostWallet;
ref.read(pWallets).getWallet(ref.read(pFrostScaffoldArgs)!.walletId!)
as BitcoinFrostWallet;
final serializedKeys = await wallet.getSerializedKeys();
if (mounted) {
@ -78,9 +68,12 @@ class _DisplayReshareConfigViewState
ref.read(pFrostResharingData).startResharerData = result;
ref.read(pFrostCreateCurrentStep.state).state = 2;
await Navigator.of(context).pushNamed(
BeginResharingView.routeName,
arguments: widget.walletId,
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
}
} catch (e, s) {
@ -215,7 +208,7 @@ class _DisplayReshareConfigViewState
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(widget.walletId)!;
.getByWalletIdSync(ref.read(pFrostScaffoldArgs)!.walletId!)!;
final myOldIndex = frostInfo.participants.indexOf(frostInfo.myName);
@ -229,62 +222,10 @@ class _DisplayReshareConfigViewState
@override
Widget build(BuildContext context) {
return ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: const DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
title: Text(
"Resharer config",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
return Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
const SizedBox(
height: 16,
),
const FrostStepUserSteps(
userSteps: info,
),
@ -354,8 +295,7 @@ class _DisplayReshareConfigViewState
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _userVerifyContinue,
onChanged: (value) => setState(
() => _userVerifyContinue = value == true,
@ -387,7 +327,6 @@ class _DisplayReshareConfigViewState
),
],
),
),
);
}
}

View file

@ -0,0 +1,349 @@
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frostdart/frostdart.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/frost_step_user_steps.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class FrostReshareStep1b extends ConsumerStatefulWidget {
const FrostReshareStep1b({
super.key,
});
static const String routeName = "/frostReshareStep1b";
static const String title = "Import reshare config";
@override
ConsumerState<FrostReshareStep1b> createState() => _FrostReshareStep1bState();
}
class _FrostReshareStep1bState extends ConsumerState<FrostReshareStep1b> {
static const info = [
"Scan the config QR code or paste the code provided by the group member who"
" is initiating resharing.",
"Wait for other participants to finish importing the config.",
"Verify that everyone has filled out their forms before continuing. If you "
"try to continue before everyone is ready, the process will be canceled.",
"Check the box and press “Start resharing”.",
];
late final TextEditingController configFieldController;
late final FocusNode configFocusNode;
bool _configEmpty = true;
bool _buttonLock = false;
bool _userVerifyContinue = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
final walletId = ref.read(pFrostScaffoldArgs)!.walletId!;
// TODO: optimize this by creating watcher providers (similar to normal WalletInfo)
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(walletId)!;
ref.read(pFrostResharingData).reset();
ref.read(pFrostResharingData).myName = frostInfo.myName;
ref.read(pFrostResharingData).resharerConfig = configFieldController.text;
String? salt;
try {
salt = Format.uint8listToString(
resharerSalt(
resharerConfig: ref.read(pFrostResharingData).resharerConfig!,
),
);
} catch (_) {
throw Exception("Bad resharer config");
}
if (frostInfo.knownSalts.contains(salt)) {
throw Exception("Duplicate config salt");
} else {
final salts = frostInfo.knownSalts.toList();
salts.add(salt);
final mainDB = ref.read(mainDBProvider);
await mainDB.isar.writeTxn(() async {
final id = frostInfo.id;
await mainDB.isar.frostWalletInfo.delete(id);
await mainDB.isar.frostWalletInfo.put(
frostInfo.copyWith(knownSalts: salts),
);
});
}
final serializedKeys = await ref.read(secureStoreProvider).read(
key: "{$walletId}_serializedFROSTKeys",
);
if (mounted) {
final result = Frost.beginResharer(
serializedKeys: serializedKeys!,
config: ref.read(pFrostResharingData).resharerConfig!,
);
ref.read(pFrostResharingData).startResharerData = result;
ref.read(pFrostCreateCurrentStep.state).state = 2;
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
@override
void initState() {
configFieldController = TextEditingController();
configFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
configFieldController.dispose();
configFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
const FrostStepUserSteps(
userSteps: info,
),
const SizedBox(height: 20),
Text(
"Enter config",
style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
),
const SizedBox(
height: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("frConfigTextFieldKey"),
controller: configFieldController,
onChanged: (_) {
setState(() {
_configEmpty = configFieldController.text.isEmpty;
});
},
focusNode: configFocusNode,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Enter config",
configFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _configEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
!_configEmpty
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Config Field.",
key: const Key("frConfigClearButtonKey"),
onTap: () {
configFieldController.text = "";
setState(() {
_configEmpty = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Config Field Input.",
key: const Key("frConfigPasteButtonKey"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
configFieldController.text =
data.text!.trim();
}
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
},
child: _configEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_configEmpty)
TextFieldIconButton(
semanticsLabel:
"Scan QR Button. Opens Camera For Scanning QR Code.",
key: const Key("frConfigScanQrButtonKey"),
onTap: () async {
try {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
final qrResult = await BarcodeScanner.scan();
configFieldController.text =
qrResult.rawContent;
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
)
],
),
),
),
),
),
),
const SizedBox(
height: 16,
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
setState(() {
_userVerifyContinue = !_userVerifyContinue;
});
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _userVerifyContinue,
onChanged: (value) => setState(
() => _userVerifyContinue = value == true,
),
),
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"I have verified that everyone has imported the config",
style: STextStyles.w500_14(context),
),
),
],
),
),
),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Start resharing",
enabled: !_configEmpty && _userVerifyContinue,
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
}
await _onPressed();
},
),
],
),
);
}
}

View file

@ -0,0 +1,425 @@
import 'dart:async';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/models/incomplete_frost_wallet.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/frost_step_user_steps.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class FrostReshareStep1c extends ConsumerStatefulWidget {
const FrostReshareStep1c({super.key});
static const String routeName = "/frostReshareStep1c";
static const String title = "Import reshare config";
@override
ConsumerState<FrostReshareStep1c> createState() => _FrostReshareStep1cState();
}
class _FrostReshareStep1cState extends ConsumerState<FrostReshareStep1c> {
static const info = [
"Scan the config QR code or paste the code provided by the group creator.",
"Enter your name EXACTLY as the group creator entered it. When in doubt, "
"double check with them. The names are case-sensitive.",
"Wait for other participants to finish entering their information.",
"Verify that everyone has filled out their forms before continuing. If you "
"try to continue before everyone is ready, the process could be canceled.",
"Check the box and press “Join group”.",
];
late final TextEditingController myNameFieldController, configFieldController;
late final FocusNode myNameFocusNode, configFocusNode;
bool _nameEmpty = true,
_configEmpty = true,
_userVerifyContinue = false,
_buttonLock = false;
Future<IncompleteFrostWallet> _createWallet() async {
final data = ref.read(pFrostScaffoldArgs)!;
final info = WalletInfo.createNew(
name: data.info.walletName,
coin: data.info.frostCurrency.coin,
);
final wallet = IncompleteFrostWallet();
wallet.info = info;
return wallet;
}
@override
void initState() {
myNameFieldController = TextEditingController();
configFieldController = TextEditingController();
myNameFocusNode = FocusNode();
configFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
myNameFieldController.dispose();
configFieldController.dispose();
myNameFocusNode.dispose();
configFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const FrostStepUserSteps(
userSteps: info,
),
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("frMyNameTextFieldKey"),
controller: myNameFieldController,
onChanged: (_) {
setState(() {
_nameEmpty = myNameFieldController.text.isEmpty;
});
},
focusNode: myNameFocusNode,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"My name",
myNameFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _nameEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
!_nameEmpty
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Config Field.",
key: const Key("frMyNameClearButtonKey"),
onTap: () {
myNameFieldController.text = "";
setState(() {
_nameEmpty = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Name Field.",
key: const Key("frMyNamePasteButtonKey"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
myNameFieldController.text =
data.text!.trim();
}
setState(() {
_nameEmpty =
myNameFieldController.text.isEmpty;
});
},
child: _nameEmpty
? const ClipboardIcon()
: const XIcon(),
),
],
),
),
),
),
),
),
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("frConfigTextFieldKey"),
controller: configFieldController,
onChanged: (_) {
setState(() {
_configEmpty = configFieldController.text.isEmpty;
});
},
focusNode: configFocusNode,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Enter config",
configFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _configEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
!_configEmpty
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Config Field.",
key: const Key("frConfigClearButtonKey"),
onTap: () {
configFieldController.text = "";
setState(() {
_configEmpty = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Config Field Input.",
key: const Key("frConfigPasteButtonKey"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
configFieldController.text =
data.text!.trim();
}
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
},
child: _configEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_configEmpty)
TextFieldIconButton(
semanticsLabel:
"Scan QR Button. Opens Camera For Scanning QR Code.",
key: const Key("frConfigScanQrButtonKey"),
onTap: () async {
try {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
final qrResult = await BarcodeScanner.scan();
configFieldController.text =
qrResult.rawContent;
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
)
],
),
),
),
),
),
),
const SizedBox(
height: 16,
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
setState(() {
_userVerifyContinue = !_userVerifyContinue;
});
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _userVerifyContinue,
onChanged: (value) => setState(
() => _userVerifyContinue = value == true,
),
),
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"I have verified that everyone has joined the group",
style: STextStyles.w500_14(context),
),
),
],
),
),
),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Join group",
enabled: _userVerifyContinue && !_nameEmpty && !_configEmpty,
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
}
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
ref.read(pFrostResharingData).reset();
ref.read(pFrostResharingData).myName =
myNameFieldController.text;
ref.read(pFrostResharingData).resharerConfig =
configFieldController.text;
if (!ref
.read(pFrostResharingData)
.configData!
.newParticipants
.contains(ref.read(pFrostResharingData).myName!)) {
ref.read(pFrostResharingData).reset();
return await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "My name not found in config participants",
desktopPopRootNavigator: Util.isDesktop,
),
);
}
Exception? ex;
final wallet = await showLoading(
whileFuture: _createWallet(),
context: context,
message: "Setting up wallet",
isDesktop: Util.isDesktop,
onException: (e) => ex = e,
);
if (ex != null) {
throw ex!;
}
if (context.mounted) {
ref.read(pFrostResharingData).incompleteWallet = wallet!;
final data = ref.read(pFrostScaffoldArgs)!;
ref.read(pFrostScaffoldArgs.state).state = (
info: data.info,
walletId: wallet.walletId,
stepRoutes: data.stepRoutes,
onSuccess: data.onSuccess,
);
ref.read(pFrostCreateCurrentStep.state).state = 2;
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (context.mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
},
)
],
),
);
}
}

View file

@ -0,0 +1,343 @@
import 'dart:async';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.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/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class FrostReshareStep2abd extends ConsumerStatefulWidget {
const FrostReshareStep2abd({super.key});
static const String routeName = "/FrostReshareStep2abd";
static const String title = "Resharers";
@override
ConsumerState<FrostReshareStep2abd> createState() =>
_FrostReshareStep2abdState();
}
class _FrostReshareStep2abdState extends ConsumerState<FrostReshareStep2abd> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<int> resharerIndexes;
late final int myResharerIndexIndex;
late final String myResharerStart;
late final bool amOutgoingParticipant;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
if (!amOutgoingParticipant) {
// collect resharer strings
final resharerStarts = controllers.map((e) => e.text).toList();
if (myResharerIndexIndex >= 0) {
// only insert my own at the correct index if I am a resharer
resharerStarts.insert(myResharerIndexIndex, myResharerStart);
}
final result = Frost.beginReshared(
myName: ref.read(pFrostResharingData).myName!,
resharerConfig: ref.read(pFrostResharingData).resharerConfig!,
resharerStarts: resharerStarts,
);
ref.read(pFrostResharingData).startResharedData = result;
}
ref.read(pFrostCreateCurrentStep.state).state = 3;
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
@override
void initState() {
// TODO: optimize this by creating watcher providers (similar to normal WalletInfo)
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(ref.read(pFrostScaffoldArgs)!.walletId!)!;
final myOldIndex =
frostInfo.participants.indexOf(ref.read(pFrostResharingData).myName!);
myResharerStart =
ref.read(pFrostResharingData).startResharerData!.resharerStart;
resharerIndexes = ref.read(pFrostResharingData).configData!.resharers;
myResharerIndexIndex = resharerIndexes.indexOf(myOldIndex);
if (myResharerIndexIndex >= 0) {
// remove my name for now as we don't need a text field for it
resharerIndexes.removeAt(myResharerIndexIndex);
}
amOutgoingParticipant = !ref
.read(pFrostResharingData)
.configData!
.newParticipants
.contains(ref.read(pFrostResharingData).myName!);
for (int i = 0; i < resharerIndexes.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: myResharerStart,
size: 220,
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
const SizedBox(
height: 12,
),
DetailItem(
title: "My resharer",
detail: myResharerStart,
button: Util.isDesktop
? IconCopyButton(
data: myResharerStart,
)
: SimpleCopyButton(
data: myResharerStart,
),
),
const SizedBox(
height: 12,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < resharerIndexes.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostResharerTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter index "
"${resharerIndexes[i]}"
"'s resharer",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Resharer Field Input.",
key: Key(
"frostResharerClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Resharer Field Input.",
key: Key(
"frostResharerPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key(
"frostCommitmentsScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 12,
),
PrimaryButton(
label: "Continue",
enabled: amOutgoingParticipant ||
!fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
);
}
}

View file

@ -0,0 +1,265 @@
import 'dart:async';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class FrostReshareStep2c extends ConsumerStatefulWidget {
const FrostReshareStep2c({super.key});
static const String routeName = "/FrostReshareStep2c";
static const String title = "Resharers";
@override
ConsumerState<FrostReshareStep2c> createState() => _FrostReshareStep2cState();
}
class _FrostReshareStep2cState extends ConsumerState<FrostReshareStep2c> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<int> resharerIndexes;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
// collect resharer strings
final resharerStarts = controllers.map((e) => e.text).toList();
final result = Frost.beginReshared(
myName: ref.read(pFrostResharingData).myName!,
resharerConfig: ref.read(pFrostResharingData).resharerConfig!,
resharerStarts: resharerStarts,
);
ref.read(pFrostResharingData).startResharedData = result;
ref.read(pFrostCreateCurrentStep.state).state = 3;
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
} finally {
_buttonLock = false;
}
}
@override
void initState() {
resharerIndexes = ref.read(pFrostResharingData).configData!.resharers;
for (int i = 0; i < resharerIndexes.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < resharerIndexes.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostResharerTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter index "
"${resharerIndexes[i]}"
"'s resharer",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Resharer Field Input.",
key: Key(
"frostResharerClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Resharer Field Input.",
key: Key(
"frostResharerPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key(
"frostCommitmentsScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Continue",
enabled: !fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
);
}
}

View file

@ -0,0 +1,331 @@
import 'dart:ffi';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.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/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class FrostReshareStep3abd extends ConsumerStatefulWidget {
const FrostReshareStep3abd({super.key});
static const String routeName = "/frostReshareStep3abd";
static const String title = "Encryption keys";
@override
ConsumerState<FrostReshareStep3abd> createState() =>
_FrostReshareStep3abdState();
}
class _FrostReshareStep3abdState extends ConsumerState<FrostReshareStep3abd> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<String> newParticipants;
late final int myIndex;
late final String? myEncryptionKey;
late final bool amOutgoingParticipant;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
// collect encryptionKeys strings and insert my own at the correct index
final encryptionKeys = controllers.map((e) => e.text).toList();
if (!amOutgoingParticipant) {
encryptionKeys.insert(myIndex, myEncryptionKey!);
}
final result = Frost.finishResharer(
machine: ref.read(pFrostResharingData).startResharerData!.machine.ref,
encryptionKeysOfResharedTo: encryptionKeys,
);
ref.read(pFrostResharingData).resharerComplete = result;
ref.read(pFrostCreateCurrentStep.state).state = 4;
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
} finally {
_buttonLock = false;
}
}
@override
void initState() {
myEncryptionKey =
ref.read(pFrostResharingData).startResharedData?.resharedStart;
newParticipants = ref.read(pFrostResharingData).configData!.newParticipants;
myIndex = newParticipants.indexOf(ref.read(pFrostResharingData).myName!);
if (myIndex >= 0) {
// remove my name for now as we don't need a text field for it
newParticipants.removeAt(myIndex);
}
if (myEncryptionKey == null && myIndex == -1) {
amOutgoingParticipant = true;
} else if (myEncryptionKey != null && myIndex >= 0) {
amOutgoingParticipant = false;
} else {
throw Exception("Invalid resharing state");
}
for (int i = 0; i < newParticipants.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
if (!amOutgoingParticipant)
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: myEncryptionKey!,
size: 220,
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
if (!amOutgoingParticipant)
const SizedBox(
height: 12,
),
if (!amOutgoingParticipant)
DetailItem(
title: "My encryption key",
detail: myEncryptionKey!,
button: Util.isDesktop
? IconCopyButton(
data: myEncryptionKey!,
)
: SimpleCopyButton(
data: myEncryptionKey!,
),
),
if (!amOutgoingParticipant)
const SizedBox(
height: 12,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < newParticipants.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostEncryptionKeyTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter "
"${newParticipants[i]}"
"'s encryption key",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key(
"frostCommitmentsScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 12,
),
PrimaryButton(
label: "Continue",
enabled: !fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
);
}
}

View file

@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/util.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';
class FrostReshareStep3c extends ConsumerStatefulWidget {
const FrostReshareStep3c({super.key});
static const String routeName = "/frostReshareStep3c";
static const String title = "Encryption keys";
@override
ConsumerState<FrostReshareStep3c> createState() => _FrostReshareStep3bState();
}
class _FrostReshareStep3bState extends ConsumerState<FrostReshareStep3c> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
size: 220,
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
const SizedBox(
height: 16,
),
DetailItem(
title: "My encryption key",
detail:
ref.watch(pFrostResharingData).startResharedData!.resharedStart,
button: Util.isDesktop
? IconCopyButton(
data: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
)
: SimpleCopyButton(
data: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
),
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Continue",
onPressed: () {
ref.read(pFrostCreateCurrentStep.state).state = 4;
Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
},
),
],
),
);
}
}

View file

@ -0,0 +1,369 @@
import 'dart:ffi';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.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/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
// was FinishResharingView
class FrostReshareStep4 extends ConsumerStatefulWidget {
const FrostReshareStep4({super.key});
static const String routeName = "/frostReshareStep4";
static const String title = "Resharer completes";
@override
ConsumerState<FrostReshareStep4> createState() => _FrostReshareStep4State();
}
class _FrostReshareStep4State extends ConsumerState<FrostReshareStep4> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<int> resharerIndexes;
late final String myName;
late final int? myResharerIndexIndex;
late final String? myResharerComplete;
late final bool amOutgoingParticipant;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
if (amOutgoingParticipant) {
ref.read(pFrostResharingData).reset();
Navigator.of(context).popUntil(
ModalRoute.withName(
Util.isDesktop ? DesktopWalletView.routeName : WalletView.routeName,
),
);
} else {
// collect resharer completes strings and insert my own at the correct index
final resharerCompletes = controllers.map((e) => e.text).toList();
if (myResharerIndexIndex != null && myResharerComplete != null) {
resharerCompletes.insert(myResharerIndexIndex!, myResharerComplete!);
}
final data = Frost.finishReshared(
prior: ref.read(pFrostResharingData).startResharedData!.prior.ref,
resharerCompletes: resharerCompletes,
);
ref.read(pFrostResharingData).newWalletData = data;
ref.read(pFrostCreateCurrentStep.state).state = 5;
await Navigator.of(context).pushNamed(
ref
.read(pFrostScaffoldArgs)!
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
.routeName,
);
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
@override
void initState() {
final amNewParticipant =
ref.read(pFrostResharingData).startResharerData == null &&
ref.read(pFrostResharingData).incompleteWallet != null &&
ref.read(pFrostResharingData).incompleteWallet?.walletId ==
ref.read(pFrostScaffoldArgs)!.walletId!;
myName = ref.read(pFrostResharingData).myName!;
resharerIndexes = ref.read(pFrostResharingData).configData!.resharers;
if (amNewParticipant) {
myResharerComplete = null;
myResharerIndexIndex = null;
amOutgoingParticipant = false;
} else {
myResharerComplete = ref.read(pFrostResharingData).resharerComplete!;
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(ref.read(pFrostScaffoldArgs)!.walletId!)!;
final myOldIndex =
frostInfo.participants.indexOf(ref.read(pFrostResharingData).myName!);
myResharerIndexIndex = resharerIndexes.indexOf(myOldIndex);
if (myResharerIndexIndex! >= 0) {
// remove my name for now as we don't need a text field for it
resharerIndexes.removeAt(myResharerIndexIndex!);
}
amOutgoingParticipant = !ref
.read(pFrostResharingData)
.configData!
.newParticipants
.contains(ref.read(pFrostResharingData).myName!);
}
for (int i = 0; i < resharerIndexes.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
if (myResharerComplete != null)
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: myResharerComplete!,
size: 220,
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
if (myResharerComplete != null)
const SizedBox(
height: 16,
),
if (myResharerComplete != null)
DetailItem(
title: "My resharer complete",
detail: myResharerComplete!,
button: Util.isDesktop
? IconCopyButton(
data: myResharerComplete!,
)
: SimpleCopyButton(
data: myResharerComplete!,
),
),
if (!amOutgoingParticipant)
const SizedBox(
height: 16,
),
if (!amOutgoingParticipant)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < resharerIndexes.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostEncryptionKeyTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter index "
"${resharerIndexes[i]}"
"'s resharer complete",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key("frostScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context)
.unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
PrimaryButton(
label: amOutgoingParticipant ? "Exit" : "Complete",
enabled: amOutgoingParticipant ||
!fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
);
}
}

View file

@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/node_service_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.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/stack_dialog.dart';
// was VerifyUpdatedWalletView
class FrostReshareStep5 extends ConsumerStatefulWidget {
const FrostReshareStep5({super.key});
static const String routeName = "/frostReshareStep5";
static const String title = "Verify";
@override
ConsumerState<FrostReshareStep5> createState() =>
_FrostReshareStep5State();
}
class _FrostReshareStep5State extends ConsumerState<FrostReshareStep5> {
late final String config;
late final String serializedKeys;
late final String reshareId;
late final bool isNew;
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
Exception? ex;
final BitcoinFrostWallet wallet;
if (isNew) {
wallet = await ref
.read(pFrostResharingData)
.incompleteWallet!
.toBitcoinFrostWallet(
mainDB: ref.read(mainDBProvider),
secureStorageInterface: ref.read(secureStoreProvider),
nodeService: ref.read(nodeServiceChangeNotifierProvider),
prefs: ref.read(prefsChangeNotifierProvider),
);
await wallet.info.setMnemonicVerified(
isar: ref.read(mainDBProvider).isar,
);
ref.read(pWallets).addWallet(wallet);
} else {
wallet = ref
.read(pWallets)
.getWallet(ref.read(pFrostScaffoldArgs)!.walletId!)
as BitcoinFrostWallet;
}
if (mounted) {
await showLoading(
whileFuture: wallet.updateWithResharedData(
serializedKeys: serializedKeys,
multisigConfig: config,
isNewWallet: isNew,
),
context: context,
message: isNew ? "Creating wallet" : "Updating wallet data",
isDesktop: Util.isDesktop,
onException: (e) => ex = e,
);
if (ex != null) {
throw ex!;
}
if (mounted) {
ref.read(pFrostResharingData).reset();
Navigator.of(context).popUntil(
ModalRoute.withName(
_popUntilPath,
),
);
}
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
String get _popUntilPath => isNew
? Util.isDesktop
? DesktopHomeView.routeName
: HomeView.routeName
: Util.isDesktop
? DesktopWalletView.routeName
: WalletView.routeName;
@override
void initState() {
config = ref.read(pFrostResharingData).newWalletData!.multisigConfig;
serializedKeys =
ref.read(pFrostResharingData).newWalletData!.serializedKeys;
reshareId = ref.read(pFrostResharingData).newWalletData!.resharedId;
isNew = ref.read(pFrostResharingData).incompleteWallet != null &&
ref.read(pFrostResharingData).incompleteWallet!.walletId ==
ref.read(pFrostScaffoldArgs)!.walletId!;
super.initState();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
"Ensure your reshare ID matches that of each other participant",
style: STextStyles.pageTitleH2(context),
),
const SizedBox(
height: 12,
),
DetailItem(
title: "ID",
detail: reshareId,
button: Util.isDesktop
? IconCopyButton(
data: reshareId,
)
: SimpleCopyButton(
data: reshareId,
),
),
const SizedBox(
height: 12,
),
const SizedBox(
height: 12,
),
Text(
"Back up your keys and config",
style: STextStyles.pageTitleH2(context),
),
const SizedBox(
height: 12,
),
DetailItem(
title: "Config",
detail: config,
button: Util.isDesktop
? IconCopyButton(
data: config,
)
: SimpleCopyButton(
data: config,
),
),
const SizedBox(
height: 12,
),
DetailItem(
title: "Keys",
detail: serializedKeys,
button: Util.isDesktop
? IconCopyButton(
data: serializedKeys,
)
: SimpleCopyButton(
data: serializedKeys,
),
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 12,
),
PrimaryButton(
label: "Confirm",
onPressed: _onPressed,
),
],
),
);
}
}

View file

@ -10,18 +10,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/frost_scaffold.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/frost_participants_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1a/initiate_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1b/import_reshare_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/initiate_resharing_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -146,9 +149,26 @@ class FrostMSWalletOptionsView extends ConsumerWidget {
ref.read(pFrostMyName.state).state = frostInfo.myName;
final wallet = ref.read(pWallets).getWallet(walletId)
as BitcoinFrostWallet;
ref.read(pFrostScaffoldArgs.state).state = (
info: (
walletName: wallet.info.name,
frostCurrency: wallet.cryptoCurrency,
),
walletId: null, // no wallet id yet
stepRoutes: FrostRouteGenerator.joinReshareStepRoutes,
onSuccess: () {
// successful completion of steps
// TODO
ref.read(pFrostScaffoldArgs.state).state = null;
}
);
Navigator.of(context).pushNamed(
ImportReshareConfigView.routeName,
arguments: walletId,
FrostStepScaffold.routeName,
);
},
),

View file

@ -4,10 +4,12 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frostdart/frostdart.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1a/display_reshare_config_view.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/frost_scaffold.dart';
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/format.dart';
@ -15,6 +17,7 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -120,10 +123,25 @@ class _CompleteReshareConfigViewState
ref.read(pFrostResharingData).myName = myName;
ref.read(pFrostResharingData).resharerConfig = config;
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as BitcoinFrostWallet;
ref.read(pFrostScaffoldArgs.state).state = (
info: (
walletName: wallet.info.name,
frostCurrency: wallet.cryptoCurrency,
),
walletId: wallet.walletId,
stepRoutes: FrostRouteGenerator.initiateReshareStepRoutes,
onSuccess: () {
// successful completion of steps
// TODO
}
);
if (mounted) {
await Navigator.of(context).pushNamed(
DisplayReshareConfigView.routeName,
arguments: widget.walletId,
FrostStepScaffold.routeName,
);
}
} catch (e, s) {

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1a/complete_reshare_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/complete_reshare_config_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';

View file

@ -1,437 +0,0 @@
import 'dart:ffi';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/verify_updated_wallet_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/widgets/background.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/simple_copy_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/detail_item.dart';
import 'package:stackwallet/widgets/frost_mascot.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class FinishResharingView extends ConsumerStatefulWidget {
const FinishResharingView({
super.key,
required this.walletId,
});
static const String routeName = "/finishResharingView";
final String walletId;
@override
ConsumerState<FinishResharingView> createState() =>
_FinishResharingViewState();
}
class _FinishResharingViewState extends ConsumerState<FinishResharingView> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<int> resharerIndexes;
late final String myName;
late final int? myResharerIndexIndex;
late final String? myResharerComplete;
late final bool amOutgoingParticipant;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
if (amOutgoingParticipant) {
ref.read(pFrostResharingData).reset();
Navigator.of(context).popUntil(
ModalRoute.withName(
Util.isDesktop ? DesktopWalletView.routeName : WalletView.routeName,
),
);
} else {
// collect resharer completes strings and insert my own at the correct index
final resharerCompletes = controllers.map((e) => e.text).toList();
if (myResharerIndexIndex != null && myResharerComplete != null) {
resharerCompletes.insert(myResharerIndexIndex!, myResharerComplete!);
}
final data = Frost.finishReshared(
prior: ref.read(pFrostResharingData).startResharedData!.prior.ref,
resharerCompletes: resharerCompletes,
);
ref.read(pFrostResharingData).newWalletData = data;
await Navigator.of(context).pushNamed(
VerifyUpdatedWalletView.routeName,
arguments: widget.walletId,
);
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
@override
void initState() {
final amNewParticipant =
ref.read(pFrostResharingData).startResharerData == null &&
ref.read(pFrostResharingData).incompleteWallet != null &&
ref.read(pFrostResharingData).incompleteWallet?.walletId ==
widget.walletId;
myName = ref.read(pFrostResharingData).myName!;
resharerIndexes = ref.read(pFrostResharingData).configData!.resharers;
if (amNewParticipant) {
myResharerComplete = null;
myResharerIndexIndex = null;
amOutgoingParticipant = false;
} else {
myResharerComplete = ref.read(pFrostResharingData).resharerComplete!;
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(widget.walletId)!;
final myOldIndex =
frostInfo.participants.indexOf(ref.read(pFrostResharingData).myName!);
myResharerIndexIndex = resharerIndexes.indexOf(myOldIndex);
if (myResharerIndexIndex! >= 0) {
// remove my name for now as we don't need a text field for it
resharerIndexes.removeAt(myResharerIndexIndex!);
}
amOutgoingParticipant = !ref
.read(pFrostResharingData)
.configData!
.newParticipants
.contains(ref.read(pFrostResharingData).myName!);
}
for (int i = 0; i < resharerIndexes.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(),
trailing: FrostMascot(
title: 'Lorem ipsum',
body:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ',
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
title: Text(
"Resharer completes",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
if (myResharerComplete != null)
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: myResharerComplete!,
size: 220,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
if (myResharerComplete != null) const _Div(),
if (myResharerComplete != null)
DetailItem(
title: "My resharer complete",
detail: myResharerComplete!,
button: Util.isDesktop
? IconCopyButton(
data: myResharerComplete!,
)
: SimpleCopyButton(
data: myResharerComplete!,
),
),
if (!amOutgoingParticipant) const _Div(),
if (!amOutgoingParticipant)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < resharerIndexes.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostEncryptionKeyTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter index "
"${resharerIndexes[i]}"
"'s resharer complete",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key("frostScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context)
.unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const _Div(),
PrimaryButton(
label: amOutgoingParticipant ? "Exit" : "Complete",
enabled: amOutgoingParticipant ||
!fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
),
);
}
}
class _Div extends StatelessWidget {
const _Div({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
height: 12,
);
}
}

View file

@ -1,401 +0,0 @@
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frostdart/frostdart.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/begin_resharing_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.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/frost_step_user_steps.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class ImportReshareConfigView extends ConsumerStatefulWidget {
const ImportReshareConfigView({
super.key,
required this.walletId,
});
static const String routeName = "/importReshareConfigView";
final String walletId;
@override
ConsumerState<ImportReshareConfigView> createState() =>
_ImportReshareConfigViewState();
}
class _ImportReshareConfigViewState
extends ConsumerState<ImportReshareConfigView> {
static const info = [
"Scan the config QR code or paste the code provided by the group member who"
" is initiating resharing.",
"Wait for other participants to finish importing the config.",
"Verify that everyone has filled out their forms before continuing. If you "
"try to continue before everyone is ready, the process will be canceled.",
"Check the box and press “Start resharing”.",
];
late final TextEditingController configFieldController;
late final FocusNode configFocusNode;
bool _configEmpty = true;
bool _buttonLock = false;
bool _userVerifyContinue = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
// TODO: optimize this by creating watcher providers (similar to normal WalletInfo)
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(widget.walletId)!;
ref.read(pFrostResharingData).reset();
ref.read(pFrostResharingData).myName = frostInfo.myName;
ref.read(pFrostResharingData).resharerConfig = configFieldController.text;
String? salt;
try {
salt = Format.uint8listToString(
resharerSalt(
resharerConfig: ref.read(pFrostResharingData).resharerConfig!,
),
);
} catch (_) {
throw Exception("Bad resharer config");
}
if (frostInfo.knownSalts.contains(salt)) {
throw Exception("Duplicate config salt");
} else {
final salts = frostInfo.knownSalts.toList();
salts.add(salt);
final mainDB = ref.read(mainDBProvider);
await mainDB.isar.writeTxn(() async {
final id = frostInfo.id;
await mainDB.isar.frostWalletInfo.delete(id);
await mainDB.isar.frostWalletInfo.put(
frostInfo.copyWith(knownSalts: salts),
);
});
}
final serializedKeys = await ref.read(secureStoreProvider).read(
key: "{${widget.walletId}}_serializedFROSTKeys",
);
if (mounted) {
final result = Frost.beginResharer(
serializedKeys: serializedKeys!,
config: ref.read(pFrostResharingData).resharerConfig!,
);
ref.read(pFrostResharingData).startResharerData = result;
await Navigator.of(context).pushNamed(
BeginResharingView.routeName,
arguments: widget.walletId,
);
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
@override
void initState() {
configFieldController = TextEditingController();
configFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
configFieldController.dispose();
configFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: const DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: const AppBarBackButton(),
title: Text(
"Import FROST reshare config",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
const FrostStepUserSteps(
userSteps: info,
),
const SizedBox(height: 20),
Text(
"Enter config",
style: STextStyles.w500_14(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
),
const SizedBox(
height: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("frConfigTextFieldKey"),
controller: configFieldController,
onChanged: (_) {
setState(() {
_configEmpty = configFieldController.text.isEmpty;
});
},
focusNode: configFocusNode,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Enter config",
configFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _configEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
!_configEmpty
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Config Field.",
key: const Key("frConfigClearButtonKey"),
onTap: () {
configFieldController.text = "";
setState(() {
_configEmpty = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Config Field Input.",
key: const Key("frConfigPasteButtonKey"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
configFieldController.text =
data.text!.trim();
}
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
},
child: _configEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_configEmpty)
TextFieldIconButton(
semanticsLabel:
"Scan QR Button. Opens Camera For Scanning QR Code.",
key: const Key("frConfigScanQrButtonKey"),
onTap: () async {
try {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
final qrResult = await BarcodeScanner.scan();
configFieldController.text =
qrResult.rawContent;
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
)
],
),
),
),
),
),
),
const SizedBox(
height: 16,
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
setState(() {
_userVerifyContinue = !_userVerifyContinue;
});
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _userVerifyContinue,
onChanged: (value) => setState(
() => _userVerifyContinue = value == true,
),
),
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"I have verified that everyone has imported the config",
style: STextStyles.w500_14(context),
),
),
],
),
),
),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Start resharing",
enabled: !_configEmpty && _userVerifyContinue,
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
}
await _onPressed();
},
),
],
),
),
);
}
}

View file

@ -1,439 +0,0 @@
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/continue_resharing_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
import 'package:stackwallet/widgets/background.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/simple_copy_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/detail_item.dart';
import 'package:stackwallet/widgets/dialogs/frost/frost_interruption_dialog.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class BeginResharingView extends ConsumerStatefulWidget {
const BeginResharingView({
super.key,
required this.walletId,
});
static const String routeName = "/beginResharingView";
final String walletId;
@override
ConsumerState<BeginResharingView> createState() => _BeginResharingViewState();
}
class _BeginResharingViewState extends ConsumerState<BeginResharingView> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<int> resharerIndexes;
late final int myResharerIndexIndex;
late final String myResharerStart;
late final bool amOutgoingParticipant;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
if (!amOutgoingParticipant) {
// collect resharer strings
final resharerStarts = controllers.map((e) => e.text).toList();
if (myResharerIndexIndex >= 0) {
// only insert my own at the correct index if I am a resharer
resharerStarts.insert(myResharerIndexIndex, myResharerStart);
}
final result = Frost.beginReshared(
myName: ref.read(pFrostResharingData).myName!,
resharerConfig: ref.read(pFrostResharingData).resharerConfig!,
resharerStarts: resharerStarts,
);
ref.read(pFrostResharingData).startResharedData = result;
}
await Navigator.of(context).pushNamed(
ContinueResharingView.routeName,
arguments: widget.walletId,
);
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
@override
void initState() {
// TODO: optimize this by creating watcher providers (similar to normal WalletInfo)
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(widget.walletId)!;
final myOldIndex =
frostInfo.participants.indexOf(ref.read(pFrostResharingData).myName!);
myResharerStart =
ref.read(pFrostResharingData).startResharerData!.resharerStart;
resharerIndexes = ref.read(pFrostResharingData).configData!.resharers;
myResharerIndexIndex = resharerIndexes.indexOf(myOldIndex);
if (myResharerIndexIndex >= 0) {
// remove my name for now as we don't need a text field for it
resharerIndexes.removeAt(myResharerIndexIndex);
}
amOutgoingParticipant = !ref
.read(pFrostResharingData)
.configData!
.newParticipants
.contains(ref.read(pFrostResharingData).myName!);
for (int i = 0; i < resharerIndexes.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: Util.isDesktop
? DesktopWalletView.routeName
: WalletView.routeName,
),
);
return false;
},
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: DesktopWalletView.routeName,
),
);
},
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: WalletView.routeName,
),
);
},
),
title: Text(
"Resharers",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: myResharerStart,
size: 220,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
const _Div(),
DetailItem(
title: "My resharer",
detail: myResharerStart,
button: Util.isDesktop
? IconCopyButton(
data: myResharerStart,
)
: SimpleCopyButton(
data: myResharerStart,
),
),
const _Div(),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < resharerIndexes.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostResharerTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter index "
"${resharerIndexes[i]}"
"'s resharer",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Resharer Field Input.",
key: Key(
"frostResharerClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Resharer Field Input.",
key: Key(
"frostResharerPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key(
"frostCommitmentsScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context)
.unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const _Div(),
PrimaryButton(
label: "Continue",
enabled: amOutgoingParticipant ||
!fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
),
),
);
}
}
class _Div extends StatelessWidget {
const _Div({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
height: 12,
);
}
}

View file

@ -1,429 +0,0 @@
import 'dart:ffi';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/finish_resharing_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.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/simple_copy_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/detail_item.dart';
import 'package:stackwallet/widgets/dialogs/frost/frost_interruption_dialog.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class ContinueResharingView extends ConsumerStatefulWidget {
const ContinueResharingView({
super.key,
required this.walletId,
});
static const String routeName = "/continueResharingView";
final String walletId;
@override
ConsumerState<ContinueResharingView> createState() =>
_ContinueResharingViewState();
}
class _ContinueResharingViewState extends ConsumerState<ContinueResharingView> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<String> newParticipants;
late final int myIndex;
late final String? myEncryptionKey;
late final bool amOutgoingParticipant;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
// collect encryptionKeys strings and insert my own at the correct index
final encryptionKeys = controllers.map((e) => e.text).toList();
if (!amOutgoingParticipant) {
encryptionKeys.insert(myIndex, myEncryptionKey!);
}
final result = Frost.finishResharer(
machine: ref.read(pFrostResharingData).startResharerData!.machine.ref,
encryptionKeysOfResharedTo: encryptionKeys,
);
ref.read(pFrostResharingData).resharerComplete = result;
await Navigator.of(context).pushNamed(
FinishResharingView.routeName,
arguments: widget.walletId,
);
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
} finally {
_buttonLock = false;
}
}
@override
void initState() {
myEncryptionKey =
ref.read(pFrostResharingData).startResharedData?.resharedStart;
newParticipants = ref.read(pFrostResharingData).configData!.newParticipants;
myIndex = newParticipants.indexOf(ref.read(pFrostResharingData).myName!);
if (myIndex >= 0) {
// remove my name for now as we don't need a text field for it
newParticipants.removeAt(myIndex);
}
if (myEncryptionKey == null && myIndex == -1) {
amOutgoingParticipant = true;
} else if (myEncryptionKey != null && myIndex >= 0) {
amOutgoingParticipant = false;
} else {
throw Exception("Invalid resharing state");
}
for (int i = 0; i < newParticipants.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: Util.isDesktop
? DesktopWalletView.routeName
: WalletView.routeName,
),
);
return false;
},
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: DesktopWalletView.routeName,
),
);
},
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: WalletView.routeName,
),
);
},
),
title: Text(
"Encryption keys",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
if (!amOutgoingParticipant)
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: myEncryptionKey!,
size: 220,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
if (!amOutgoingParticipant) const _Div(),
if (!amOutgoingParticipant)
DetailItem(
title: "My encryption key",
detail: myEncryptionKey!,
button: Util.isDesktop
? IconCopyButton(
data: myEncryptionKey!,
)
: SimpleCopyButton(
data: myEncryptionKey!,
),
),
if (!amOutgoingParticipant) const _Div(),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < newParticipants.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostEncryptionKeyTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter "
"${newParticipants[i]}"
"'s encryption key",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Encryption Key Field Input.",
key: Key(
"frostEncryptionKeyPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key(
"frostCommitmentsScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context)
.unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const _Div(),
PrimaryButton(
label: "Continue",
enabled: !fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
),
),
);
}
}
class _Div extends StatelessWidget {
const _Div({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
height: 12,
);
}
}

View file

@ -1,198 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/finish_resharing_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.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/simple_copy_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/detail_item.dart';
import 'package:stackwallet/widgets/dialogs/frost/frost_interruption_dialog.dart';
import 'package:stackwallet/widgets/frost_mascot.dart';
class NewContinueSharingView extends ConsumerStatefulWidget {
const NewContinueSharingView({
super.key,
required this.walletId,
});
static const String routeName = "/NewContinueSharingView";
final String walletId;
@override
ConsumerState<NewContinueSharingView> createState() =>
_NewContinueSharingViewState();
}
class _NewContinueSharingViewState
extends ConsumerState<NewContinueSharingView> {
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName:
Util.isDesktop ? DesktopHomeView.routeName : HomeView.routeName,
),
);
return false;
},
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: DesktopHomeView.routeName,
),
);
},
),
trailing: FrostMascot(
title: 'Lorem ipsum',
body:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ',
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: HomeView.routeName,
),
);
},
),
title: Text(
"Encryption keys",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
SizedBox(
height: 220,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
QrImageView(
data: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
size: 220,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
],
),
),
const _Div(),
DetailItem(
title: "My encryption key",
detail: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
button: Util.isDesktop
? IconCopyButton(
data: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
)
: SimpleCopyButton(
data: ref
.watch(pFrostResharingData)
.startResharedData!
.resharedStart,
),
),
if (!Util.isDesktop) const Spacer(),
const _Div(),
PrimaryButton(
label: "Continue",
onPressed: () {
Navigator.of(context).pushNamed(
FinishResharingView.routeName,
arguments: widget.walletId,
);
},
),
],
),
),
),
);
}
}
class _Div extends StatelessWidget {
const _Div({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
height: 12,
);
}
}

View file

@ -1,431 +0,0 @@
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_start_resharing_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/crypto_currency/intermediate/frost_currency.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/models/incomplete_frost_wallet.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.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/frost_mascot.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class NewImportResharerConfigView extends ConsumerStatefulWidget {
const NewImportResharerConfigView({
super.key,
required this.walletName,
required this.frostCurrency,
});
static const String routeName = "/newImportResharerConfigView";
final String walletName;
final FrostCurrency frostCurrency;
@override
ConsumerState<NewImportResharerConfigView> createState() =>
_NewImportResharerConfigViewState();
}
class _NewImportResharerConfigViewState
extends ConsumerState<NewImportResharerConfigView> {
late final TextEditingController myNameFieldController, configFieldController;
late final FocusNode myNameFocusNode, configFocusNode;
bool _nameEmpty = true, _configEmpty = true;
bool _buttonLock = false;
Future<IncompleteFrostWallet> _createWallet() async {
final info = WalletInfo.createNew(
name: widget.walletName,
coin: widget.frostCurrency.coin,
);
final wallet = IncompleteFrostWallet();
wallet.info = info;
return wallet;
}
@override
void initState() {
myNameFieldController = TextEditingController();
configFieldController = TextEditingController();
myNameFocusNode = FocusNode();
configFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
myNameFieldController.dispose();
configFieldController.dispose();
myNameFocusNode.dispose();
configFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: const DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(),
// TODO: [prio=high] get rid of placeholder text??
trailing: FrostMascot(
title: 'Lorem ipsum',
body:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ',
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
title: Text(
"Import FROST reshare config",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("frMyNameTextFieldKey"),
controller: myNameFieldController,
onChanged: (_) {
setState(() {
_nameEmpty = myNameFieldController.text.isEmpty;
});
},
focusNode: myNameFocusNode,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"My name",
myNameFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _nameEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
!_nameEmpty
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Config Field.",
key: const Key("frMyNameClearButtonKey"),
onTap: () {
myNameFieldController.text = "";
setState(() {
_nameEmpty = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Name Field.",
key: const Key("frMyNamePasteButtonKey"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
myNameFieldController.text =
data.text!.trim();
}
setState(() {
_nameEmpty =
myNameFieldController.text.isEmpty;
});
},
child: _nameEmpty
? const ClipboardIcon()
: const XIcon(),
),
],
),
),
),
),
),
),
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("frConfigTextFieldKey"),
controller: configFieldController,
onChanged: (_) {
setState(() {
_configEmpty = configFieldController.text.isEmpty;
});
},
focusNode: configFocusNode,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Enter config",
configFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _configEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
!_configEmpty
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Config Field.",
key: const Key("frConfigClearButtonKey"),
onTap: () {
configFieldController.text = "";
setState(() {
_configEmpty = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Config Field Input.",
key: const Key("frConfigPasteButtonKey"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
configFieldController.text =
data.text!.trim();
}
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
},
child: _configEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_configEmpty)
TextFieldIconButton(
semanticsLabel:
"Scan QR Button. Opens Camera For Scanning QR Code.",
key: const Key("frConfigScanQrButtonKey"),
onTap: () async {
try {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
final qrResult = await BarcodeScanner.scan();
configFieldController.text =
qrResult.rawContent;
setState(() {
_configEmpty =
configFieldController.text.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
)
],
),
),
),
),
),
),
const SizedBox(
height: 16,
),
if (!Util.isDesktop) const Spacer(),
const SizedBox(
height: 16,
),
PrimaryButton(
label: "Start",
enabled: !_nameEmpty && !_configEmpty,
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
}
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
ref.read(pFrostResharingData).reset();
ref.read(pFrostResharingData).myName =
myNameFieldController.text;
ref.read(pFrostResharingData).resharerConfig =
configFieldController.text;
if (!ref
.read(pFrostResharingData)
.configData!
.newParticipants
.contains(ref.read(pFrostResharingData).myName!)) {
ref.read(pFrostResharingData).reset();
return await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "My name not found in config participants",
desktopPopRootNavigator: Util.isDesktop,
),
);
}
Exception? ex;
final wallet = await showLoading(
whileFuture: _createWallet(),
context: context,
message: "Setting up wallet",
isDesktop: Util.isDesktop,
onException: (e) => ex = e,
);
if (ex != null) {
throw ex!;
}
if (context.mounted) {
ref.read(pFrostResharingData).incompleteWallet = wallet!;
await Navigator.of(context).pushNamed(
NewStartResharingView.routeName,
arguments: wallet.walletId,
);
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (context.mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
},
),
],
),
),
);
}
}

View file

@ -1,373 +0,0 @@
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_continue_sharing_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.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/dialogs/frost/frost_interruption_dialog.dart';
import 'package:stackwallet/widgets/frost_mascot.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class NewStartResharingView extends ConsumerStatefulWidget {
const NewStartResharingView({
super.key,
required this.walletId,
});
static const String routeName = "/newStartResharingView";
final String walletId;
@override
ConsumerState<NewStartResharingView> createState() =>
_NewStartResharingViewState();
}
class _NewStartResharingViewState extends ConsumerState<NewStartResharingView> {
final List<TextEditingController> controllers = [];
final List<FocusNode> focusNodes = [];
late final List<int> resharerIndexes;
final List<bool> fieldIsEmptyFlags = [];
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
// collect resharer strings
final resharerStarts = controllers.map((e) => e.text).toList();
final result = Frost.beginReshared(
myName: ref.read(pFrostResharingData).myName!,
resharerConfig: ref.read(pFrostResharingData).resharerConfig!,
resharerStarts: resharerStarts,
);
ref.read(pFrostResharingData).startResharedData = result;
await Navigator.of(context).pushNamed(
NewContinueSharingView.routeName,
arguments: widget.walletId,
);
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
} finally {
_buttonLock = false;
}
}
@override
void initState() {
resharerIndexes = ref.read(pFrostResharingData).configData!.resharers;
for (int i = 0; i < resharerIndexes.length; i++) {
controllers.add(TextEditingController());
focusNodes.add(FocusNode());
fieldIsEmptyFlags.add(true);
}
super.initState();
}
@override
void dispose() {
for (int i = 0; i < controllers.length; i++) {
controllers[i].dispose();
}
for (int i = 0; i < focusNodes.length; i++) {
focusNodes[i].dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName:
Util.isDesktop ? DesktopHomeView.routeName : HomeView.routeName,
),
);
return false;
},
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: DesktopHomeView.routeName,
),
);
},
),
trailing: FrostMascot(
title: 'Lorem ipsum',
body:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ',
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => const FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: HomeView.routeName,
),
);
},
),
title: Text(
"Resharers",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < resharerIndexes.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: Key("frostResharerTextFieldKey_$i"),
controller: controllers[i],
focusNode: focusNodes[i],
readOnly: false,
autocorrect: false,
enableSuggestions: false,
style: STextStyles.field(context),
onChanged: (_) {
setState(() {
fieldIsEmptyFlags[i] =
controllers[i].text.isEmpty;
});
},
decoration: standardInputDecoration(
"Enter index "
"${resharerIndexes[i]}"
"'s resharer",
focusNodes[i],
context,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: fieldIsEmptyFlags[i]
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
!fieldIsEmptyFlags[i]
? TextFieldIconButton(
semanticsLabel:
"Clear Button. Clears The Resharer Field Input.",
key: Key(
"frostResharerClearButtonKey_$i"),
onTap: () {
controllers[i].text = "";
setState(() {
fieldIsEmptyFlags[i] = true;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
semanticsLabel:
"Paste Button. Pastes From Clipboard To Resharer Field Input.",
key: Key(
"frostResharerPasteButtonKey_$i"),
onTap: () async {
final ClipboardData? data =
await Clipboard.getData(
Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
controllers[i].text =
data.text!.trim();
}
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
},
child: fieldIsEmptyFlags[i]
? const ClipboardIcon()
: const XIcon(),
),
if (fieldIsEmptyFlags[i])
TextFieldIconButton(
semanticsLabel: "Scan QR Button. "
"Opens Camera For Scanning QR Code.",
key: Key(
"frostCommitmentsScanQrButtonKey_$i"),
onTap: () async {
try {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context)
.unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75));
}
final qrResult =
await BarcodeScanner.scan();
controllers[i].text =
qrResult.rawContent;
setState(() {
fieldIsEmptyFlags[i] =
controllers[i]
.text
.isEmpty;
});
} on PlatformException catch (e, s) {
Logging.instance.log(
"Failed to get camera permissions "
"while trying to scan qr code: $e\n$s",
level: LogLevel.Warning,
);
}
},
child: const QrCodeIcon(),
),
],
),
),
),
),
),
),
),
],
),
],
),
if (!Util.isDesktop) const Spacer(),
const _Div(),
PrimaryButton(
label: "Continue",
enabled: !fieldIsEmptyFlags.reduce((v, e) => v |= e),
onPressed: _onPressed,
),
],
),
),
),
);
}
}
class _Div extends StatelessWidget {
const _Div({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
height: 12,
);
}
}

View file

@ -1,315 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/node_service_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.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';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.dart';
import 'package:stackwallet/widgets/background.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/simple_copy_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/detail_item.dart';
import 'package:stackwallet/widgets/dialogs/frost/frost_interruption_dialog.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class VerifyUpdatedWalletView extends ConsumerStatefulWidget {
const VerifyUpdatedWalletView({
super.key,
required this.walletId,
});
static const String routeName = "/verifyUpdatedWalletView";
final String walletId;
@override
ConsumerState<VerifyUpdatedWalletView> createState() =>
_VerifyUpdatedWalletViewState();
}
class _VerifyUpdatedWalletViewState
extends ConsumerState<VerifyUpdatedWalletView> {
late final String config;
late final String serializedKeys;
late final String reshareId;
late final bool isNew;
bool _buttonLock = false;
Future<void> _onPressed() async {
if (_buttonLock) {
return;
}
_buttonLock = true;
try {
Exception? ex;
final BitcoinFrostWallet wallet;
if (isNew) {
wallet = await ref
.read(pFrostResharingData)
.incompleteWallet!
.toBitcoinFrostWallet(
mainDB: ref.read(mainDBProvider),
secureStorageInterface: ref.read(secureStoreProvider),
nodeService: ref.read(nodeServiceChangeNotifierProvider),
prefs: ref.read(prefsChangeNotifierProvider),
);
await wallet.info.setMnemonicVerified(
isar: ref.read(mainDBProvider).isar,
);
ref.read(pWallets).addWallet(wallet);
} else {
wallet =
ref.read(pWallets).getWallet(widget.walletId) as BitcoinFrostWallet;
}
if (mounted) {
await showLoading(
whileFuture: wallet.updateWithResharedData(
serializedKeys: serializedKeys,
multisigConfig: config,
isNewWallet: isNew,
),
context: context,
message: isNew ? "Creating wallet" : "Updating wallet data",
isDesktop: Util.isDesktop,
onException: (e) => ex = e,
);
if (ex != null) {
throw ex!;
}
if (mounted) {
ref.read(pFrostResharingData).reset();
Navigator.of(context).popUntil(
ModalRoute.withName(
_popUntilPath,
),
);
}
}
} catch (e, s) {
Logging.instance.log(
"$e\n$s",
level: LogLevel.Fatal,
);
if (mounted) {
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Error",
message: e.toString(),
desktopPopRootNavigator: Util.isDesktop,
),
);
}
} finally {
_buttonLock = false;
}
}
String get _popUntilPath => isNew
? Util.isDesktop
? DesktopHomeView.routeName
: HomeView.routeName
: Util.isDesktop
? DesktopWalletView.routeName
: WalletView.routeName;
@override
void initState() {
config = ref.read(pFrostResharingData).newWalletData!.multisigConfig;
serializedKeys =
ref.read(pFrostResharingData).newWalletData!.serializedKeys;
reshareId = ref.read(pFrostResharingData).newWalletData!.resharedId;
isNew = ref.read(pFrostResharingData).incompleteWallet != null &&
ref.read(pFrostResharingData).incompleteWallet!.walletId ==
widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: _popUntilPath,
),
);
return false;
},
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: _popUntilPath,
),
);
},
),
trailing: ExitToMyStackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: _popUntilPath,
),
);
},
),
),
body: SizedBox(
width: 480,
child: child,
),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (_) => FrostInterruptionDialog(
type: FrostInterruptionDialogType.resharing,
popUntilOnYesRouteName: _popUntilPath,
),
);
},
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
);
},
),
),
),
),
child: Column(
children: [
Text(
"Ensure your reshare ID matches that of each other participant",
style: STextStyles.pageTitleH2(context),
),
const _Div(),
DetailItem(
title: "ID",
detail: reshareId,
button: Util.isDesktop
? IconCopyButton(
data: reshareId,
)
: SimpleCopyButton(
data: reshareId,
),
),
const _Div(),
const _Div(),
Text(
"Back up your keys and config",
style: STextStyles.pageTitleH2(context),
),
const _Div(),
DetailItem(
title: "Config",
detail: config,
button: Util.isDesktop
? IconCopyButton(
data: config,
)
: SimpleCopyButton(
data: config,
),
),
const _Div(),
DetailItem(
title: "Keys",
detail: serializedKeys,
button: Util.isDesktop
? IconCopyButton(
data: serializedKeys,
)
: SimpleCopyButton(
data: serializedKeys,
),
),
if (!Util.isDesktop) const Spacer(),
const _Div(),
PrimaryButton(
label: "Confirm",
onPressed: _onPressed,
),
],
),
),
),
);
}
}
class _Div extends StatelessWidget {
const _Div({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
height: 12,
);
}
}

View file

@ -123,17 +123,8 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_pr
import 'package:stackwallet/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/frost_participants_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/finish_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1a/complete_reshare_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1a/display_reshare_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1a/initiate_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_1b/import_reshare_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/begin_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/continue_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_continue_sharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_import_resharer_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_start_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/verify_updated_wallet_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/complete_reshare_config_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/initiate_resharing_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart';
@ -499,52 +490,6 @@ class RouteGenerator {
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case NewImportResharerConfigView.routeName:
if (args is ({
String walletName,
FrostCurrency frostCurrency,
})) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => NewImportResharerConfigView(
walletName: args.walletName,
frostCurrency: args.frostCurrency,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case NewStartResharingView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => NewStartResharingView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case NewContinueSharingView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => NewContinueSharingView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case FrostStepScaffold.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
@ -582,20 +527,6 @@ class RouteGenerator {
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case ImportReshareConfigView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => ImportReshareConfigView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case InitiateResharingView.routeName:
if (args is String) {
return getRoute(
@ -625,76 +556,6 @@ class RouteGenerator {
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DisplayReshareConfigView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => DisplayReshareConfigView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case BeginResharingView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => BeginResharingView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case ContinueResharingView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => ContinueResharingView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case FinishResharingView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => FinishResharingView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case VerifyUpdatedWalletView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => VerifyUpdatedWalletView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case FrostSendView.routeName:
if (args is ({
String walletId,