mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-19 05:58:50 +02:00
CW-1135-add-support-for-bitbox-hww (#2476)
* chore: add ffi and ffigen dependencies to pubspec.yaml * refactor: replace method channel with FFI implementation for MWeb module * fix: correct `cd` command in build_mwebd.sh script * chore: add go.sum file with dependency checksums * fix: ensure ANDROID_NDK_VERSION is set before building and dynamically update NDK_BIN path * fix: update NDK_BIN path to dynamically detect platform in build_mwebd.sh script * refactor: remove unused mwebd dependencies and clean up CwMwebPlugin code * Update scripts/android/build_mwebd.sh Co-authored-by: cyan <cyjan@mrcyjanek.net> * refactor: replace iOS MoneroWallet framework with Mwebd.xcframework and clean up plugin implementation [skip-ci] * refactor: extract hardware wallet service abstraction * refactor: extend BitcoinHardwareWalletService from HardwareWalletService and override account retrieval method * feat: add BitBox hardware wallet support and implement manufacturer selection page with custom UI components * refactor: update manufacturer selection UI and integrate custom gradient styles for ManufacturerOptionTile widget * fix: mwebd ffi ios * feat: add BitBox hardware wallet support, update derivation paths, and refactor hardware wallet credential implementations * feat: add bitbox support and refactor the old ledger implementation * feat: add BitBox hardware wallet support and refactor hardware wallet service implementations * feat: refactor hardware wallet support to introduce abstraction for BitBox and Ledger; update device scanning, initialization, and UI integration * fix: BitBox hardware wallet ethereum transaction signing * chore: update BitBox Flutter path references and include build steps in CI workflows * chore: update CI workflows to include `--dont-install` flag for BitBox Flutter builds * refactor: replace if-else logic with switch-case for HardwareWalletViewModel factory * feat: refactor hardware wallet services to introduce shared interface, improve ledger and bitbox implementations, and support additional transaction signing operations * feat: add SVG assets for hardware wallet manufacturers (Foundation, Keystone, SeedSigner) * feat: redesign hardware wallet restoration flow with improved routing, UI updates, and manufacturer-specific device selection handling * fix: validate required BIP353 fields in transaction builder to prevent potential null value errors * feat: improve BitBox transaction signing logs with detailed PSBT information * refactor: update MWebFfi class to use non-nullable `lib` and improve address debug logging * feat: add BitBox coin type constant and improve hardware wallet service extensibility * chore: remove unused iOS/Android build scripts for mwebd * feat: add BitBox SVG asset and update localization for hardware wallet connection instructions * feat: update manufacturer tags and add Toast Easter egg for device selection interactions * refactor: replace LedgerViewModel with HardwareWalletViewModel across providers and improve hardware wallet handling * refactor: replace mweb.so with libmweb.so, use sync methods in MWebFfi, and update iOS/Android frameworks/scripts accordingly * feat: add Ledger Nano X SVG asset to hardware wallet resources * chore: unify mwebd build process in workflows, remove unused gomobile init command * refactor: remove redundant print statement and unnecessary whitespace * chore: add `gomobile init` to Android PR test build workflow * chore: update Android PR test build workflow and add file existence check in `build_bitbox_flutter.sh` * chore: override `ledger_usb_plus` dependency in pubspec_base.yaml * feat: add verbose logging for BitBox connection and initialization process * Update lib/view_model/hardware_wallet/bitbox_view_model.dart [skip ci] * CW-1206-refactor-hardware-wallets-code (#2507) * feat: add support for new hardware wallet types (Cupcake, Coldcard, SeedSigner) and improve QR Code handling - Introduced new hardware wallet types (Cupcake, Coldcard, SeedSigner) with corresponding assets and localization updates. - Updated QR Code format selection for hardware wallets with new formats (BCUR, BBQR) specific to devices. - Improved hardware wallet restoration flow to include airgapped wallet handling via QR Code scanning. - Refactored routing, view models, and dependency injection to accommodate new hardware wallet types. - Enhanced UI for device manufacturer selection and QR Code format dialogs. * feat: update hardware wallet assets and restoration flow * feat: add changing airgapped wallet type * feat: add Cupcake Man SVG asset to hardware wallet resources * refactor: update Litecoin receive options to require wallet parameter and handle hardware wallets * fix: debugging keystone integration feat: support for zpub view only restore * fix: seed signer restore * fix: disable bitbox restoring (for now) [skip-ci] * refactor: rename `isCupcake` to `isBitcoinViewOnly` for clarity [skip ci] --------- Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * refactor: standardize hardware wallet connection handling across wallet types --------- Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Czarek Nakamoto
Omar Hatem
parent
e1bb0e4a69
commit
e4672c75b7
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
@@ -9,23 +10,25 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_wid
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||
|
||||
typedef OnConnectDevice = void Function(BuildContext, LedgerViewModel);
|
||||
typedef OnConnectDevice = void Function(BuildContext, HardwareWalletViewModel);
|
||||
|
||||
class ConnectDevicePageParams {
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final bool isReconnect;
|
||||
final HardwareWalletType hardwareWalletType;
|
||||
|
||||
ConnectDevicePageParams({
|
||||
required this.walletType,
|
||||
required this.hardwareWalletType,
|
||||
required this.onConnectDevice,
|
||||
this.allowChangeWallet = false,
|
||||
this.isReconnect = true,
|
||||
@@ -37,9 +40,9 @@ class ConnectDevicePage extends BasePage {
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final bool isReconnect;
|
||||
final LedgerViewModel ledgerVM;
|
||||
final HardwareWalletViewModel hardwareWalletVM;
|
||||
|
||||
ConnectDevicePage(ConnectDevicePageParams params, this.ledgerVM)
|
||||
ConnectDevicePage(ConnectDevicePageParams params, this.hardwareWalletVM)
|
||||
: walletType = params.walletType,
|
||||
onConnectDevice = params.onConnectDevice,
|
||||
allowChangeWallet = params.allowChangeWallet,
|
||||
@@ -51,8 +54,7 @@ class ConnectDevicePage extends BasePage {
|
||||
: S.current.restore_title_from_hardware_wallet;
|
||||
|
||||
@override
|
||||
Widget? leading(BuildContext context) =>
|
||||
!isReconnect ? super.leading(context) : null;
|
||||
Widget? leading(BuildContext context) => !isReconnect ? super.leading(context) : null;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => PopScope(
|
||||
@@ -61,7 +63,7 @@ class ConnectDevicePage extends BasePage {
|
||||
walletType,
|
||||
onConnectDevice,
|
||||
allowChangeWallet,
|
||||
ledgerVM,
|
||||
hardwareWalletVM,
|
||||
currentTheme,
|
||||
));
|
||||
}
|
||||
@@ -70,14 +72,14 @@ class ConnectDevicePageBody extends StatefulWidget {
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final LedgerViewModel ledgerVM;
|
||||
final HardwareWalletViewModel hardwareWalletVM;
|
||||
final MaterialThemeBase currentTheme;
|
||||
|
||||
const ConnectDevicePageBody(
|
||||
this.walletType,
|
||||
this.onConnectDevice,
|
||||
this.allowChangeWallet,
|
||||
this.ledgerVM,
|
||||
this.hardwareWalletVM,
|
||||
this.currentTheme,
|
||||
);
|
||||
|
||||
@@ -86,13 +88,13 @@ class ConnectDevicePageBody extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
var bleDevices = <LedgerDevice>[];
|
||||
var usbDevices = <LedgerDevice>[];
|
||||
var bleDevices = <HardwareWalletDevice>[];
|
||||
var usbDevices = <HardwareWalletDevice>[];
|
||||
|
||||
late Timer? _usbRefreshTimer = null;
|
||||
late Timer? _bleRefreshTimer = null;
|
||||
late Timer? _bleStateTimer = null;
|
||||
late StreamSubscription<LedgerDevice>? _bleRefresh = null;
|
||||
late StreamSubscription<HardwareWalletDevice>? _bleRefresh = null;
|
||||
|
||||
bool longWait = false;
|
||||
Timer? _longWaitTimer;
|
||||
@@ -101,21 +103,21 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_bleStateTimer = Timer.periodic(
|
||||
Duration(seconds: 1), (_) => widget.ledgerVM.updateBleState());
|
||||
_bleStateTimer =
|
||||
Timer.periodic(Duration(seconds: 1), (_) => widget.hardwareWalletVM.updateBleState());
|
||||
|
||||
_bleRefreshTimer =
|
||||
Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices());
|
||||
_bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices());
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
_usbRefreshTimer =
|
||||
Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices());
|
||||
_usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices());
|
||||
}
|
||||
|
||||
_longWaitTimer = Timer(Duration(seconds: 10), () {
|
||||
if (widget.ledgerVM.bleIsEnabled && bleDevices.isEmpty)
|
||||
setState(() => longWait = true);
|
||||
});
|
||||
if (widget.hardwareWalletVM.hasBluetooth) {
|
||||
_longWaitTimer = Timer(Duration(seconds: 10), () {
|
||||
if (widget.hardwareWalletVM.isBleEnabled && bleDevices.isEmpty)
|
||||
setState(() => longWait = true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,35 +129,25 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
_bleRefresh?.cancel();
|
||||
_longWaitTimer?.cancel();
|
||||
|
||||
widget.ledgerVM.stopScanning();
|
||||
widget.hardwareWalletVM.stopScanning();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _refreshUsbDevices() async {
|
||||
final dev = await widget.ledgerVM.ledgerPlusUSB.devices;
|
||||
final dev = await widget.hardwareWalletVM.getAllUsbDevices();
|
||||
if (usbDevices.length != dev.length) setState(() => usbDevices = dev);
|
||||
// _usbRefresh = widget.ledgerVM
|
||||
// .scanForUsbDevices()
|
||||
// .listen((device) => setState(() => usbDevices.add(device)))
|
||||
// ..onError((e) {
|
||||
// throw e.toString();
|
||||
// });
|
||||
// Keep polling until the lfp lib gets updated
|
||||
// _usbRefreshTimer?.cancel();
|
||||
// _usbRefreshTimer = null;
|
||||
}
|
||||
|
||||
Future<void> _refreshBleDevices() async {
|
||||
try {
|
||||
if (widget.ledgerVM.bleIsEnabled) {
|
||||
_bleRefresh =
|
||||
widget.ledgerVM.scanForBleDevices().listen((device) => setState(() {
|
||||
bleDevices.add(device);
|
||||
if (longWait) longWait = false;
|
||||
}))
|
||||
..onError((e) {
|
||||
throw e.toString();
|
||||
});
|
||||
if (widget.hardwareWalletVM.isBleEnabled) {
|
||||
_bleRefresh = widget.hardwareWalletVM.scanForBleDevices().listen((device) => setState(() {
|
||||
bleDevices.add(device);
|
||||
if (longWait) longWait = false;
|
||||
}))
|
||||
..onError((e) {
|
||||
throw e.toString();
|
||||
});
|
||||
_bleRefreshTimer?.cancel();
|
||||
_bleRefreshTimer = null;
|
||||
}
|
||||
@@ -164,25 +156,35 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connectToDevice(LedgerDevice device) async {
|
||||
final isConnected =
|
||||
await widget.ledgerVM.connectLedger(device, widget.walletType);
|
||||
if (isConnected) widget.onConnectDevice(context, widget.ledgerVM);
|
||||
Future<void> _connectToDevice(HardwareWalletDevice device) async {
|
||||
final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
|
||||
if (isConnected) widget.onConnectDevice(context, widget.hardwareWalletVM);
|
||||
}
|
||||
|
||||
String _getDeviceTileLeading(LedgerDeviceType deviceInfo) {
|
||||
switch (deviceInfo) {
|
||||
case LedgerDeviceType.nanoX:
|
||||
String _getDeviceTileLeading(HardwareWalletDeviceType deviceType) {
|
||||
switch (deviceType) {
|
||||
case HardwareWalletDeviceType.ledgerNanoX:
|
||||
return 'assets/images/hardware_wallet/ledger_nano_x.png';
|
||||
case LedgerDeviceType.stax:
|
||||
case HardwareWalletDeviceType.ledgerStax:
|
||||
return 'assets/images/hardware_wallet/ledger_stax.png';
|
||||
case LedgerDeviceType.flex:
|
||||
case HardwareWalletDeviceType.ledgerFlex:
|
||||
return 'assets/images/hardware_wallet/ledger_flex.png';
|
||||
case HardwareWalletDeviceType.BitBox02:
|
||||
return 'assets/images/hardware_wallet/device_bitbox.svg';
|
||||
|
||||
default:
|
||||
return 'assets/images/hardware_wallet/ledger_nano_x.png';
|
||||
}
|
||||
}
|
||||
|
||||
String get description {
|
||||
if (!Platform.isAndroid) return S.of(context).connect_your_hardware_wallet_ble;
|
||||
|
||||
if (widget.hardwareWalletVM.hasBluetooth) return S.of(context).connect_your_hardware_wallet;
|
||||
|
||||
return S.of(context).connect_your_hardware_wallet_usb;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@@ -197,11 +199,8 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Text(
|
||||
Platform.isAndroid
|
||||
? S.of(context).connect_your_hardware_wallet
|
||||
: S.of(context).connect_your_hardware_wallet_ios,
|
||||
style: Theme.of(context)
|
||||
.textTheme.titleMedium,
|
||||
description,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
@@ -211,22 +210,20 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Text(
|
||||
S.of(context).if_you_dont_see_your_device,
|
||||
style: Theme.of(context)
|
||||
.textTheme.titleMedium,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => Offstage(
|
||||
offstage: widget.ledgerVM.bleIsEnabled,
|
||||
offstage: widget.hardwareWalletVM.isBleEnabled ||
|
||||
!widget.hardwareWalletVM.hasBluetooth,
|
||||
child: Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Text(
|
||||
S.of(context).ledger_please_enable_bluetooth,
|
||||
style: Theme.of(context)
|
||||
.textTheme.titleMedium,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
@@ -239,8 +236,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
S.of(context).bluetooth,
|
||||
style: Theme.of(context)
|
||||
.textTheme.bodyMedium,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -251,7 +247,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
child: DeviceTile(
|
||||
onPressed: () => _connectToDevice(device),
|
||||
title: device.name,
|
||||
leading: _getDeviceTileLeading(device.deviceInfo),
|
||||
leading: _getDeviceTileLeading(device.type),
|
||||
connectionType: device.connectionType,
|
||||
),
|
||||
),
|
||||
@@ -265,8 +261,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
S.of(context).usb,
|
||||
style: Theme.of(context)
|
||||
.textTheme.bodyMedium,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -277,7 +272,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
child: DeviceTile(
|
||||
onPressed: () => _connectToDevice(device),
|
||||
title: device.name,
|
||||
leading: _getDeviceTileLeading(device.deviceInfo),
|
||||
leading: _getDeviceTileLeading(device.type),
|
||||
connectionType: device.connectionType,
|
||||
),
|
||||
),
|
||||
@@ -287,10 +282,8 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
if (widget.allowChangeWallet) ...[
|
||||
PrimaryButton(
|
||||
text: S.of(context).wallets,
|
||||
color: Theme.of(context)
|
||||
.colorScheme.primary,
|
||||
textColor: Theme.of(context)
|
||||
.colorScheme.onPrimary,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
onPressed: _onChangeWallet,
|
||||
)
|
||||
],
|
||||
@@ -312,8 +305,8 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
void _onChangeWallet() {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletList,
|
||||
arguments: (BuildContext context) => Navigator.of(context)
|
||||
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false),
|
||||
arguments: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(Routes.dashboard, (route) => false),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/connect_device/widgets/dotted_divider.dart';
|
||||
import 'package:cake_wallet/src/screens/connect_device/widgets/manufacturer_option_tile.dart';
|
||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class SelectDeviceManufacturerPage extends BasePage {
|
||||
SelectDeviceManufacturerPage({
|
||||
this.showUnavailable = true,
|
||||
this.onSelect,
|
||||
this.availableHardwareWalletTypes,
|
||||
});
|
||||
|
||||
final bool showUnavailable;
|
||||
final void Function(BuildContext, HardwareWalletType)? onSelect;
|
||||
final List<HardwareWalletType>? availableHardwareWalletTypes;
|
||||
|
||||
@override
|
||||
String get title => S.current.select_manufacturer_title;
|
||||
|
||||
@override
|
||||
AppBarStyle get appBarStyle => AppBarStyle.regular;
|
||||
|
||||
List<_DeviceManufacturer> get availableManufacturers => [
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/ledger_man.svg', height: 25),
|
||||
hardwareWalletType: HardwareWalletType.ledger,
|
||||
),
|
||||
// _DeviceManufacturer(
|
||||
// image: SvgPicture.asset('assets/images/hardware_wallet/bitbox_man.svg', height: 25),
|
||||
// hardwareWalletType: HardwareWalletType.bitbox,
|
||||
// tag: S.current.new_tag,
|
||||
// ),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/cupcake_man.svg', height: 25),
|
||||
hardwareWalletType: HardwareWalletType.cupcake,
|
||||
tag: S.current.new_tag,
|
||||
),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/coldcard_man.svg', height: 25),
|
||||
hardwareWalletType: HardwareWalletType.coldcard,
|
||||
tag: S.current.new_tag,
|
||||
),
|
||||
// _DeviceManufacturer(
|
||||
// image: SvgPicture.asset('assets/images/hardware_wallet/seedsigner_man.svg', height: 25),
|
||||
// hardwareWalletType: HardwareWalletType.seedsigner,
|
||||
// tag: S.current.new_tag,
|
||||
// ),
|
||||
]
|
||||
.where((e) => availableHardwareWalletTypes == null
|
||||
? ![HardwareWalletType.cupcake].contains(e.hardwareWalletType)
|
||||
: availableHardwareWalletTypes!.contains(e.hardwareWalletType))
|
||||
.toList();
|
||||
|
||||
List<_DeviceManufacturer> get comingManufacturers => [
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/trezor_man.svg', height: 25),
|
||||
tag: S.current.coming_soon_tag,
|
||||
),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/bitbox_man.svg', height: 25),
|
||||
hardwareWalletType: HardwareWalletType.bitbox,
|
||||
tag: S.current.coming_soon_tag,
|
||||
),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/seedsigner_man.svg', height: 25),
|
||||
hardwareWalletType: HardwareWalletType.seedsigner,
|
||||
tag: S.current.coming_soon_tag,
|
||||
),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/foundation_man.svg', height: 25),
|
||||
tag: S.current.coming_soon_tag,
|
||||
),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset('assets/images/hardware_wallet/keystone_man.svg', height: 25),
|
||||
tag: S.current.coming_soon_tag,
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => Container(
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
...availableManufacturers.map(
|
||||
(manufacturer) => Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: ManufacturerOptionTile(
|
||||
image: manufacturer.image,
|
||||
tag: manufacturer.tag,
|
||||
onPressed: () {
|
||||
if (onSelect != null)
|
||||
return onSelect!.call(context, manufacturer.hardwareWalletType!);
|
||||
|
||||
if (isAirgappedWallet(manufacturer.hardwareWalletType)) {
|
||||
_onScanQRCode(context, manufacturer.hardwareWalletType!);
|
||||
} else if (manufacturer.hardwareWalletType != null) {
|
||||
Navigator.pushNamed(context, Routes.connectHardwareWallet,
|
||||
arguments: [manufacturer.hardwareWalletType]);
|
||||
}
|
||||
},
|
||||
isDarkTheme: currentTheme.isDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showUnavailable) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 10),
|
||||
child: DottedDivider(color: Theme.of(context).colorScheme.surfaceContainer),
|
||||
),
|
||||
...comingManufacturers.map(
|
||||
(manufacturer) => Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: ManufacturerOptionTile(
|
||||
image: manufacturer.image,
|
||||
tag: manufacturer.tag,
|
||||
onPressed: () =>
|
||||
Fluttertoast.showToast(msg: 'One more tap and it might work'),
|
||||
// Ester egg
|
||||
isDarkTheme: currentTheme.isDark,
|
||||
isUnavailable: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
bool isAirgappedWallet(HardwareWalletType? type) => [
|
||||
HardwareWalletType.cupcake,
|
||||
HardwareWalletType.coldcard,
|
||||
HardwareWalletType.seedsigner,
|
||||
HardwareWalletType.keystone,
|
||||
].contains(type);
|
||||
|
||||
bool isRestoring = false;
|
||||
|
||||
Future<void> _onScanQRCode(BuildContext context, HardwareWalletType type) async {
|
||||
final isCameraPermissionGranted =
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
|
||||
if (!isCameraPermissionGranted) return;
|
||||
try {
|
||||
if (isRestoring) return;
|
||||
|
||||
isRestoring = true;
|
||||
|
||||
final restoredWallet = await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
|
||||
|
||||
final params = {
|
||||
'walletType': restoredWallet.type,
|
||||
'restoredWallet': restoredWallet,
|
||||
'hardwareWalletType': type,
|
||||
};
|
||||
|
||||
Navigator.pushNamed(context, Routes.restoreWallet, arguments: params)
|
||||
.then((_) => isRestoring = false);
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _DeviceManufacturer {
|
||||
final SvgPicture image;
|
||||
final HardwareWalletType? hardwareWalletType;
|
||||
final String? tag;
|
||||
|
||||
_DeviceManufacturer({required this.image, this.hardwareWalletType, this.tag});
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class DeviceTile extends StatelessWidget {
|
||||
const DeviceTile({
|
||||
@@ -12,15 +13,15 @@ class DeviceTile extends StatelessWidget {
|
||||
final VoidCallback onPressed;
|
||||
final String title;
|
||||
final String? leading;
|
||||
final ConnectionType? connectionType;
|
||||
final HardwareWalletConnectionType? connectionType;
|
||||
|
||||
String? get connectionTypeIcon {
|
||||
switch (connectionType) {
|
||||
case ConnectionType.ble:
|
||||
case HardwareWalletConnectionType.ble:
|
||||
return 'assets/images/bluetooth.png';
|
||||
case ConnectionType.usb:
|
||||
case HardwareWalletConnectionType.usb:
|
||||
return 'assets/images/usb.png';
|
||||
case null:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -42,12 +43,14 @@ class DeviceTile extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
if (leading != null)
|
||||
if (leading != null && !leading!.endsWith(".svg"))
|
||||
Image.asset(
|
||||
leading!,
|
||||
height: 30,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
if (leading != null && leading!.endsWith(".svg"))
|
||||
SvgPicture.asset(leading!, height: 30),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16),
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DottedDivider extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
const DottedDivider({super.key, required this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SizedBox(
|
||||
width: double.infinity,
|
||||
child: CustomPaint(
|
||||
painter: _DashedLinePainter(color: color),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _DashedLinePainter extends CustomPainter {
|
||||
final Color color;
|
||||
|
||||
const _DashedLinePainter({required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
double dashWidth = 9, dashSpace = 5, startX = 0;
|
||||
final paint = Paint()
|
||||
..color = color
|
||||
..strokeWidth = 1;
|
||||
while (startX < size.width) {
|
||||
canvas.drawLine(Offset(startX, 0), Offset(startX + dashWidth, 0), paint);
|
||||
startX += dashWidth + dashSpace;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => false;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ManufacturerOptionTile extends StatelessWidget {
|
||||
const ManufacturerOptionTile({
|
||||
required this.onPressed,
|
||||
required this.image,
|
||||
required this.isDarkTheme,
|
||||
this.tag,
|
||||
this.isUnavailable = false,
|
||||
});
|
||||
|
||||
final VoidCallback onPressed;
|
||||
final Widget image;
|
||||
final String? tag;
|
||||
final bool isUnavailable;
|
||||
final bool isDarkTheme;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Opacity(
|
||||
opacity: isUnavailable ? 0.5 : 1,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
isDarkTheme
|
||||
? CustomThemeColors.cardGradientColorPrimaryDark
|
||||
: CustomThemeColors.cardGradientColorPrimaryLight,
|
||||
isDarkTheme
|
||||
? CustomThemeColors.cardGradientColorSecondaryDark
|
||||
: CustomThemeColors.cardGradientColorSecondaryLight,
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: [
|
||||
Container(height: 25, child: image),
|
||||
if (tag != null)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.transparent),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.surfaceContainerLow),
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
margin: EdgeInsets.only(left: 5),
|
||||
child: Text(tag!))
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import 'package:cake_wallet/src/widgets/standard_switch.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
@@ -27,6 +28,23 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
final btcLockLight = 'assets/images/btc_lock_light.png';
|
||||
final btcLockDark = 'assets/images/btc_lock_dark.png';
|
||||
|
||||
String? get hardwareWalletIcon {
|
||||
switch (dashboardViewModel.wallet.hardwareWalletType) {
|
||||
case null:
|
||||
return null;
|
||||
case HardwareWalletType.ledger:
|
||||
return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
|
||||
case HardwareWalletType.bitbox:
|
||||
return "assets/images/hardware_wallet/device_bitbox.svg";
|
||||
case HardwareWalletType.cupcake:
|
||||
return "assets/images/cupcake.svg";
|
||||
case HardwareWalletType.coldcard:
|
||||
case HardwareWalletType.seedsigner:
|
||||
case HardwareWalletType.keystone:
|
||||
return "assets/images/hardware_wallet/device_qr.svg";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
@@ -86,10 +104,10 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (dashboardViewModel.wallet.isHardwareWallet)
|
||||
if (hardwareWalletIcon != null)
|
||||
Container(
|
||||
child: Image.asset(
|
||||
'assets/images/hardware_wallet/ledger_nano_x.png',
|
||||
child: SvgPicture.asset(
|
||||
hardwareWalletIcon!,
|
||||
width: 24,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dev/moneroc_cache_debug.dart';
|
||||
import 'package:cake_wallet/view_model/dev/qr_tools_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class DevQRToolsPage extends BasePage {
|
||||
@override
|
||||
String? get title => "[dev] *QR tools";
|
||||
|
||||
final QRToolsViewModel viewModel = QRToolsViewModel();
|
||||
|
||||
late final textCtrl = TextEditingController(text: viewModel.input);
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: textCtrl,
|
||||
maxLines: 8,
|
||||
onChanged: (value) {
|
||||
viewModel.input = value;
|
||||
}
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return Expanded(
|
||||
child: JsonExplorer(data: viewModel.data, title: "result"),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_utils.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -11,12 +11,12 @@ import 'package:cake_wallet/src/screens/setup_2fa/widgets/popup_cancellable_aler
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/new_wallet_type_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -51,7 +51,7 @@ class NewWalletTypePage extends BasePage {
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
newWalletTypeViewModel: newWalletTypeViewModel,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
isHardwareWallet: newWalletTypeArguments.isHardwareWallet,
|
||||
hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,14 +61,16 @@ class WalletTypeForm extends StatefulWidget {
|
||||
required this.isCreate,
|
||||
required this.newWalletTypeViewModel,
|
||||
this.onTypeSelected,
|
||||
required this.isHardwareWallet,
|
||||
this.hardwareWalletType,
|
||||
});
|
||||
|
||||
final bool isCreate;
|
||||
final Image walletImage;
|
||||
final NewWalletTypeViewModel newWalletTypeViewModel;
|
||||
final void Function(BuildContext, WalletType)? onTypeSelected;
|
||||
final bool isHardwareWallet;
|
||||
final HardwareWalletType? hardwareWalletType;
|
||||
|
||||
bool get isHardwareWallet => hardwareWalletType != null;
|
||||
|
||||
@override
|
||||
WalletTypeFormState createState() => WalletTypeFormState();
|
||||
@@ -90,7 +92,9 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
types = filteredTypes = availableWalletTypes
|
||||
.where((element) =>
|
||||
!widget.isHardwareWallet ||
|
||||
DeviceConnectionType.supportedConnectionTypes(element, Platform.isIOS).isNotEmpty)
|
||||
DeviceConnectionType.supportedConnectionTypes(
|
||||
element, widget.hardwareWalletType!, Platform.isIOS)
|
||||
.isNotEmpty)
|
||||
.toList();
|
||||
super.initState();
|
||||
|
||||
@@ -148,7 +152,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
isSelected: selected == type,
|
||||
onTap: () => setState(() => selected = type),
|
||||
deviceConnectionTypes: widget.isHardwareWallet
|
||||
? DeviceConnectionType.supportedConnectionTypes(type, Platform.isIOS)
|
||||
? DeviceConnectionType.supportedConnectionTypes(
|
||||
type, widget.hardwareWalletType!, Platform.isIOS)
|
||||
: [],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -111,7 +111,7 @@ class QRWidget extends StatelessWidget {
|
||||
),
|
||||
if (addressListViewModel.isPayjoinUnavailable &&
|
||||
!addressListViewModel.isSilentPayments &&
|
||||
!addressListViewModel.isCupcake) ...[
|
||||
!addressListViewModel.isBitcoinViewOnly) ...[
|
||||
GestureDetector(
|
||||
onTap: () => _onPayjoinInactivePressed(context),
|
||||
child: Row(
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/option_tile.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||
@@ -12,11 +11,9 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
|
||||
class RestoreOptionsPage extends BasePage {
|
||||
RestoreOptionsPage({required this.isNewInstall});
|
||||
@@ -27,9 +24,8 @@ class RestoreOptionsPage extends BasePage {
|
||||
final bool isNewInstall;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return _RestoreOptionsBody(isNewInstall: isNewInstall, themeType: currentTheme.type);
|
||||
}
|
||||
Widget body(BuildContext context) =>
|
||||
_RestoreOptionsBody(isNewInstall: isNewInstall, themeType: currentTheme.type);
|
||||
}
|
||||
|
||||
class _RestoreOptionsBody extends StatefulWidget {
|
||||
@@ -45,41 +41,25 @@ class _RestoreOptionsBody extends StatefulWidget {
|
||||
class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
bool isRestoring = false;
|
||||
|
||||
bool get _doesSupportHardwareWallets {
|
||||
if (isMoneroOnly) {
|
||||
return DeviceConnectionType.supportedConnectionTypes(WalletType.monero, Platform.isIOS)
|
||||
.isNotEmpty;
|
||||
}
|
||||
String get imageRestoreHWPath => widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_hw_dark.png'
|
||||
: 'assets/images/restore_hw.png';
|
||||
|
||||
return true;
|
||||
}
|
||||
String get imageRestoreCupcakePath => widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_cupcake_dark.png'
|
||||
: 'assets/images/restore_cupcake.png';
|
||||
|
||||
String get imageRestoreHWPath =>
|
||||
widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_hw_dark.png'
|
||||
: 'assets/images/restore_hw.png';
|
||||
String get imageRestoreQRPath => widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_qr_dark.png'
|
||||
: 'assets/images/restore_qr.png';
|
||||
|
||||
String get imageRestoreCupcakePath =>
|
||||
widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_cupcake_dark.png'
|
||||
: 'assets/images/restore_cupcake.png';
|
||||
String get imageRestoreHotWalletPath => widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_hot_wallet_dark.png'
|
||||
: 'assets/images/restore_hot_wallet.png';
|
||||
|
||||
String get imageRestoreQRPath =>
|
||||
widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_qr_dark.png'
|
||||
: 'assets/images/restore_qr.png';
|
||||
|
||||
|
||||
String get imageRestoreHotWalletPath =>
|
||||
widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_hot_wallet_dark.png'
|
||||
: 'assets/images/restore_hot_wallet.png';
|
||||
|
||||
|
||||
String get imageRestoreBackupPath =>
|
||||
widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_backup_dark.png'
|
||||
: 'assets/images/restore_backup.png';
|
||||
String get imageRestoreBackupPath => widget.themeType == ThemeType.dark
|
||||
? 'assets/images/restore_backup_dark.png'
|
||||
: 'assets/images/restore_backup.png';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -116,18 +96,20 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
tag: S.of(context).new_tag,
|
||||
),
|
||||
),
|
||||
if (_doesSupportHardwareWallets)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: OptionTile(
|
||||
key: ValueKey('restore_options_from_hardware_wallet_button_key'),
|
||||
onPressed: () =>
|
||||
Navigator.pushNamed(context, Routes.restoreWalletFromHardwareWallet),
|
||||
image: imageRestoreHW,
|
||||
title: S.of(context).restore_title_from_hardware_wallet,
|
||||
description: S.of(context).restore_description_from_hardware_wallet,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: OptionTile(
|
||||
key: ValueKey('restore_options_from_hardware_wallet_button_key'),
|
||||
onPressed: () => Navigator.pushNamed(
|
||||
context,
|
||||
isSingleCoin
|
||||
? Routes.connectHardwareWallet
|
||||
: Routes.restoreWalletFromHardwareWallet),
|
||||
image: imageRestoreHW,
|
||||
title: S.of(context).restore_title_from_hardware_wallet,
|
||||
description: S.of(context).restore_description_from_hardware_wallet,
|
||||
),
|
||||
),
|
||||
if (widget.isNewInstall)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
@@ -143,11 +125,11 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: OptionTile(
|
||||
key: ValueKey('restore_options_from_qr_button_key'),
|
||||
onPressed: () => _onScanQRCode(context),
|
||||
image: imageRestoreQR,
|
||||
title: S.of(context).scan_qr_code,
|
||||
description: S.of(context).cold_or_recover_wallet,
|
||||
key: ValueKey('restore_options_from_qr_button_key'),
|
||||
onPressed: () => _onScanQRCode(context),
|
||||
image: imageRestoreQR,
|
||||
title: S.of(context).scan_qr_code,
|
||||
description: S.of(context).cold_or_recover_wallet,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -184,7 +166,11 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
|
||||
final restoredWallet = await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
|
||||
|
||||
final params = {'walletType': restoredWallet.type, 'restoredWallet': restoredWallet};
|
||||
final params = {
|
||||
'walletType': restoredWallet.type,
|
||||
'restoredWallet': restoredWallet,
|
||||
'hardwareWalletType': HardwareWalletType.cupcake,
|
||||
};
|
||||
|
||||
Navigator.pushNamed(context, Routes.restoreWallet, arguments: params).then((_) {
|
||||
if (mounted) setState(() => isRestoring = false);
|
||||
|
||||
@@ -440,18 +440,21 @@ class SendPage extends BasePage {
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.isHardwareWallet) {
|
||||
if (!sendViewModel.ledgerViewModel!.isConnected) {
|
||||
if (!sendViewModel.hardwareWalletViewModel!.isConnected) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: sendViewModel.walletType,
|
||||
hardwareWalletType:
|
||||
sendViewModel.wallet.walletInfo.hardwareWalletType!,
|
||||
onConnectDevice: (BuildContext context, _) {
|
||||
sendViewModel.ledgerViewModel!
|
||||
.setLedger(sendViewModel.wallet);
|
||||
sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(sendViewModel.wallet);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
));
|
||||
} else {
|
||||
sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet);
|
||||
sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(sendViewModel.wallet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.da
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -26,16 +27,14 @@ class OtherSettingsPage extends BasePage {
|
||||
final OtherSettingsViewModel _otherSettingsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
return Container(
|
||||
Widget body(BuildContext context) => Observer(
|
||||
builder: (_) => Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
if (_otherSettingsViewModel.displayTransactionPriority)
|
||||
_otherSettingsViewModel.walletType == WalletType.bitcoin ?
|
||||
SettingsPriorityPickerCell(
|
||||
_otherSettingsViewModel.walletType == WalletType.bitcoin
|
||||
? SettingsPriorityPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||
displayItem: _otherSettingsViewModel.getDisplayBitcoinPriority,
|
||||
@@ -44,8 +43,8 @@ class OtherSettingsPage extends BasePage {
|
||||
onItemSelected: _otherSettingsViewModel.onDisplayBitcoinPrioritySelected,
|
||||
customValue: _otherSettingsViewModel.customBitcoinFeeRate,
|
||||
maxValue: _otherSettingsViewModel.maxCustomFeeRate?.toDouble(),
|
||||
) :
|
||||
SettingsPickerCell(
|
||||
)
|
||||
: SettingsPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||
displayItem: _otherSettingsViewModel.getDisplayPriority,
|
||||
@@ -55,75 +54,96 @@ class OtherSettingsPage extends BasePage {
|
||||
if (_otherSettingsViewModel.changeRepresentativeEnabled)
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.change_rep,
|
||||
handler: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamed(Routes.changeRep),
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.changeRep),
|
||||
),
|
||||
if (_otherSettingsViewModel.changeHardwareWalletTypeEnabled)
|
||||
SettingsCellWithArrow(
|
||||
title: "Hardware wallet manufacturer",
|
||||
handler: (context) => Navigator.of(context)
|
||||
.pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: {
|
||||
"showUnavailable": false,
|
||||
"availableHardwareWalletTypes": [
|
||||
HardwareWalletType.cupcake,
|
||||
HardwareWalletType.coldcard,
|
||||
HardwareWalletType.seedsigner,
|
||||
],
|
||||
"onSelect": (BuildContext context, HardwareWalletType hwType) async {
|
||||
await _otherSettingsViewModel.onHardwareWalletTypeChanged(hwType);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
}),
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.settings_terms_and_conditions,
|
||||
handler: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamed(Routes.readDisclaimer),
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.readDisclaimer),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions && _otherSettingsViewModel.walletType == WalletType.monero)
|
||||
if (FeatureFlag.hasDevOptions &&
|
||||
_otherSettingsViewModel.walletType == WalletType.monero)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] monero background sync',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devMoneroBackgroundSync),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions && [WalletType.monero, WalletType.wownero, WalletType.zano].contains(_otherSettingsViewModel.walletType))
|
||||
if (FeatureFlag.hasDevOptions &&
|
||||
[WalletType.monero, WalletType.wownero, WalletType.zano]
|
||||
.contains(_otherSettingsViewModel.walletType))
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] xmr call profiler',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devMoneroCallProfiler),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions && [WalletType.monero].contains(_otherSettingsViewModel.walletType))
|
||||
if (FeatureFlag.hasDevOptions &&
|
||||
[WalletType.monero].contains(_otherSettingsViewModel.walletType))
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] xmr wallet cache debug',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devMoneroWalletCacheDebug),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] shared preferences',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devSharedPreferences),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] secure storage preferences',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devSecurePreferences),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] background sync logs',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devBackgroundSyncLogs),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] socket health logs',
|
||||
handler: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devSocketHealthLogs),
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.devSocketHealthLogs),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] network requests logs',
|
||||
handler: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devNetworkRequests),
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.devNetworkRequests),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] exchange provider logs',
|
||||
handler: (BuildContext context) =>
|
||||
handler: (context) =>
|
||||
Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions)
|
||||
SettingsCellWithArrow(
|
||||
title: '[dev] *QR tools',
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.devQRTools),
|
||||
),
|
||||
Spacer(),
|
||||
SettingsVersionCell(
|
||||
title: S.of(context).version(_otherSettingsViewModel.currentVersion)),
|
||||
title: S.of(context).version(_otherSettingsViewModel.currentVersion),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ class AnimatedURPage extends BasePage {
|
||||
child: URQR(
|
||||
urqr: urQr,
|
||||
walletType: animatedURmodel.wallet.type,
|
||||
hardwareWalletType: animatedURmodel.wallet.hardwareWalletType,
|
||||
),
|
||||
),
|
||||
if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header]
|
||||
@@ -125,4 +126,4 @@ class AnimatedURPage extends BasePage {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
@@ -7,67 +6,65 @@ import 'package:cake_wallet/src/screens/ur/widgets/qr_selection_dialog.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class URQR extends StatefulWidget {
|
||||
URQR({super.key, required this.urqr, required this.walletType});
|
||||
const URQR({super.key, required this.urqr, required this.walletType, this.hardwareWalletType});
|
||||
|
||||
final Map<String, String> urqr;
|
||||
final WalletType walletType;
|
||||
final HardwareWalletType? hardwareWalletType;
|
||||
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_URQRState createState() => _URQRState();
|
||||
}
|
||||
|
||||
const urFrameTime = 1000 ~/ 5;
|
||||
|
||||
class _URQRState extends State<URQR> {
|
||||
Timer? t;
|
||||
Timer? _timer;
|
||||
int frame = 0;
|
||||
int selectedInt = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setState(() {
|
||||
t = Timer.periodic(const Duration(milliseconds: urFrameTime), (timer) {
|
||||
_nextFrame();
|
||||
});
|
||||
});
|
||||
}
|
||||
final keys = widget.urqr.keys.toList();
|
||||
if (widget.hardwareWalletType == HardwareWalletType.coldcard) {
|
||||
selectedInt = 1;
|
||||
selected = keys[(selectedInt) % keys.length];
|
||||
} else if (widget.hardwareWalletType == HardwareWalletType.seedsigner) {
|
||||
selectedInt = 2;
|
||||
selected = keys[(selectedInt) % keys.length];
|
||||
}
|
||||
|
||||
void _nextFrame() {
|
||||
setState(() {
|
||||
frame++;
|
||||
});
|
||||
_timer = Timer.periodic(const Duration(milliseconds: urFrameTime), (_) => _nextFrame());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
t?.cancel();
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
late String selected =
|
||||
(widget.urqr.isEmpty) ? "unknown" : widget.urqr.keys.first;
|
||||
int selectedInt = 0;
|
||||
|
||||
List<String> get frames {
|
||||
return widget.urqr[selected]?.split("\n") ?? [];
|
||||
}
|
||||
|
||||
late String nextLabel =
|
||||
widget.urqr.keys.toList()[(selectedInt + 1) % widget.urqr.length];
|
||||
|
||||
void next() {
|
||||
String get nextLabel => widget.urqr.keys.toList()[(selectedInt + 1) % widget.urqr.length];
|
||||
void next() => setState(() {
|
||||
final keys = widget.urqr.keys.toList();
|
||||
setState(() {
|
||||
selectedInt++;
|
||||
nextLabel = keys[(selectedInt + 1) % keys.length];
|
||||
selected = keys[(selectedInt) % keys.length];
|
||||
});
|
||||
}
|
||||
|
||||
selectedInt++;
|
||||
selected = keys[(selectedInt) % keys.length];
|
||||
});
|
||||
|
||||
late String selected = (widget.urqr.isEmpty) ? "unknown" : widget.urqr.keys.first;
|
||||
|
||||
|
||||
List<String> get frames => widget.urqr[selected]?.split("\n") ?? [];
|
||||
void _nextFrame() => setState(() => frame++);
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -107,45 +104,44 @@ All frames:
|
||||
);
|
||||
}
|
||||
|
||||
Widget _legacySwitch(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: PrimaryButton(
|
||||
onPressed: next,
|
||||
text: nextLabel,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
Widget _legacySwitch(BuildContext context) => SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: PrimaryButton(
|
||||
onPressed: next,
|
||||
text: nextLabel,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Widget _newSwitch(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(height: 32),
|
||||
const SizedBox(height: 32),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.qr_code,
|
||||
color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
SizedBox(width: 4),
|
||||
Text("QR Code Format",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
)),
|
||||
Icon(Icons.qr_code, color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
"QR Code Format",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@@ -176,7 +172,7 @@ All frames:
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -193,10 +189,7 @@ All frames:
|
||||
_getCurrentFormatSubtitle(),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.6),
|
||||
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -204,10 +197,7 @@ All frames:
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.4),
|
||||
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
size: 24,
|
||||
),
|
||||
],
|
||||
@@ -216,7 +206,7 @@ All frames:
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
const SizedBox(height: 16),
|
||||
GestureDetector(
|
||||
onTap: () => _showInfoDialog(context),
|
||||
child: Row(
|
||||
@@ -226,35 +216,32 @@ All frames:
|
||||
"What's this for?",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getCurrentFormatName() {
|
||||
if (widget.urqr.isEmpty) return "Unknown";
|
||||
final currentKey =
|
||||
widget.urqr.keys.elementAt(selectedInt % widget.urqr.length);
|
||||
final currentKey = widget.urqr.keys.elementAt(selectedInt % widget.urqr.length);
|
||||
return currentKey.split(' ')[0];
|
||||
}
|
||||
|
||||
String _getCurrentFormatSubtitle() {
|
||||
if (widget.urqr.isEmpty) return "";
|
||||
final currentKey =
|
||||
widget.urqr.keys.elementAt(selectedInt % widget.urqr.length);
|
||||
final currentKey = widget.urqr.keys.elementAt(selectedInt % widget.urqr.length);
|
||||
return currentKey.substring(currentKey.indexOf(' ') + 1);
|
||||
}
|
||||
|
||||
@@ -264,30 +251,25 @@ All frames:
|
||||
final keys = widget.urqr.keys.toList();
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return QRFormatSelectionDialog(
|
||||
formats: keys,
|
||||
currentIndex: selectedInt,
|
||||
onFormatSelected: (index) {
|
||||
Navigator.pop(context);
|
||||
setState(() {
|
||||
selectedInt = index;
|
||||
selected = keys[index];
|
||||
nextLabel = keys[(index + 1) % keys.length];
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
context: context,
|
||||
builder: (context) => QRFormatSelectionDialog(
|
||||
formats: keys,
|
||||
currentIndex: selectedInt,
|
||||
onFormatSelected: (index) {
|
||||
Navigator.pop(context);
|
||||
setState(() {
|
||||
selectedInt = index;
|
||||
selected = keys[index];
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showInfoDialog(BuildContext context) async {
|
||||
await showModalBottomSheet(
|
||||
Future<void> _showInfoDialog(BuildContext context) => showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return QRFormatInfoBottomSheet();
|
||||
});
|
||||
}
|
||||
builder: (_) => QRFormatInfoBottomSheet(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -477,8 +479,9 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: WalletType.monero,
|
||||
hardwareWalletType: HardwareWalletType.ledger,
|
||||
onConnectDevice: (context, ledgerVM) async {
|
||||
monero!.setGlobalLedgerConnection(ledgerVM.connection);
|
||||
if (ledgerVM is LedgerViewModel) monero!.setGlobalLedgerConnection(ledgerVM.connection);
|
||||
didConnect = true;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user