mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-03 01:09:22 +00:00
feat: add qr code scanning and display
and correct spacing in explainer text
This commit is contained in:
parent
00932dbc0b
commit
81008841cc
3 changed files with 195 additions and 5 deletions
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:barcode_scan2/platform_wrapper.dart';
|
||||
import 'package:bip48/bip48.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -19,6 +20,7 @@ import '../../../providers/global/secure_store_provider.dart';
|
|||
import '../../../providers/global/wallets_provider.dart';
|
||||
import '../../../services/transaction_notification_tracker.dart';
|
||||
import '../../../utilities/enums/derive_path_type_enum.dart';
|
||||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/util.dart';
|
||||
import '../../../wallets/crypto_currency/coins/bip48_bitcoin.dart';
|
||||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
@ -27,6 +29,7 @@ import '../../../wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
|||
import '../../../wallets/wallet/wallet.dart';
|
||||
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../widgets/desktop/qr_code_scanner_dialog.dart';
|
||||
import '../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../widgets/icon_widgets/copy_icon.dart';
|
||||
import '../../../widgets/icon_widgets/qrcode_icon.dart';
|
||||
|
@ -34,6 +37,7 @@ import '../../add_wallet_views/restore_wallet_view/sub_widgets/restore_failed_di
|
|||
import '../../add_wallet_views/restore_wallet_view/sub_widgets/restore_succeeded_dialog.dart';
|
||||
import '../../add_wallet_views/restore_wallet_view/sub_widgets/restoring_dialog.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import 'xpub_qr_popup.dart';
|
||||
|
||||
final multisigCoordinatorStateProvider =
|
||||
StateNotifierProvider<MultisigCoordinatorState, MultisigCoordinatorData>(
|
||||
|
@ -310,7 +314,12 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
|||
.buttonTextSecondary,
|
||||
),
|
||||
onPressed: () {
|
||||
// TODO: Implement QR code scanning
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => XpubQrPopup(
|
||||
xpub: _myXpub,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
@ -393,9 +402,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
|||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
onPressed: () {
|
||||
// TODO: Implement QR code scanning
|
||||
},
|
||||
onPressed: () => {scanQr(i - 1)},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SecondaryButton(
|
||||
|
@ -629,6 +636,55 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
|||
await WakelockPlus.disable();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> scanQr(int cosignerIndex) async {
|
||||
try {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75),
|
||||
);
|
||||
}
|
||||
|
||||
final qrResult = await BarcodeScanner.scan();
|
||||
|
||||
xpubControllers[cosignerIndex].text = qrResult.rawContent;
|
||||
|
||||
ref
|
||||
.read(multisigCoordinatorStateProvider.notifier)
|
||||
.addCosignerXpub(qrResult.rawContent);
|
||||
|
||||
setState(() {}); // Trigger rebuild to update button state.
|
||||
} else {
|
||||
// Platform.isLinux, Platform.isWindows, or Platform.isMacOS.
|
||||
final qrResult = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => const QrCodeScannerDialog(),
|
||||
);
|
||||
|
||||
if (qrResult == null) {
|
||||
Logging.instance.log(
|
||||
"QR scanning cancelled",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
} else {
|
||||
xpubControllers[cosignerIndex].text = qrResult;
|
||||
|
||||
ref
|
||||
.read(multisigCoordinatorStateProvider.notifier)
|
||||
.addCosignerXpub(qrResult);
|
||||
|
||||
setState(() {}); // Trigger rebuild to update button state.
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MultisigCoordinatorData {
|
||||
|
|
|
@ -157,7 +157,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
|||
"Multisignature accounts, also called shared accounts, require "
|
||||
"multiple signatures to authorize a transaction. This can "
|
||||
"increase security by preventing a single point of failure or "
|
||||
"allow multiple parties to jointly control funds."
|
||||
"allow multiple parties to jointly control funds. "
|
||||
"For example, in a 2-of-3 multisig account, two of the three "
|
||||
"cosigners are required in order to sign a transaction and spend "
|
||||
"funds.",
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class XpubQrPopup extends StatelessWidget {
|
||||
const XpubQrPopup({
|
||||
super.key,
|
||||
required this.xpub,
|
||||
});
|
||||
|
||||
final String xpub;
|
||||
|
||||
Future<void> _copy(BuildContext context) async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: xpub),
|
||||
);
|
||||
if (context.mounted) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return DesktopDialog(
|
||||
maxWidth: isDesktop ? 600 : MediaQuery.of(context).size.width - 32,
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 32),
|
||||
child: Text(
|
||||
"Your xPub",
|
||||
style: STextStyles.desktopH2(context),
|
||||
),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride:
|
||||
Navigator.of(context, rootNavigator: true).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(32, 0, 32, 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: isDesktop ? 12 : 16,
|
||||
),
|
||||
DetailItem(
|
||||
title: "Derivation path",
|
||||
detail: "m/48'/0'/0'/2'", // TODO: Get actual derivation path
|
||||
horizontal: true,
|
||||
borderColor: isDesktop
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG
|
||||
: null,
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 12 : 16,
|
||||
),
|
||||
QR(
|
||||
data: xpub,
|
||||
size:
|
||||
isDesktop ? 256 : MediaQuery.of(context).size.width / 1.5,
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 12 : 16,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
borderColor: isDesktop
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG
|
||||
: null,
|
||||
child: SelectableText(
|
||||
xpub,
|
||||
style: STextStyles.w500_14(context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 12 : 16,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (isDesktop) const Spacer(),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Copy",
|
||||
onPressed: () => _copy(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue