mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-02-02 03:06:29 +00:00
wrap one way frost flow in sub navigation
This commit is contained in:
parent
999df80e60
commit
cb5f4e61de
31 changed files with 2719 additions and 3385 deletions
|
@ -38,7 +38,7 @@ class _FrostScaffoldState extends ConsumerState<FrostStepScaffold> {
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
ref.read(pFrostCreateNewArgs.state).state = null;
|
ref.read(pFrostScaffoldArgs.state).state = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestPopLock = false;
|
_requestPopLock = false;
|
||||||
|
@ -46,7 +46,7 @@ class _FrostScaffoldState extends ConsumerState<FrostStepScaffold> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_routes = ref.read(pFrostCreateNewArgs)!.$2;
|
_routes = ref.read(pFrostScaffoldArgs)!.stepRoutes;
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -408,13 +408,14 @@ class _NewFrostMsWalletViewState
|
||||||
controllers.first.text.trim();
|
controllers.first.text.trim();
|
||||||
ref.read(pFrostMultisigConfig.notifier).state = config;
|
ref.read(pFrostMultisigConfig.notifier).state = config;
|
||||||
|
|
||||||
ref.read(pFrostCreateNewArgs.state).state = (
|
ref.read(pFrostScaffoldArgs.state).state = (
|
||||||
(
|
info: (
|
||||||
walletName: widget.walletName,
|
walletName: widget.walletName,
|
||||||
frostCurrency: widget.frostCurrency,
|
frostCurrency: widget.frostCurrency,
|
||||||
),
|
),
|
||||||
FrostRouteGenerator.createNewConfigStepRoutes,
|
walletId: null,
|
||||||
() {
|
stepRoutes: FrostRouteGenerator.createNewConfigStepRoutes,
|
||||||
|
onSuccess: () {
|
||||||
// successful completion of steps
|
// successful completion of steps
|
||||||
if (Util.isDesktop) {
|
if (Util.isDesktop) {
|
||||||
Navigator.of(context).popUntil(
|
Navigator.of(context).popUntil(
|
||||||
|
@ -434,7 +435,7 @@ class _NewFrostMsWalletViewState
|
||||||
ref.read(pFrostMultisigConfig.state).state = null;
|
ref.read(pFrostMultisigConfig.state).state = null;
|
||||||
ref.read(pFrostStartKeyGenData.state).state = null;
|
ref.read(pFrostStartKeyGenData.state).state = null;
|
||||||
ref.read(pFrostSecretSharesData.state).state = null;
|
ref.read(pFrostSecretSharesData.state).state = null;
|
||||||
ref.read(pFrostCreateNewArgs.state).state = null;
|
ref.read(pFrostScaffoldArgs.state).state = null;
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
showFloatingFlushBar(
|
showFloatingFlushBar(
|
||||||
|
|
|
@ -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/frost_scaffold.dart';
|
||||||
import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.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/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/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/exit_to_my_stack_button.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
|
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
|
||||||
|
@ -141,13 +140,14 @@ class _SelectNewFrostImportTypeViewState
|
||||||
final String route;
|
final String route;
|
||||||
switch (_selectedOption) {
|
switch (_selectedOption) {
|
||||||
case _ImportOption.multisigNew:
|
case _ImportOption.multisigNew:
|
||||||
ref.read(pFrostCreateNewArgs.state).state = (
|
ref.read(pFrostScaffoldArgs.state).state = (
|
||||||
(
|
info: (
|
||||||
walletName: widget.walletName,
|
walletName: widget.walletName,
|
||||||
frostCurrency: widget.frostCurrency,
|
frostCurrency: widget.frostCurrency,
|
||||||
),
|
),
|
||||||
FrostRouteGenerator.importNewConfigStepRoutes,
|
walletId: null, // no wallet id yet
|
||||||
() {
|
stepRoutes: FrostRouteGenerator.importNewConfigStepRoutes,
|
||||||
|
onSuccess: () {
|
||||||
// successful completion of steps
|
// successful completion of steps
|
||||||
if (Util.isDesktop) {
|
if (Util.isDesktop) {
|
||||||
Navigator.of(context).popUntil(
|
Navigator.of(context).popUntil(
|
||||||
|
@ -167,7 +167,7 @@ class _SelectNewFrostImportTypeViewState
|
||||||
ref.read(pFrostMultisigConfig.state).state = null;
|
ref.read(pFrostMultisigConfig.state).state = null;
|
||||||
ref.read(pFrostStartKeyGenData.state).state = null;
|
ref.read(pFrostStartKeyGenData.state).state = null;
|
||||||
ref.read(pFrostSecretSharesData.state).state = null;
|
ref.read(pFrostSecretSharesData.state).state = null;
|
||||||
ref.read(pFrostCreateNewArgs.state).state = null;
|
ref.read(pFrostScaffoldArgs.state).state = null;
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
showFloatingFlushBar(
|
showFloatingFlushBar(
|
||||||
|
@ -179,22 +179,26 @@ class _SelectNewFrostImportTypeViewState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await Navigator.of(context).pushNamed(
|
|
||||||
FrostStepScaffold.routeName,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _ImportOption.resharerExisting:
|
case _ImportOption.resharerExisting:
|
||||||
await Navigator.of(context).pushNamed(
|
ref.read(pFrostScaffoldArgs.state).state = (
|
||||||
NewImportResharerConfigView.routeName,
|
info: (
|
||||||
arguments: (
|
|
||||||
walletName: widget.walletName,
|
walletName: widget.walletName,
|
||||||
frostCurrency: widget.frostCurrency,
|
frostCurrency: widget.frostCurrency,
|
||||||
),
|
),
|
||||||
|
walletId: null, // no wallet id yet
|
||||||
|
stepRoutes: FrostRouteGenerator.joinReshareStepRoutes,
|
||||||
|
onSuccess: () {
|
||||||
|
// successful completion of steps
|
||||||
|
}
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Navigator.of(context).pushNamed(
|
||||||
|
FrostStepScaffold.routeName,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.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/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/transaction_views/transaction_details_view.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.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;
|
ref.read(pFrostCreateCurrentStep.state).state = 2;
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
FrostCreateStep2.routeName,
|
ref
|
||||||
|
.read(pFrostScaffoldArgs)!
|
||||||
|
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
|
||||||
|
.routeName,
|
||||||
// FrostShareCommitmentsView.routeName,
|
// FrostShareCommitmentsView.routeName,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:barcode_scan2/barcode_scan2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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/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/providers/frost_wallet/frost_wallet_providers.dart';
|
||||||
import 'package:stackwallet/services/frost.dart';
|
import 'package:stackwallet/services/frost.dart';
|
||||||
|
@ -356,8 +355,11 @@ class _FrostCreateStep1bState extends ConsumerState<FrostCreateStep1b> {
|
||||||
myName: ref.read(pFrostMyName.state).state!,
|
myName: ref.read(pFrostMyName.state).state!,
|
||||||
);
|
);
|
||||||
ref.read(pFrostCreateCurrentStep.state).state = 2;
|
ref.read(pFrostCreateCurrentStep.state).state = 2;
|
||||||
Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
FrostCreateStep2.routeName,
|
ref
|
||||||
|
.read(pFrostScaffoldArgs)!
|
||||||
|
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
|
||||||
|
.routeName,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.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/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/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.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;
|
ref.read(pFrostCreateCurrentStep.state).state = 3;
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
FrostCreateStep3.routeName,
|
ref
|
||||||
|
.read(pFrostScaffoldArgs)!
|
||||||
|
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
|
||||||
|
.routeName,
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.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/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/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.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;
|
ref.read(pFrostCreateCurrentStep.state).state = 4;
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
FrostCreateStep4.routeName,
|
ref
|
||||||
|
.read(pFrostScaffoldArgs)!
|
||||||
|
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
|
||||||
|
.routeName,
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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/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/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
|
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
|
||||||
|
@ -62,9 +61,12 @@ class _FrostCreateStep4State extends ConsumerState<FrostCreateStep4> {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
label: "Confirm",
|
label: "Confirm",
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(pFrostCreateCurrentStep.state).state = 4;
|
ref.read(pFrostCreateCurrentStep.state).state = 5;
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
FrostCreateStep5.routeName,
|
ref
|
||||||
|
.read(pFrostScaffoldArgs)!
|
||||||
|
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
|
||||||
|
.routeName,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -162,11 +162,11 @@ class _FrostCreateStep5State extends ConsumerState<FrostCreateStep5> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final data = ref.read(pFrostCreateNewArgs)!;
|
final data = ref.read(pFrostScaffoldArgs)!;
|
||||||
|
|
||||||
final info = WalletInfo.createNew(
|
final info = WalletInfo.createNew(
|
||||||
coin: data.$1.frostCurrency.coin,
|
coin: data.info.frostCurrency.coin,
|
||||||
name: data.$1.walletName,
|
name: data.info.walletName,
|
||||||
);
|
);
|
||||||
|
|
||||||
final wallet = await Wallet.create(
|
final wallet = await Wallet.create(
|
||||||
|
@ -206,7 +206,7 @@ class _FrostCreateStep5State extends ConsumerState<FrostCreateStep5> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ref.read(pFrostCreateNewArgs)!.$3();
|
ref.read(pFrostScaffoldArgs)!.onSuccess();
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
|
|
@ -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_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_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/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/route_generator.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/frost_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/intermediate/frost_currency.dart';
|
||||||
|
|
||||||
typedef FrostStepRoute = ({String routeName, String title});
|
typedef FrostStepRoute = ({String routeName, String title});
|
||||||
|
|
||||||
final pFrostCreateCurrentStep = StateProvider.autoDispose((ref) => 1);
|
final pFrostCreateCurrentStep = StateProvider.autoDispose((ref) => 1);
|
||||||
final pFrostCreateNewArgs = StateProvider<
|
final pFrostScaffoldArgs = StateProvider<
|
||||||
(
|
({
|
||||||
({String walletName, FrostCurrency frostCurrency}),
|
({String walletName, FrostCurrency frostCurrency}) info,
|
||||||
List<FrostStepRoute>,
|
String? walletId,
|
||||||
VoidCallback,
|
List<FrostStepRoute> stepRoutes,
|
||||||
)?>((ref) => null);
|
VoidCallback onSuccess,
|
||||||
|
})?>((ref) => null);
|
||||||
|
|
||||||
abstract class FrostRouteGenerator {
|
abstract class FrostRouteGenerator {
|
||||||
static const bool useMaterialPageRoute = true;
|
static const bool useMaterialPageRoute = true;
|
||||||
|
@ -39,6 +49,42 @@ abstract class FrostRouteGenerator {
|
||||||
(routeName: FrostCreateStep5.routeName, title: FrostCreateStep5.title),
|
(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) {
|
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||||
final args = settings.arguments;
|
final args = settings.arguments;
|
||||||
|
|
||||||
|
@ -85,6 +131,69 @@ abstract class FrostRouteGenerator {
|
||||||
settings: settings,
|
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:
|
default:
|
||||||
return _routeError("");
|
return _routeError("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.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/pages/wallet_view/transaction_views/transaction_details_view.dart';
|
||||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.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/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
|
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.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/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/primary_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
import 'package:stackwallet/widgets/detail_item.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/frost_step_user_steps.dart';
|
||||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
|
|
||||||
class DisplayReshareConfigView extends ConsumerStatefulWidget {
|
class FrostReshareStep1a extends ConsumerStatefulWidget {
|
||||||
const DisplayReshareConfigView({
|
const FrostReshareStep1a({super.key});
|
||||||
super.key,
|
|
||||||
required this.walletId,
|
|
||||||
});
|
|
||||||
|
|
||||||
static const String routeName = "/displayReshareConfigView";
|
static const String routeName = "/frostReshareStep1a";
|
||||||
|
static const String title = "Resharer config";
|
||||||
final String walletId;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<DisplayReshareConfigView> createState() =>
|
ConsumerState<FrostReshareStep1a> createState() => _FrostReshareStep1aState();
|
||||||
_DisplayReshareConfigViewState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DisplayReshareConfigViewState
|
class _FrostReshareStep1aState extends ConsumerState<FrostReshareStep1a> {
|
||||||
extends ConsumerState<DisplayReshareConfigView> {
|
|
||||||
static const info = [
|
static const info = [
|
||||||
"Share this config with the signing group participants as well as any new "
|
"Share this config with the signing group participants as well as any new "
|
||||||
"participant.",
|
"participant.",
|
||||||
|
@ -67,7 +56,8 @@ class _DisplayReshareConfigViewState
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final wallet =
|
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();
|
final serializedKeys = await wallet.getSerializedKeys();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
@ -78,9 +68,12 @@ class _DisplayReshareConfigViewState
|
||||||
|
|
||||||
ref.read(pFrostResharingData).startResharerData = result;
|
ref.read(pFrostResharingData).startResharerData = result;
|
||||||
|
|
||||||
|
ref.read(pFrostCreateCurrentStep.state).state = 2;
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
BeginResharingView.routeName,
|
ref
|
||||||
arguments: widget.walletId,
|
.read(pFrostScaffoldArgs)!
|
||||||
|
.stepRoutes[ref.read(pFrostCreateCurrentStep) - 1]
|
||||||
|
.routeName,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
@ -215,7 +208,7 @@ class _DisplayReshareConfigViewState
|
||||||
.read(mainDBProvider)
|
.read(mainDBProvider)
|
||||||
.isar
|
.isar
|
||||||
.frostWalletInfo
|
.frostWalletInfo
|
||||||
.getByWalletIdSync(widget.walletId)!;
|
.getByWalletIdSync(ref.read(pFrostScaffoldArgs)!.walletId!)!;
|
||||||
|
|
||||||
final myOldIndex = frostInfo.participants.indexOf(frostInfo.myName);
|
final myOldIndex = frostInfo.participants.indexOf(frostInfo.myName);
|
||||||
|
|
||||||
|
@ -229,164 +222,110 @@ class _DisplayReshareConfigViewState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ConditionalParent(
|
return Padding(
|
||||||
condition: Util.isDesktop,
|
padding: const EdgeInsets.all(16),
|
||||||
builder: (child) => DesktopScaffold(
|
child: Column(
|
||||||
background: Theme.of(context).extension<StackColors>()!.background,
|
children: [
|
||||||
appBar: const DesktopAppBar(
|
const FrostStepUserSteps(
|
||||||
isCompactHeight: false,
|
userSteps: info,
|
||||||
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(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
child: Column(
|
SizedBox(
|
||||||
children: [
|
height: 220,
|
||||||
const SizedBox(
|
child: Row(
|
||||||
height: 16,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
|
||||||
const FrostStepUserSteps(
|
|
||||||
userSteps: info,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
SizedBox(
|
|
||||||
height: 220,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
QrImageView(
|
|
||||||
data: ref.watch(pFrostResharingData).resharerConfig!,
|
|
||||||
size: 220,
|
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.background,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 32,
|
|
||||||
),
|
|
||||||
DetailItem(
|
|
||||||
title: "Config",
|
|
||||||
detail: ref.watch(pFrostResharingData).resharerConfig!,
|
|
||||||
button: Util.isDesktop
|
|
||||||
? IconCopyButton(
|
|
||||||
data: ref.watch(pFrostResharingData).resharerConfig!,
|
|
||||||
)
|
|
||||||
: SimpleCopyButton(
|
|
||||||
data: ref.watch(pFrostResharingData).resharerConfig!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: Util.isDesktop ? 64 : 16,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
QrImageView(
|
||||||
child: SecondaryButton(
|
data: ref.watch(pFrostResharingData).resharerConfig!,
|
||||||
label: "Show group participants",
|
size: 220,
|
||||||
onPressed: _showParticipantsDialog,
|
backgroundColor:
|
||||||
),
|
Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
foregroundColor: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.accentColorDark,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (iAmInvolved && !Util.isDesktop) const Spacer(),
|
),
|
||||||
if (iAmInvolved)
|
const SizedBox(
|
||||||
const SizedBox(
|
height: 32,
|
||||||
height: 16,
|
),
|
||||||
),
|
DetailItem(
|
||||||
if (iAmInvolved)
|
title: "Config",
|
||||||
GestureDetector(
|
detail: ref.watch(pFrostResharingData).resharerConfig!,
|
||||||
onTap: () {
|
button: Util.isDesktop
|
||||||
setState(() {
|
? IconCopyButton(
|
||||||
_userVerifyContinue = !_userVerifyContinue;
|
data: ref.watch(pFrostResharingData).resharerConfig!,
|
||||||
});
|
)
|
||||||
},
|
: SimpleCopyButton(
|
||||||
child: Container(
|
data: ref.watch(pFrostResharingData).resharerConfig!,
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Util.isDesktop ? 64 : 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SecondaryButton(
|
||||||
|
label: "Show group participants",
|
||||||
|
onPressed: _showParticipantsDialog,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (iAmInvolved)
|
],
|
||||||
const SizedBox(
|
),
|
||||||
height: 16,
|
if (iAmInvolved && !Util.isDesktop) const Spacer(),
|
||||||
|
if (iAmInvolved)
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
if (iAmInvolved)
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (iAmInvolved)
|
),
|
||||||
PrimaryButton(
|
if (iAmInvolved)
|
||||||
label: "Start resharing",
|
const SizedBox(
|
||||||
enabled: _userVerifyContinue,
|
height: 16,
|
||||||
onPressed: _onPressed,
|
),
|
||||||
),
|
if (iAmInvolved)
|
||||||
],
|
PrimaryButton(
|
||||||
),
|
label: "Start resharing",
|
||||||
|
enabled: _userVerifyContinue,
|
||||||
|
onPressed: _onPressed,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,18 +10,21 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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/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/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/initiate_resharing/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_desktop_specific/my_stack_view/exit_to_my_stack_button.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/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.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/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.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/background.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
@ -146,9 +149,26 @@ class FrostMSWalletOptionsView extends ConsumerWidget {
|
||||||
|
|
||||||
ref.read(pFrostMyName.state).state = frostInfo.myName;
|
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(
|
Navigator.of(context).pushNamed(
|
||||||
ImportReshareConfigView.routeName,
|
FrostStepScaffold.routeName,
|
||||||
arguments: walletId,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,10 +4,12 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:frostdart/frostdart.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/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/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.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/services/frost.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/format.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/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.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/background.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
@ -120,10 +123,25 @@ class _CompleteReshareConfigViewState
|
||||||
ref.read(pFrostResharingData).myName = myName;
|
ref.read(pFrostResharingData).myName = myName;
|
||||||
ref.read(pFrostResharingData).resharerConfig = config;
|
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) {
|
if (mounted) {
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
DisplayReshareConfigView.routeName,
|
FrostStepScaffold.routeName,
|
||||||
arguments: widget.walletId,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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/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/db/main_db_provider.dart';
|
||||||
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
|
import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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/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_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/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/initiate_resharing/complete_reshare_config_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/initiate_resharing/initiate_resharing_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/wallet_backup_views/wallet_backup_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_network_settings_view/wallet_network_settings_view.dart';
|
||||||
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_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()}");
|
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:
|
case FrostStepScaffold.routeName:
|
||||||
return getRoute(
|
return getRoute(
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
@ -582,20 +527,6 @@ class RouteGenerator {
|
||||||
}
|
}
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
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:
|
case InitiateResharingView.routeName:
|
||||||
if (args is String) {
|
if (args is String) {
|
||||||
return getRoute(
|
return getRoute(
|
||||||
|
@ -625,76 +556,6 @@ class RouteGenerator {
|
||||||
}
|
}
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
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:
|
case FrostSendView.routeName:
|
||||||
if (args is ({
|
if (args is ({
|
||||||
String walletId,
|
String walletId,
|
||||||
|
|
Loading…
Reference in a new issue