mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
CW-1291-ledger-fixes (#3390)
* feat: add connecting indicator in hardware wallet connect screen * chore: bump ledger related dependencies * chore: bump ledger related dependencies * feat: add additional guard against gLedger being null * auto-reformat * feat: improve ledger connection and state management * fix: connect_device_page padding on iOS * chore: update universal_ble dependency reference --------- Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Robert Malikowski
Omar Hatem
parent
0822c7d976
commit
ed540bae04
+7
-4
@@ -421,16 +421,19 @@ Future<void> setup({
|
||||
case HardwareWalletType.seedsigner:
|
||||
case HardwareWalletType.keystone:
|
||||
throw Exception(
|
||||
"This should not have happened, because airgapped Wallets don't need View Models");
|
||||
"This should not have happened, because airgapped Wallets don't need View Models",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
getIt.registerLazySingleton(() => LedgerViewModel());
|
||||
getIt.registerLazySingleton(() => LedgerViewModel(getIt<AppStore>()));
|
||||
|
||||
getIt.registerLazySingleton(() => BitboxViewModel());
|
||||
getIt.registerLazySingleton(BitboxViewModel.new);
|
||||
|
||||
getIt.registerLazySingleton(
|
||||
() => TrezorConnect("cakewallet://trezor_connect", appName: "Cake Wallet"));
|
||||
() => TrezorConnect("cakewallet://trezor_connect", appName: "Cake Wallet"),
|
||||
);
|
||||
|
||||
getIt.registerLazySingleton(() => TrezorConnectViewModel(getIt<TrezorConnect>()));
|
||||
|
||||
getIt.registerFactory<KeyService>(() => KeyService(getIt.get<SecureStorage>()));
|
||||
|
||||
@@ -433,6 +433,9 @@ class CWMonero extends Monero {
|
||||
@override
|
||||
void setGlobalLedgerConnection(ledger.LedgerConnection connection) => gLedger = connection;
|
||||
|
||||
@override
|
||||
bool hasGlobalLedgerConnection() => gLedger != null;
|
||||
|
||||
@override
|
||||
String? getLastLedgerCommand() => latestLedgerCommand;
|
||||
|
||||
|
||||
@@ -75,6 +75,23 @@ void startAuthenticationStateChange(
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ledgerVM.suppressMoneroReconnectPrompt = true;
|
||||
|
||||
if (!monero!.hasGlobalLedgerConnection()) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: "Unexpected Error",
|
||||
alertContent:
|
||||
"Unexpected Error while trying to connect to the device (has no global LedgerConnection)",
|
||||
buttonText: S.of(context).try_again,
|
||||
buttonAction: Navigator.of(context).pop,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
@@ -85,14 +102,17 @@ void startAuthenticationStateChange(
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
),
|
||||
);
|
||||
bool tryOpening = true;
|
||||
|
||||
var isConnected = false;
|
||||
var tryOpening = true;
|
||||
while (tryOpening) {
|
||||
try {
|
||||
await loadCurrentWallet();
|
||||
isConnected = true;
|
||||
tryOpening = false;
|
||||
} on Exception catch (e) {
|
||||
final ledgerErrorMessage = ledgerVM.interpretErrorCode(e.toString());
|
||||
if (ledgerErrorMessage != null) {
|
||||
if (ledgerErrorMessage != null && context.mounted) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithTwoActions(
|
||||
@@ -116,16 +136,17 @@ void startAuthenticationStateChange(
|
||||
}
|
||||
|
||||
getIt.get<BottomSheetService>().showNext();
|
||||
await navigatorKey.currentState!
|
||||
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||
if (isConnected) {
|
||||
ledgerVM.suppressMoneroReconnectPrompt = false;
|
||||
await navigatorKey.currentState!
|
||||
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||
}
|
||||
},
|
||||
allowChangeWallet: true,
|
||||
),
|
||||
);
|
||||
|
||||
// await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.connectDevices, (route) => false, arguments: ConnectDevicePageParams(walletType: walletType, onConnectDevice: onConnectDevice));
|
||||
} else {
|
||||
navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final linkViewModel = getIt.get<LinkViewModel>();
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
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/main.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
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/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:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/main.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
|
||||
import "package:cake_wallet/routes.dart";
|
||||
import "package:cake_wallet/src/screens/base_page.dart";
|
||||
import "package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart";
|
||||
import "package:cake_wallet/src/widgets/cake_image_widget.dart";
|
||||
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/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/cupertino.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_mobx/flutter_mobx.dart";
|
||||
|
||||
typedef OnConnectDevice = void Function(BuildContext, HardwareWalletViewModel);
|
||||
|
||||
class ConnectDevicePageParams {
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final bool isReconnect;
|
||||
final HardwareWalletType hardwareWalletType;
|
||||
|
||||
ConnectDevicePageParams({
|
||||
const ConnectDevicePageParams({
|
||||
required this.walletType,
|
||||
required this.hardwareWalletType,
|
||||
required this.onConnectDevice,
|
||||
this.allowChangeWallet = false,
|
||||
this.isReconnect = true,
|
||||
});
|
||||
}
|
||||
|
||||
class ConnectDevicePage extends BasePage {
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final bool isReconnect;
|
||||
final HardwareWalletViewModel hardwareWalletVM;
|
||||
final HardwareWalletType hardwareWalletType;
|
||||
}
|
||||
|
||||
class ConnectDevicePage extends BasePage {
|
||||
ConnectDevicePage(ConnectDevicePageParams params, this.hardwareWalletVM)
|
||||
: walletType = params.walletType,
|
||||
onConnectDevice = params.onConnectDevice,
|
||||
allowChangeWallet = params.allowChangeWallet,
|
||||
isReconnect = params.isReconnect;
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final bool isReconnect;
|
||||
final HardwareWalletViewModel hardwareWalletVM;
|
||||
|
||||
@override
|
||||
String get title => isReconnect
|
||||
@@ -60,31 +60,32 @@ class ConnectDevicePage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => PopScope(
|
||||
canPop: !isReconnect,
|
||||
child: ConnectDevicePageBody(
|
||||
walletType,
|
||||
onConnectDevice,
|
||||
allowChangeWallet,
|
||||
hardwareWalletVM,
|
||||
currentTheme,
|
||||
));
|
||||
canPop: !isReconnect,
|
||||
child: ConnectDevicePageBody(
|
||||
walletType,
|
||||
onConnectDevice,
|
||||
hardwareWalletVM,
|
||||
currentTheme,
|
||||
allowChangeWallet: allowChangeWallet,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class ConnectDevicePageBody extends StatefulWidget {
|
||||
const ConnectDevicePageBody(
|
||||
this.walletType,
|
||||
this.onConnectDevice,
|
||||
this.hardwareWalletVM,
|
||||
this.currentTheme, {
|
||||
this.allowChangeWallet = false,
|
||||
});
|
||||
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
final bool allowChangeWallet;
|
||||
final HardwareWalletViewModel hardwareWalletVM;
|
||||
final MaterialThemeBase currentTheme;
|
||||
|
||||
const ConnectDevicePageBody(
|
||||
this.walletType,
|
||||
this.onConnectDevice,
|
||||
this.allowChangeWallet,
|
||||
this.hardwareWalletVM,
|
||||
this.currentTheme,
|
||||
);
|
||||
|
||||
@override
|
||||
ConnectDevicePageBodyState createState() => ConnectDevicePageBodyState();
|
||||
}
|
||||
@@ -95,10 +96,10 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
|
||||
List<HardwareWalletDevice> get allDevices => [...bleDevices, ...usbDevices];
|
||||
|
||||
late Timer? _usbRefreshTimer = null;
|
||||
late Timer? _bleRefreshTimer = null;
|
||||
late Timer? _bleStateTimer = null;
|
||||
late StreamSubscription<HardwareWalletDevice>? _bleRefresh = null;
|
||||
Timer? _usbRefreshTimer;
|
||||
Timer? _bleRefreshTimer;
|
||||
Timer? _bleStateTimer;
|
||||
StreamSubscription<HardwareWalletDevice>? _bleRefresh;
|
||||
|
||||
bool longWait = false;
|
||||
Timer? _longWaitTimer;
|
||||
@@ -107,19 +108,22 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_bleStateTimer =
|
||||
Timer.periodic(Duration(seconds: 1), (_) => widget.hardwareWalletVM.updateBleState());
|
||||
_bleStateTimer = Timer.periodic(
|
||||
const Duration(seconds: 1),
|
||||
(_) => widget.hardwareWalletVM.updateBleState(),
|
||||
);
|
||||
|
||||
_bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices());
|
||||
_bleRefreshTimer = Timer.periodic(const Duration(seconds: 1), (_) => _refreshBleDevices());
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
_usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices());
|
||||
_usbRefreshTimer = Timer.periodic(const Duration(seconds: 1), (_) => _refreshUsbDevices());
|
||||
}
|
||||
|
||||
if (widget.hardwareWalletVM.hasBluetooth) {
|
||||
_longWaitTimer = Timer(Duration(seconds: 10), () {
|
||||
if (widget.hardwareWalletVM.isBleEnabled && bleDevices.isEmpty)
|
||||
_longWaitTimer = Timer(const Duration(seconds: 10), () {
|
||||
if (widget.hardwareWalletVM.isBleEnabled && bleDevices.isEmpty) {
|
||||
setState(() => longWait = true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -134,22 +138,29 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
_longWaitTimer?.cancel();
|
||||
|
||||
widget.hardwareWalletVM.stopScanning();
|
||||
_isConnectPressed = false;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _refreshUsbDevices() async {
|
||||
final dev = await widget.hardwareWalletVM.getAllUsbDevices();
|
||||
if (usbDevices.length != dev.length) setState(() => usbDevices = dev);
|
||||
|
||||
if (usbDevices.length != dev.length) {
|
||||
setState(() => usbDevices = dev);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _refreshBleDevices() async {
|
||||
try {
|
||||
if (widget.hardwareWalletVM.isBleEnabled) {
|
||||
_bleRefresh = widget.hardwareWalletVM.scanForBleDevices().listen((device) => setState(() {
|
||||
bleDevices.add(device);
|
||||
if (longWait) longWait = false;
|
||||
}))
|
||||
..onError((Object e) {
|
||||
_bleRefresh = widget.hardwareWalletVM.scanForBleDevices().listen(
|
||||
(device) => setState(() {
|
||||
bleDevices.add(device);
|
||||
if (longWait) {
|
||||
longWait = false;
|
||||
}
|
||||
}),
|
||||
)..onError((e) {
|
||||
printV(e);
|
||||
});
|
||||
_bleRefreshTimer?.cancel();
|
||||
@@ -160,48 +171,66 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connectToDevice(HardwareWalletDevice device) async {
|
||||
final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
|
||||
var _isConnectPressed = false;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isConnected)
|
||||
widget.onConnectDevice(navigatorKey.currentContext!, widget.hardwareWalletVM);
|
||||
});
|
||||
Future<void> _connectToDevice(HardwareWalletDevice device) async {
|
||||
if (_isConnectPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isConnectPressed = true;
|
||||
try {
|
||||
final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
|
||||
_isConnectPressed = false;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isConnected) {
|
||||
widget.onConnectDevice(navigatorKey.currentContext!, widget.hardwareWalletVM);
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
_isConnectPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
String _getDeviceTileLeading(HardwareWalletDeviceType deviceType) {
|
||||
printV(deviceType);
|
||||
switch (deviceType) {
|
||||
case HardwareWalletDeviceType.ledgerNanoX:
|
||||
return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
|
||||
case HardwareWalletDeviceType.ledgerNanoGen5:
|
||||
return 'assets/new-ui/hardware_wallets/device_ledger_nano_gen_5.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_gen_5.svg";
|
||||
case HardwareWalletDeviceType.ledgerStax:
|
||||
return 'assets/new-ui/hardware_wallets/ledger_stax.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_stax.svg";
|
||||
case HardwareWalletDeviceType.ledgerFlex:
|
||||
return 'assets/new-ui/hardware_wallets/ledger_flex.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_flex.svg";
|
||||
case HardwareWalletDeviceType.BitBox02:
|
||||
case HardwareWalletDeviceType.BitBox02Nova:
|
||||
return 'assets/new-ui/hardware_wallets/device_bitbox.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_bitbox.svg";
|
||||
case HardwareWalletDeviceType.trezorModelOne:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_model_one.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_model_one.svg";
|
||||
case HardwareWalletDeviceType.trezorModelT:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_model_t.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_model_t.svg";
|
||||
case HardwareWalletDeviceType.trezorSafe3:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_safe_3.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_3.svg";
|
||||
case HardwareWalletDeviceType.trezorSafe5:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_safe_5.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_5.svg";
|
||||
case HardwareWalletDeviceType.trezorSafe7:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_safe_7.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_7.svg";
|
||||
|
||||
default:
|
||||
return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
|
||||
}
|
||||
}
|
||||
|
||||
String get description {
|
||||
if (!Platform.isAndroid) return S.of(context).connect_your_hardware_wallet_ble;
|
||||
if (!Platform.isAndroid) {
|
||||
return S.of(context).connect_your_hardware_wallet_ble;
|
||||
}
|
||||
|
||||
if (widget.hardwareWalletVM.hasBluetooth) return S.of(context).connect_your_hardware_wallet;
|
||||
if (widget.hardwareWalletVM.hasBluetooth) {
|
||||
return S.of(context).connect_your_hardware_wallet;
|
||||
}
|
||||
|
||||
return S.of(context).connect_your_hardware_wallet_usb;
|
||||
}
|
||||
@@ -210,7 +239,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
Widget build(BuildContext context) => Center(
|
||||
child: Container(
|
||||
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
|
||||
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 24),
|
||||
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
@@ -218,9 +247,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
child: Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/hardware_wallet.svg",
|
||||
),
|
||||
const CakeImageWidget(imageUrl: "assets/new-ui/hardware_wallet.svg"),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
@@ -231,7 +258,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
offstage: widget.hardwareWalletVM.isBleEnabled ||
|
||||
!widget.hardwareWalletVM.hasBluetooth,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Text(
|
||||
S.of(context).ledger_please_enable_bluetooth,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
@@ -242,67 +269,89 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18)),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final item = allDevices[index];
|
||||
return GestureDetector(
|
||||
onTap: () => _connectToDevice(item),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: _getDeviceTileLeading(item.type),
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn),
|
||||
),
|
||||
Text(item.name)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
if (item.connectionType ==
|
||||
HardwareWalletConnectionType.ble)
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/bluetooth.svg",
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
itemBuilder: (context, index) {
|
||||
final item = allDevices[index];
|
||||
return GestureDetector(
|
||||
onTap: () => _connectToDevice(item),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) {
|
||||
if (widget.hardwareWalletVM.isConnecting) {
|
||||
return CupertinoActivityIndicator(
|
||||
animating: true,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
radius: 12,
|
||||
);
|
||||
}
|
||||
|
||||
return CakeImageWidget(
|
||||
imageUrl: _getDeviceTileLeading(item.type),
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
BlendMode.srcIn),
|
||||
),
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Text(item.name),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
if (item.connectionType ==
|
||||
HardwareWalletConnectionType.ble)
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/arrow_forward.svg",
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
imageUrl: "assets/new-ui/bluetooth.svg",
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/arrow_forward.svg",
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh),
|
||||
itemCount: allDevices.length),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
itemCount: allDevices.length,
|
||||
),
|
||||
),
|
||||
if (widget.allowChangeWallet) ...[
|
||||
PrimaryButton(
|
||||
@@ -310,7 +359,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
onPressed: _onChangeWallet,
|
||||
)
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
@@ -321,7 +370,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
textColor: Theme.of(context).colorScheme.primary,
|
||||
onPressed: () => _onHowToConnect(context),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -339,17 +388,25 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (BuildContext bottomSheetContext) => InfoStepsBottomSheet(
|
||||
builder: (_) => InfoStepsBottomSheet(
|
||||
titleText: S.of(context).how_to_connect,
|
||||
steps: [
|
||||
InfoStep('assets/images/wallet_connect_step_icons/step1_power.svg',
|
||||
S.of(context).connect_hw_info_step_1),
|
||||
InfoStep('assets/images/wallet_connect_step_icons/step2_connect.svg',
|
||||
S.of(context).connect_hw_info_step_2),
|
||||
InfoStep('assets/images/wallet_connect_step_icons/step3_unlock.svg',
|
||||
S.of(context).connect_hw_info_step_3),
|
||||
InfoStep('assets/images/wallet_connect_step_icons/step4_select.svg',
|
||||
S.of(context).connect_hw_info_step_4),
|
||||
InfoStep(
|
||||
"assets/images/wallet_connect_step_icons/step1_power.svg",
|
||||
S.of(context).connect_hw_info_step_1,
|
||||
),
|
||||
InfoStep(
|
||||
"assets/images/wallet_connect_step_icons/step2_connect.svg",
|
||||
S.of(context).connect_hw_info_step_2,
|
||||
),
|
||||
InfoStep(
|
||||
"assets/images/wallet_connect_step_icons/step3_unlock.svg",
|
||||
S.of(context).connect_hw_info_step_3,
|
||||
),
|
||||
InfoStep(
|
||||
"assets/images/wallet_connect_step_icons/step4_select.svg",
|
||||
S.of(context).connect_hw_info_step_4,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
+24
-23
@@ -1,25 +1,25 @@
|
||||
import 'dart:async';
|
||||
import "dart:async";
|
||||
|
||||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_store.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cake_wallet/store/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import "package:cake_wallet/core/amount_parsing_proxy.dart";
|
||||
import "package:cake_wallet/di.dart";
|
||||
import "package:cake_wallet/entities/preferences_key.dart";
|
||||
import "package:cake_wallet/reactions/wallet_connect.dart";
|
||||
import "package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart";
|
||||
import "package:cake_wallet/store/authentication_store.dart";
|
||||
import "package:cake_wallet/store/settings_store.dart";
|
||||
import "package:cake_wallet/store/wallet_list_store.dart";
|
||||
import "package:cake_wallet/themes/core/theme_store.dart";
|
||||
import "package:cake_wallet/utils/exception_handler.dart";
|
||||
import "package:cw_core/balance.dart";
|
||||
import "package:cw_core/transaction_history.dart";
|
||||
import "package:cw_core/transaction_info.dart";
|
||||
import "package:cw_core/utils/print_verbose.dart";
|
||||
import "package:cw_core/wallet_base.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
|
||||
part 'app_store.g.dart';
|
||||
part "app_store.g.dart";
|
||||
|
||||
class AppStore = AppStoreBase with _$AppStore;
|
||||
|
||||
@@ -59,8 +59,9 @@ abstract class AppStoreBase with Store {
|
||||
@action
|
||||
Future<void> changeCurrentWallet(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) async {
|
||||
bool changingToSameWalletType = this.wallet?.type == wallet.type;
|
||||
this.wallet?.close(shouldCleanup: !changingToSameWalletType);
|
||||
final changingToSameWalletType = this.wallet?.type == wallet.type;
|
||||
|
||||
await this.wallet?.close(shouldCleanup: !changingToSameWalletType);
|
||||
this.wallet = wallet;
|
||||
this.wallet!.setExceptionHandler(ExceptionHandler.onError);
|
||||
|
||||
@@ -80,7 +81,7 @@ abstract class AppStoreBase with Store {
|
||||
wcService.create();
|
||||
await wcService.init();
|
||||
} catch (e, s) {
|
||||
printV('WalletConnect setup failed: $e\n$s');
|
||||
printV("WalletConnect setup failed: $e\n$s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isConnecting = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => Platform.isIOS && false; // TODO: remove when we enable bluetooth
|
||||
|
||||
@@ -52,10 +56,11 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
Future<void> stopScanning() async {}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
|
||||
if (!(device is BitboxHardwareWalletDevice)) return false;
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
if (isConnecting) return false;
|
||||
isConnecting = true;
|
||||
|
||||
try {
|
||||
final appDocDir = await getAppDir();
|
||||
@@ -66,17 +71,15 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
printV("Bitbox initialized!", file: '${appDocDir.path}/error.txt');
|
||||
await bitboxManager.channelHashVerify();
|
||||
printV("Bitbox channel-hash verified!", file: '${appDocDir.path}/error.txt');
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
return true;
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _isConnecting = false;
|
||||
|
||||
@override
|
||||
bool isConnected(WalletType type) => false;
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import "package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
|
||||
import "package:cw_core/hardware/hardware_wallet_service.dart";
|
||||
import "package:cw_core/wallet_base.dart";
|
||||
import "package:cw_core/wallet_info.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
|
||||
abstract class HardwareWalletViewModel {
|
||||
HardwareWalletType get hardwareWalletType;
|
||||
bool get isBleEnabled;
|
||||
bool get hasBluetooth;
|
||||
bool get isConnecting;
|
||||
|
||||
bool isConnected(WalletType type);
|
||||
|
||||
@@ -26,4 +27,6 @@ abstract class HardwareWalletViewModel {
|
||||
Future<void> initWallet(WalletBase wallet);
|
||||
|
||||
String? interpretErrorCode(String error) => null;
|
||||
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,47 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as sdk;
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import "package:cake_wallet/bitcoin/bitcoin.dart";
|
||||
import "package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
|
||||
import "package:cake_wallet/evm/evm.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/main.dart";
|
||||
import "package:cake_wallet/monero/monero.dart";
|
||||
import "package:cake_wallet/routes.dart";
|
||||
import "package:cake_wallet/src/screens/connect_device/connect_device_page.dart";
|
||||
import "package:cake_wallet/store/app_store.dart";
|
||||
import "package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart";
|
||||
import "package:cake_wallet/wallet_type_utils.dart";
|
||||
import "package:cw_core/hardware/device_connection_type.dart";
|
||||
import "package:cw_core/hardware/hardware_wallet_service.dart";
|
||||
import "package:cw_core/utils/print_verbose.dart";
|
||||
import "package:cw_core/wallet_base.dart";
|
||||
import "package:cw_core/wallet_info.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:ledger_flutter_plus/ledger_flutter_plus.dart" as sdk;
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:permission_handler/permission_handler.dart";
|
||||
|
||||
part 'ledger_view_model.g.dart';
|
||||
part "ledger_view_model.g.dart";
|
||||
|
||||
class LedgerViewModel = LedgerViewModelBase with _$LedgerViewModel;
|
||||
|
||||
abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
late final sdk.LedgerInterface ledgerPlusBLE;
|
||||
late final sdk.LedgerInterface ledgerPlusUSB;
|
||||
|
||||
bool get _doesSupportHardwareWallets {
|
||||
if (isMoneroOnly) {
|
||||
return DeviceConnectionType.supportedConnectionTypes(
|
||||
WalletType.monero, HardwareWalletType.ledger, Platform.isIOS)
|
||||
.isNotEmpty;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LedgerViewModelBase() {
|
||||
LedgerViewModelBase(AppStore appStore) {
|
||||
if (_doesSupportHardwareWallets) {
|
||||
reaction((_) => isBleEnabled, (_) {
|
||||
if (isBleEnabled) _initBLE();
|
||||
if (isBleEnabled) {
|
||||
_initBLE();
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => appStore.wallet, (wallet) async {
|
||||
if (wallet != null && wallet.type != _connectingWalletType) {
|
||||
await close();
|
||||
}
|
||||
});
|
||||
|
||||
updateBleState();
|
||||
|
||||
if (!Platform.isIOS) {
|
||||
@@ -53,6 +50,21 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
}
|
||||
}
|
||||
|
||||
late final sdk.LedgerInterface ledgerPlusBLE;
|
||||
late final sdk.LedgerInterface ledgerPlusUSB;
|
||||
|
||||
bool get _doesSupportHardwareWallets {
|
||||
if (isMoneroOnly) {
|
||||
return DeviceConnectionType.supportedConnectionTypes(
|
||||
WalletType.monero,
|
||||
HardwareWalletType.ledger,
|
||||
Platform.isIOS,
|
||||
).isNotEmpty;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.ledger;
|
||||
|
||||
@@ -60,6 +72,10 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isConnecting = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => true;
|
||||
|
||||
@@ -69,9 +85,11 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
if (isBleEnabled && !_bleIsInitialized) {
|
||||
ledgerPlusBLE = sdk.LedgerInterface.ble(
|
||||
onPermissionRequest: (_) async {
|
||||
if (Platform.isMacOS) return true;
|
||||
if (Platform.isMacOS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<Permission, PermissionStatus> statuses = await [
|
||||
final statuses = await [
|
||||
Permission.bluetoothScan,
|
||||
Permission.bluetoothConnect,
|
||||
Permission.bluetoothAdvertise,
|
||||
@@ -79,7 +97,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
|
||||
return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||
},
|
||||
bleOptions: sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)),
|
||||
bleOptions: sdk.BluetoothOptions(maxScanDuration: const Duration(minutes: 5)),
|
||||
);
|
||||
_bleIsInitialized = true;
|
||||
}
|
||||
@@ -91,28 +109,41 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
|
||||
final newState = bleState == sdk.AvailabilityState.poweredOn;
|
||||
|
||||
if (newState != isBleEnabled) isBleEnabled = newState;
|
||||
if (newState != isBleEnabled) {
|
||||
isBleEnabled = newState;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<HardwareWalletDevice> scanForBleDevices() =>
|
||||
ledgerPlusBLE.scan().map((d) => LedgerHardwareWalletDevice(d));
|
||||
ledgerPlusBLE.scan().map(LedgerHardwareWalletDevice.new);
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() => ledgerPlusUSB.devices
|
||||
.then((devices) => devices.map((d) => LedgerHardwareWalletDevice(d)).toList());
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() =>
|
||||
ledgerPlusUSB.devices.then((devices) => devices.map(LedgerHardwareWalletDevice.new).toList());
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {
|
||||
if (_bleIsInitialized) await ledgerPlusBLE.stopScanning();
|
||||
if (!Platform.isIOS) await ledgerPlusUSB.stopScanning();
|
||||
if (_bleIsInitialized) {
|
||||
await ledgerPlusBLE.stopScanning();
|
||||
}
|
||||
if (!Platform.isIOS) {
|
||||
await ledgerPlusUSB.stopScanning();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
|
||||
if (!(device is LedgerHardwareWalletDevice)) return false;
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
if (device is! LedgerHardwareWalletDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isConnecting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isConnecting = true;
|
||||
_connectingWalletType = type;
|
||||
if (isConnected(type)) {
|
||||
try {
|
||||
@@ -123,34 +154,36 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
final ledger =
|
||||
device.connectionType == HardwareWalletConnectionType.ble ? ledgerPlusBLE : ledgerPlusUSB;
|
||||
|
||||
if (_connectionChangeSubscription == null) {
|
||||
_connectionChangeSubscription =
|
||||
ledger.deviceStateChanges(device.device.id).listen(_connectionChangeListener);
|
||||
}
|
||||
_connectionChangeSubscription ??=
|
||||
ledger.deviceStateChanges(device.device.id).listen(_connectionChangeListener);
|
||||
|
||||
try {
|
||||
_connection = await ledger.connect(device.device);
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
return true;
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamSubscription<sdk.BleConnectionState>? _connectionChangeSubscription;
|
||||
sdk.LedgerConnection? _connection;
|
||||
bool _isConnecting = false;
|
||||
WalletType? _connectingWalletType;
|
||||
bool suppressMoneroReconnectPrompt = false;
|
||||
|
||||
void _connectionChangeListener(sdk.BleConnectionState event) {
|
||||
printV('Ledger Device State Changed: $event');
|
||||
if (event == sdk.BleConnectionState.disconnected && !_isConnecting) {
|
||||
printV("Ledger Device State Changed: $event");
|
||||
if (event == sdk.BleConnectionState.disconnected && !isConnecting) {
|
||||
_connection = null;
|
||||
if (_connectingWalletType == WalletType.monero) {
|
||||
monero!.resetLedgerConnection();
|
||||
|
||||
if (suppressMoneroReconnectPrompt) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(navigatorKey.currentContext!).pushNamed(
|
||||
Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
@@ -158,7 +191,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
hardwareWalletType: HardwareWalletType.ledger,
|
||||
allowChangeWallet: true,
|
||||
isReconnect: true,
|
||||
onConnectDevice: (context, ledgerVM) async {
|
||||
onConnectDevice: (context, ledgerVM) {
|
||||
if (context.mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
@@ -170,26 +203,24 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
bool isConnected(WalletType type) => _connection != null && !(_connection!.isDisconnected);
|
||||
bool isConnected(WalletType type) => _connection != null && !_connection!.isDisconnected;
|
||||
|
||||
sdk.LedgerConnection get connection => _connection!;
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
Future<void> initWallet(WalletBase wallet) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return monero!.setLedgerConnection(wallet, connection);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!
|
||||
.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!
|
||||
.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return evm!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type} for ledger');
|
||||
throw Exception("Unexpected wallet type: ${wallet.type} for ledger");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,16 +247,31 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
return error;
|
||||
}
|
||||
|
||||
var errorRegex = RegExp(r'(?:0x\S*?|[0-9a-f]{4})(?= )').firstMatch(error.toString());
|
||||
final errorRegex = RegExp(r"(?:0x\S*?|[0-9a-f]{4})(?= )").firstMatch(error.toString());
|
||||
|
||||
String errorCode = errorRegex?.group(0).toString().replaceAll("0x", "") ?? "";
|
||||
final errorCode = errorRegex?.group(0).toString().replaceAll("0x", "") ?? "";
|
||||
if (errorCode.contains("6985")) {
|
||||
return S.current.ledger_error_tx_rejected_by_user;
|
||||
} else if (errorCode.contains("5515")) {
|
||||
return S.current.ledger_error_device_locked;
|
||||
} else if (["6e01", "6a87", "6d02", "6511", "6e00"].any((e) => errorCode.contains(e))) {
|
||||
} else if (["6e01", "6a87", "6d02", "6511", "6e00"].any(errorCode.contains)) {
|
||||
return S.current.ledger_error_wrong_app;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
printV("Resetting ledger VM");
|
||||
await _connectionChangeSubscription?.cancel();
|
||||
await _connection?.disconnect().catchError((_) {});
|
||||
|
||||
_connectingWalletType = null;
|
||||
_connectionChangeSubscription = null;
|
||||
_connection = null;
|
||||
isConnecting = false;
|
||||
await stopScanning();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isConnecting = false;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
@@ -112,7 +116,6 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
|
||||
if (!Platform.isIOS) await trezorUSB.stopScanning();
|
||||
}
|
||||
|
||||
bool _isConnecting = false;
|
||||
sdk.TrezorClient? _client;
|
||||
|
||||
Completer<String?>? _pinCompleter;
|
||||
@@ -123,11 +126,12 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
|
||||
void setParingPin(String pin) => _pinCompleter?.complete(pin);
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type,
|
||||
[bool isRetry = false]) async {
|
||||
if (!(device is TrezorHardwareWalletDevice)) return false;
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
if (isConnecting) return false;
|
||||
isConnecting = true;
|
||||
paringState = TrezorParingState.initial;
|
||||
|
||||
try {
|
||||
@@ -182,7 +186,7 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
|
||||
printV(e);
|
||||
return false;
|
||||
} finally {
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,7 @@ abstract class WalletListViewModelBase with Store {
|
||||
if (walletItem.type == WalletType.haven) {
|
||||
return;
|
||||
}
|
||||
// bool switchingToSameWalletType = walletItem.type == _appStore.wallet?.type;
|
||||
// await _appStore.wallet?.close(shouldCleanup: !switchingToSameWalletType);
|
||||
|
||||
final wallet = await _walletLoadingService.load(walletItem.type, walletItem.name);
|
||||
await _appStore.changeCurrentWallet(wallet);
|
||||
updateList();
|
||||
|
||||
Reference in New Issue
Block a user