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>
Konstantin Ullrich committed
Jul 16, 2026 at 20:39 UTC
ed540bae04e3f0c7e911de14a33402ae5d8b36ee
12 files changed
+420
-285
lib/di.dart
+7
-4
@@ -421,16 +421,19 @@ Future<void> setup({
421
case HardwareWalletType.seedsigner:
422
case HardwareWalletType.keystone:
423
throw Exception(
424
- "This should not have happened, because airgapped Wallets don't need View Models");
424
+ "This should not have happened, because airgapped Wallets don't need View Models",
425
+ );
426
}
427
});
428
428
- getIt.registerLazySingleton(() => LedgerViewModel());
429
+ getIt.registerLazySingleton(() => LedgerViewModel(getIt<AppStore>()));
430
430
- getIt.registerLazySingleton(() => BitboxViewModel());
431
+ getIt.registerLazySingleton(BitboxViewModel.new);
432
433
getIt.registerLazySingleton(
433
- () => TrezorConnect("cakewallet://trezor_connect", appName: "Cake Wallet"));
434
+ () => TrezorConnect("cakewallet://trezor_connect", appName: "Cake Wallet"),
435
+ );
436
+
437
getIt.registerLazySingleton(() => TrezorConnectViewModel(getIt<TrezorConnect>()));
438
439
getIt.registerFactory<KeyService>(() => KeyService(getIt.get<SecureStorage>()));
lib/monero/cw_monero.dart
+3
@@ -433,6 +433,9 @@ class CWMonero extends Monero {
433
@override
434
void setGlobalLedgerConnection(ledger.LedgerConnection connection) => gLedger = connection;
435
436
+ @override
437
+ bool hasGlobalLedgerConnection() => gLedger != null;
438
+
439
@override
440
String? getLastLedgerCommand() => latestLedgerCommand;
441
lib/reactions/on_authentication_state_change.dart
+28
-7
@@ -75,6 +75,23 @@ void startAuthenticationStateChange(
75
);
76
return;
77
}
78
+
79
+ ledgerVM.suppressMoneroReconnectPrompt = true;
80
+
81
+ if (!monero!.hasGlobalLedgerConnection()) {
82
+ await showPopUp<void>(
83
+ context: context,
84
+ builder: (context) => AlertWithOneAction(
85
+ alertTitle: "Unexpected Error",
86
+ alertContent:
87
+ "Unexpected Error while trying to connect to the device (has no global LedgerConnection)",
88
+ buttonText: S.of(context).try_again,
89
+ buttonAction: Navigator.of(context).pop,
90
+ ),
91
+ );
92
+ return;
93
+ }
94
+
95
showPopUp<void>(
96
context: context,
97
builder: (context) => AlertWithOneAction(
@@ -85,14 +102,17 @@ void startAuthenticationStateChange(
102
buttonAction: () => Navigator.of(context).pop(),
103
),
104
);
88
- bool tryOpening = true;
105
+
106
+ var isConnected = false;
107
+ var tryOpening = true;
108
while (tryOpening) {
109
try {
110
await loadCurrentWallet();
111
+ isConnected = true;
112
tryOpening = false;
113
} on Exception catch (e) {
114
final ledgerErrorMessage = ledgerVM.interpretErrorCode(e.toString());
95
- if (ledgerErrorMessage != null) {
115
+ if (ledgerErrorMessage != null && context.mounted) {
116
await showPopUp<void>(
117
context: context,
118
builder: (context) => AlertWithTwoActions(
@@ -116,16 +136,17 @@ void startAuthenticationStateChange(
136
}
137
138
getIt.get<BottomSheetService>().showNext();
119
- await navigatorKey.currentState!
120
- .pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
139
+ if (isConnected) {
140
+ ledgerVM.suppressMoneroReconnectPrompt = false;
141
+ await navigatorKey.currentState!
142
+ .pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
143
+ }
144
},
145
allowChangeWallet: true,
146
),
147
);
125
-
126
- // await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.connectDevices, (route) => false, arguments: ConnectDevicePageParams(walletType: walletType, onConnectDevice: onConnectDevice));
148
} else {
128
- navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
149
+ await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
150
}
151
WidgetsBinding.instance.addPostFrameCallback((_) {
152
final linkViewModel = getIt.get<LinkViewModel>();
lib/src/screens/connect_device/connect_device_page.dart
+206
-149
@@ -1,54 +1,54 @@
1
-import 'dart:async';
2
-import 'dart:io';
3
-
4
-import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
5
-import 'package:cake_wallet/generated/i18n.dart';
6
-import 'package:cake_wallet/main.dart';
7
-import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
8
-import 'package:cake_wallet/routes.dart';
9
-import 'package:cake_wallet/src/screens/base_page.dart';
10
-import 'package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart';
11
-import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
12
-import 'package:cake_wallet/src/widgets/primary_button.dart';
13
-import 'package:cake_wallet/themes/core/material_base_theme.dart';
14
-import 'package:cake_wallet/utils/responsive_layout_util.dart';
15
-import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
16
-import 'package:cw_core/utils/print_verbose.dart';
17
-import 'package:cw_core/wallet_info.dart';
18
-import 'package:cw_core/wallet_type.dart';
19
-import 'package:flutter/material.dart';
20
-import 'package:flutter_mobx/flutter_mobx.dart';
1
+import "dart:async";
2
+import "dart:io";
3
+
4
+import "package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
5
+import "package:cake_wallet/generated/i18n.dart";
6
+import "package:cake_wallet/main.dart";
7
+import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
8
+import "package:cake_wallet/routes.dart";
9
+import "package:cake_wallet/src/screens/base_page.dart";
10
+import "package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart";
11
+import "package:cake_wallet/src/widgets/cake_image_widget.dart";
12
+import "package:cake_wallet/src/widgets/primary_button.dart";
13
+import "package:cake_wallet/themes/core/material_base_theme.dart";
14
+import "package:cake_wallet/utils/responsive_layout_util.dart";
15
+import "package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart";
16
+import "package:cw_core/utils/print_verbose.dart";
17
+import "package:cw_core/wallet_info.dart";
18
+import "package:cw_core/wallet_type.dart";
19
+import "package:flutter/cupertino.dart";
20
+import "package:flutter/material.dart";
21
+import "package:flutter_mobx/flutter_mobx.dart";
22
23
typedef OnConnectDevice = void Function(BuildContext, HardwareWalletViewModel);
24
25
class ConnectDevicePageParams {
25
- final WalletType walletType;
26
- final OnConnectDevice onConnectDevice;
27
- final bool allowChangeWallet;
28
- final bool isReconnect;
29
- final HardwareWalletType hardwareWalletType;
30
-
31
- ConnectDevicePageParams({
26
+ const ConnectDevicePageParams({
27
required this.walletType,
28
required this.hardwareWalletType,
29
required this.onConnectDevice,
30
this.allowChangeWallet = false,
31
this.isReconnect = true,
32
});
38
-}
33
40
-class ConnectDevicePage extends BasePage {
34
final WalletType walletType;
35
final OnConnectDevice onConnectDevice;
36
final bool allowChangeWallet;
37
final bool isReconnect;
45
- final HardwareWalletViewModel hardwareWalletVM;
38
+ final HardwareWalletType hardwareWalletType;
39
+}
40
41
+class ConnectDevicePage extends BasePage {
42
ConnectDevicePage(ConnectDevicePageParams params, this.hardwareWalletVM)
43
: walletType = params.walletType,
44
onConnectDevice = params.onConnectDevice,
45
allowChangeWallet = params.allowChangeWallet,
46
isReconnect = params.isReconnect;
47
+ final WalletType walletType;
48
+ final OnConnectDevice onConnectDevice;
49
+ final bool allowChangeWallet;
50
+ final bool isReconnect;
51
+ final HardwareWalletViewModel hardwareWalletVM;
52
53
@override
54
String get title => isReconnect
@@ -60,31 +60,32 @@ class ConnectDevicePage extends BasePage {
60
61
@override
62
Widget body(BuildContext context) => PopScope(
63
- canPop: !isReconnect,
64
- child: ConnectDevicePageBody(
65
- walletType,
66
- onConnectDevice,
67
- allowChangeWallet,
68
- hardwareWalletVM,
69
- currentTheme,
70
- ));
63
+ canPop: !isReconnect,
64
+ child: ConnectDevicePageBody(
65
+ walletType,
66
+ onConnectDevice,
67
+ hardwareWalletVM,
68
+ currentTheme,
69
+ allowChangeWallet: allowChangeWallet,
70
+ ),
71
+ );
72
}
73
74
class ConnectDevicePageBody extends StatefulWidget {
75
+ const ConnectDevicePageBody(
76
+ this.walletType,
77
+ this.onConnectDevice,
78
+ this.hardwareWalletVM,
79
+ this.currentTheme, {
80
+ this.allowChangeWallet = false,
81
+ });
82
+
83
final WalletType walletType;
84
final OnConnectDevice onConnectDevice;
85
final bool allowChangeWallet;
86
final HardwareWalletViewModel hardwareWalletVM;
87
final MaterialThemeBase currentTheme;
88
80
- const ConnectDevicePageBody(
81
- this.walletType,
82
- this.onConnectDevice,
83
- this.allowChangeWallet,
84
- this.hardwareWalletVM,
85
- this.currentTheme,
86
- );
87
-
89
@override
90
ConnectDevicePageBodyState createState() => ConnectDevicePageBodyState();
91
}
@@ -95,10 +96,10 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
96
97
List<HardwareWalletDevice> get allDevices => [...bleDevices, ...usbDevices];
98
98
- late Timer? _usbRefreshTimer = null;
99
- late Timer? _bleRefreshTimer = null;
100
- late Timer? _bleStateTimer = null;
101
- late StreamSubscription<HardwareWalletDevice>? _bleRefresh = null;
99
+ Timer? _usbRefreshTimer;
100
+ Timer? _bleRefreshTimer;
101
+ Timer? _bleStateTimer;
102
+ StreamSubscription<HardwareWalletDevice>? _bleRefresh;
103
104
bool longWait = false;
105
Timer? _longWaitTimer;
@@ -107,19 +108,22 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
108
void initState() {
109
super.initState();
110
WidgetsBinding.instance.addPostFrameCallback((_) {
110
- _bleStateTimer =
111
- Timer.periodic(Duration(seconds: 1), (_) => widget.hardwareWalletVM.updateBleState());
111
+ _bleStateTimer = Timer.periodic(
112
+ const Duration(seconds: 1),
113
+ (_) => widget.hardwareWalletVM.updateBleState(),
114
+ );
115
113
- _bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices());
116
+ _bleRefreshTimer = Timer.periodic(const Duration(seconds: 1), (_) => _refreshBleDevices());
117
118
if (Platform.isAndroid) {
116
- _usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices());
119
+ _usbRefreshTimer = Timer.periodic(const Duration(seconds: 1), (_) => _refreshUsbDevices());
120
}
121
122
if (widget.hardwareWalletVM.hasBluetooth) {
120
- _longWaitTimer = Timer(Duration(seconds: 10), () {
121
- if (widget.hardwareWalletVM.isBleEnabled && bleDevices.isEmpty)
123
+ _longWaitTimer = Timer(const Duration(seconds: 10), () {
124
+ if (widget.hardwareWalletVM.isBleEnabled && bleDevices.isEmpty) {
125
setState(() => longWait = true);
126
+ }
127
});
128
}
129
});
@@ -134,22 +138,29 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
138
_longWaitTimer?.cancel();
139
140
widget.hardwareWalletVM.stopScanning();
141
+ _isConnectPressed = false;
142
super.dispose();
143
}
144
145
Future<void> _refreshUsbDevices() async {
146
final dev = await widget.hardwareWalletVM.getAllUsbDevices();
142
- if (usbDevices.length != dev.length) setState(() => usbDevices = dev);
147
+
148
+ if (usbDevices.length != dev.length) {
149
+ setState(() => usbDevices = dev);
150
+ }
151
}
152
153
Future<void> _refreshBleDevices() async {
154
try {
155
if (widget.hardwareWalletVM.isBleEnabled) {
148
- _bleRefresh = widget.hardwareWalletVM.scanForBleDevices().listen((device) => setState(() {
149
- bleDevices.add(device);
150
- if (longWait) longWait = false;
151
- }))
152
- ..onError((Object e) {
156
+ _bleRefresh = widget.hardwareWalletVM.scanForBleDevices().listen(
157
+ (device) => setState(() {
158
+ bleDevices.add(device);
159
+ if (longWait) {
160
+ longWait = false;
161
+ }
162
+ }),
163
+ )..onError((e) {
164
printV(e);
165
});
166
_bleRefreshTimer?.cancel();
@@ -160,48 +171,66 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
171
}
172
}
173
174
+ var _isConnectPressed = false;
175
+
176
Future<void> _connectToDevice(HardwareWalletDevice device) async {
164
- final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
177
+ if (_isConnectPressed) {
178
+ return;
179
+ }
180
166
- WidgetsBinding.instance.addPostFrameCallback((_) {
167
- if (isConnected)
168
- widget.onConnectDevice(navigatorKey.currentContext!, widget.hardwareWalletVM);
169
- });
181
+ _isConnectPressed = true;
182
+ try {
183
+ final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
184
+ _isConnectPressed = false;
185
+
186
+ WidgetsBinding.instance.addPostFrameCallback((_) {
187
+ if (isConnected) {
188
+ widget.onConnectDevice(navigatorKey.currentContext!, widget.hardwareWalletVM);
189
+ }
190
+ });
191
+ } catch (_) {
192
+ _isConnectPressed = false;
193
+ }
194
}
195
196
String _getDeviceTileLeading(HardwareWalletDeviceType deviceType) {
197
+ printV(deviceType);
198
switch (deviceType) {
199
case HardwareWalletDeviceType.ledgerNanoX:
175
- return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
200
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
201
case HardwareWalletDeviceType.ledgerNanoGen5:
177
- return 'assets/new-ui/hardware_wallets/device_ledger_nano_gen_5.svg';
202
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_gen_5.svg";
203
case HardwareWalletDeviceType.ledgerStax:
179
- return 'assets/new-ui/hardware_wallets/ledger_stax.svg';
204
+ return "assets/new-ui/hardware_wallets/device_ledger_stax.svg";
205
case HardwareWalletDeviceType.ledgerFlex:
181
- return 'assets/new-ui/hardware_wallets/ledger_flex.svg';
206
+ return "assets/new-ui/hardware_wallets/device_ledger_flex.svg";
207
case HardwareWalletDeviceType.BitBox02:
208
case HardwareWalletDeviceType.BitBox02Nova:
184
- return 'assets/new-ui/hardware_wallets/device_bitbox.svg';
209
+ return "assets/new-ui/hardware_wallets/device_bitbox.svg";
210
case HardwareWalletDeviceType.trezorModelOne:
186
- return 'assets/new-ui/hardware_wallets/device_trezor_model_one.svg';
211
+ return "assets/new-ui/hardware_wallets/device_trezor_model_one.svg";
212
case HardwareWalletDeviceType.trezorModelT:
188
- return 'assets/new-ui/hardware_wallets/device_trezor_model_t.svg';
213
+ return "assets/new-ui/hardware_wallets/device_trezor_model_t.svg";
214
case HardwareWalletDeviceType.trezorSafe3:
190
- return 'assets/new-ui/hardware_wallets/device_trezor_safe_3.svg';
215
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_3.svg";
216
case HardwareWalletDeviceType.trezorSafe5:
192
- return 'assets/new-ui/hardware_wallets/device_trezor_safe_5.svg';
217
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_5.svg";
218
case HardwareWalletDeviceType.trezorSafe7:
194
- return 'assets/new-ui/hardware_wallets/device_trezor_safe_7.svg';
219
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_7.svg";
220
221
default:
197
- return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
222
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
223
}
224
}
225
226
String get description {
202
- if (!Platform.isAndroid) return S.of(context).connect_your_hardware_wallet_ble;
227
+ if (!Platform.isAndroid) {
228
+ return S.of(context).connect_your_hardware_wallet_ble;
229
+ }
230
204
- if (widget.hardwareWalletVM.hasBluetooth) return S.of(context).connect_your_hardware_wallet;
231
+ if (widget.hardwareWalletVM.hasBluetooth) {
232
+ return S.of(context).connect_your_hardware_wallet;
233
+ }
234
235
return S.of(context).connect_your_hardware_wallet_usb;
236
}
@@ -210,7 +239,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
239
Widget build(BuildContext context) => Center(
240
child: Container(
241
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
213
- padding: EdgeInsets.symmetric(vertical: 24, horizontal: 24),
242
+ padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24),
243
child: Column(
244
children: [
245
Expanded(
@@ -218,9 +247,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
247
child: Column(
248
spacing: 24,
249
children: [
221
- CakeImageWidget(
222
- imageUrl: "assets/new-ui/hardware_wallet.svg",
223
- ),
250
+ const CakeImageWidget(imageUrl: "assets/new-ui/hardware_wallet.svg"),
251
Text(
252
description,
253
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
@@ -231,7 +258,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
258
offstage: widget.hardwareWalletVM.isBleEnabled ||
259
!widget.hardwareWalletVM.hasBluetooth,
260
child: Padding(
234
- padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
261
+ padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20),
262
child: Text(
263
S.of(context).ledger_please_enable_bluetooth,
264
style: Theme.of(context).textTheme.titleMedium,
@@ -242,67 +269,89 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
269
),
270
Container(
271
decoration: BoxDecoration(
245
- color: Theme.of(context).colorScheme.surfaceContainer,
246
- borderRadius: BorderRadius.circular(18)),
272
+ color: Theme.of(context).colorScheme.surfaceContainer,
273
+ borderRadius: BorderRadius.circular(18),
274
+ ),
275
child: ListView.separated(
248
- shrinkWrap: true,
249
- itemBuilder: (context, index) {
250
- final item = allDevices[index];
251
- return GestureDetector(
252
- onTap: () => _connectToDevice(item),
253
- behavior: HitTestBehavior.opaque,
254
- child: SizedBox(
255
- height: 48,
256
- child: Padding(
257
- padding: const EdgeInsets.symmetric(horizontal: 12.0),
258
- child: Row(
259
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
260
- children: [
261
- Row(
262
- spacing: 12,
263
- children: [
276
+ shrinkWrap: true,
277
+ padding: EdgeInsets.zero,
278
+ itemBuilder: (context, index) {
279
+ final item = allDevices[index];
280
+ return GestureDetector(
281
+ onTap: () => _connectToDevice(item),
282
+ behavior: HitTestBehavior.opaque,
283
+ child: SizedBox(
284
+ height: 48,
285
+ child: Padding(
286
+ padding: const EdgeInsets.symmetric(horizontal: 12),
287
+ child: Row(
288
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
289
+ children: [
290
+ Row(
291
+ spacing: 12,
292
+ children: [
293
+ Observer(
294
+ builder: (_) {
295
+ if (widget.hardwareWalletVM.isConnecting) {
296
+ return CupertinoActivityIndicator(
297
+ animating: true,
298
+ color: Theme.of(context)
299
+ .colorScheme
300
+ .onSurfaceVariant,
301
+ radius: 12,
302
+ );
303
+ }
304
+
305
+ return CakeImageWidget(
306
+ imageUrl: _getDeviceTileLeading(item.type),
307
+ width: 24,
308
+ height: 24,
309
+ colorFilter: ColorFilter.mode(
310
+ Theme.of(context).colorScheme.onSurfaceVariant,
311
+ BlendMode.srcIn,
312
+ ),
313
+ );
314
+ },
315
+ ),
316
+ Text(item.name),
317
+ ],
318
+ ),
319
+ Row(
320
+ spacing: 12,
321
+ children: [
322
+ if (item.connectionType ==
323
+ HardwareWalletConnectionType.ble)
324
CakeImageWidget(
265
- imageUrl: _getDeviceTileLeading(item.type),
325
+ imageUrl: "assets/new-ui/bluetooth.svg",
326
width: 24,
327
height: 24,
328
colorFilter: ColorFilter.mode(
269
- Theme.of(context).colorScheme.onSurfaceVariant,
270
- BlendMode.srcIn),
271
- ),
272
- Text(item.name)
273
- ],
274
- ),
275
- Row(
276
- spacing: 12,
277
- children: [
278
- if (item.connectionType ==
279
- HardwareWalletConnectionType.ble)
280
- CakeImageWidget(
281
- imageUrl: "assets/new-ui/bluetooth.svg",
282
- width: 24,
283
- height: 24,
284
- colorFilter: ColorFilter.mode(
285
- Theme.of(context).colorScheme.primary,
286
- BlendMode.srcIn),
329
+ Theme.of(context).colorScheme.primary,
330
+ BlendMode.srcIn,
331
),
288
- CakeImageWidget(
289
- imageUrl: "assets/new-ui/arrow_forward.svg",
290
- height: 16,
291
- colorFilter: ColorFilter.mode(
292
- Theme.of(context).colorScheme.onSurfaceVariant,
293
- BlendMode.srcIn))
294
- ],
295
- )
296
- ],
297
- ),
332
+ ),
333
+ CakeImageWidget(
334
+ imageUrl: "assets/new-ui/arrow_forward.svg",
335
+ height: 16,
336
+ colorFilter: ColorFilter.mode(
337
+ Theme.of(context).colorScheme.onSurfaceVariant,
338
+ BlendMode.srcIn,
339
+ ),
340
+ ),
341
+ ],
342
+ ),
343
+ ],
344
),
345
),
300
- );
301
- },
302
- separatorBuilder: (context, index) => Container(
303
- height: 1,
304
- color: Theme.of(context).colorScheme.surfaceContainerHigh),
305
- itemCount: allDevices.length),
346
+ ),
347
+ );
348
+ },
349
+ separatorBuilder: (context, index) => Container(
350
+ height: 1,
351
+ color: Theme.of(context).colorScheme.surfaceContainerHigh,
352
+ ),
353
+ itemCount: allDevices.length,
354
+ ),
355
),
356
if (widget.allowChangeWallet) ...[
357
PrimaryButton(
@@ -310,7 +359,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
359
color: Theme.of(context).colorScheme.primary,
360
textColor: Theme.of(context).colorScheme.onPrimary,
361
onPressed: _onChangeWallet,
313
- )
362
+ ),
363
],
364
],
365
),
@@ -321,7 +370,7 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
370
color: Theme.of(context).colorScheme.surfaceContainer,
371
textColor: Theme.of(context).colorScheme.primary,
372
onPressed: () => _onHowToConnect(context),
324
- )
373
+ ),
374
],
375
),
376
),
@@ -339,17 +388,25 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
388
showModalBottomSheet(
389
context: context,
390
isScrollControlled: true,
342
- builder: (BuildContext bottomSheetContext) => InfoStepsBottomSheet(
391
+ builder: (_) => InfoStepsBottomSheet(
392
titleText: S.of(context).how_to_connect,
393
steps: [
345
- InfoStep('assets/images/wallet_connect_step_icons/step1_power.svg',
346
- S.of(context).connect_hw_info_step_1),
347
- InfoStep('assets/images/wallet_connect_step_icons/step2_connect.svg',
348
- S.of(context).connect_hw_info_step_2),
349
- InfoStep('assets/images/wallet_connect_step_icons/step3_unlock.svg',
350
- S.of(context).connect_hw_info_step_3),
351
- InfoStep('assets/images/wallet_connect_step_icons/step4_select.svg',
352
- S.of(context).connect_hw_info_step_4),
394
+ InfoStep(
395
+ "assets/images/wallet_connect_step_icons/step1_power.svg",
396
+ S.of(context).connect_hw_info_step_1,
397
+ ),
398
+ InfoStep(
399
+ "assets/images/wallet_connect_step_icons/step2_connect.svg",
400
+ S.of(context).connect_hw_info_step_2,
401
+ ),
402
+ InfoStep(
403
+ "assets/images/wallet_connect_step_icons/step3_unlock.svg",
404
+ S.of(context).connect_hw_info_step_3,
405
+ ),
406
+ InfoStep(
407
+ "assets/images/wallet_connect_step_icons/step4_select.svg",
408
+ S.of(context).connect_hw_info_step_4,
409
+ ),
410
],
411
),
412
);
lib/store/app_store.dart
+26
-25
@@ -1,25 +1,25 @@
1
-import 'dart:async';
2
-
3
-import 'package:cake_wallet/core/amount_parsing_proxy.dart';
4
-import 'package:cake_wallet/di.dart';
5
-import 'package:cake_wallet/entities/preferences_key.dart';
6
-import 'package:cake_wallet/reactions/wallet_connect.dart';
7
-import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart';
8
-import 'package:cake_wallet/themes/core/theme_store.dart';
9
-import 'package:cake_wallet/utils/exception_handler.dart';
10
-import 'package:cw_core/transaction_info.dart';
11
-import 'package:cw_core/utils/print_verbose.dart';
12
-import 'package:cw_core/wallet_type.dart';
13
-import 'package:mobx/mobx.dart';
14
-import 'package:cw_core/balance.dart';
15
-import 'package:cw_core/wallet_base.dart';
16
-import 'package:cw_core/transaction_history.dart';
17
-import 'package:cake_wallet/store/wallet_list_store.dart';
18
-import 'package:cake_wallet/store/authentication_store.dart';
19
-import 'package:cake_wallet/store/settings_store.dart';
20
-import 'package:shared_preferences/shared_preferences.dart';
21
-
22
-part 'app_store.g.dart';
1
+import "dart:async";
2
+
3
+import "package:cake_wallet/core/amount_parsing_proxy.dart";
4
+import "package:cake_wallet/di.dart";
5
+import "package:cake_wallet/entities/preferences_key.dart";
6
+import "package:cake_wallet/reactions/wallet_connect.dart";
7
+import "package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart";
8
+import "package:cake_wallet/store/authentication_store.dart";
9
+import "package:cake_wallet/store/settings_store.dart";
10
+import "package:cake_wallet/store/wallet_list_store.dart";
11
+import "package:cake_wallet/themes/core/theme_store.dart";
12
+import "package:cake_wallet/utils/exception_handler.dart";
13
+import "package:cw_core/balance.dart";
14
+import "package:cw_core/transaction_history.dart";
15
+import "package:cw_core/transaction_info.dart";
16
+import "package:cw_core/utils/print_verbose.dart";
17
+import "package:cw_core/wallet_base.dart";
18
+import "package:cw_core/wallet_type.dart";
19
+import "package:mobx/mobx.dart";
20
+import "package:shared_preferences/shared_preferences.dart";
21
+
22
+part "app_store.g.dart";
23
24
class AppStore = AppStoreBase with _$AppStore;
25
@@ -59,8 +59,9 @@ abstract class AppStoreBase with Store {
59
@action
60
Future<void> changeCurrentWallet(
61
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) async {
62
- bool changingToSameWalletType = this.wallet?.type == wallet.type;
63
- this.wallet?.close(shouldCleanup: !changingToSameWalletType);
62
+ final changingToSameWalletType = this.wallet?.type == wallet.type;
63
+
64
+ await this.wallet?.close(shouldCleanup: !changingToSameWalletType);
65
this.wallet = wallet;
66
this.wallet!.setExceptionHandler(ExceptionHandler.onError);
67
@@ -80,7 +81,7 @@ abstract class AppStoreBase with Store {
81
wcService.create();
82
await wcService.init();
83
} catch (e, s) {
83
- printV('WalletConnect setup failed: $e\n$s');
84
+ printV("WalletConnect setup failed: $e\n$s");
85
}
86
}
87
}
lib/view_model/hardware_wallet/bitbox_view_model.dart
+9
-6
@@ -35,6 +35,10 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
35
@observable
36
bool isBleEnabled = false;
37
38
+ @override
39
+ @observable
40
+ bool isConnecting = false;
41
+
42
@override
43
bool get hasBluetooth => Platform.isIOS && false; // TODO: remove when we enable bluetooth
44
@@ -52,10 +56,11 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
56
Future<void> stopScanning() async {}
57
58
@override
59
+ @action
60
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
61
if (!(device is BitboxHardwareWalletDevice)) return false;
57
- if (_isConnecting) return false;
58
- _isConnecting = true;
62
+ if (isConnecting) return false;
63
+ isConnecting = true;
64
65
try {
66
final appDocDir = await getAppDir();
@@ -66,17 +71,15 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
71
printV("Bitbox initialized!", file: '${appDocDir.path}/error.txt');
72
await bitboxManager.channelHashVerify();
73
printV("Bitbox channel-hash verified!", file: '${appDocDir.path}/error.txt');
69
- _isConnecting = false;
74
+ isConnecting = false;
75
return true;
76
} catch (e) {
77
printV(e);
78
}
74
- _isConnecting = false;
79
+ isConnecting = false;
80
return false;
81
}
82
78
- bool _isConnecting = false;
79
-
83
@override
84
bool isConnected(WalletType type) => false;
85
lib/view_model/hardware_wallet/hardware_wallet_view_model.dart
+8
-5
@@ -1,13 +1,14 @@
1
-import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
2
-import 'package:cw_core/hardware/hardware_wallet_service.dart';
3
-import 'package:cw_core/wallet_base.dart';
4
-import 'package:cw_core/wallet_info.dart';
5
-import 'package:cw_core/wallet_type.dart';
1
+import "package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
2
+import "package:cw_core/hardware/hardware_wallet_service.dart";
3
+import "package:cw_core/wallet_base.dart";
4
+import "package:cw_core/wallet_info.dart";
5
+import "package:cw_core/wallet_type.dart";
6
7
abstract class HardwareWalletViewModel {
8
HardwareWalletType get hardwareWalletType;
9
bool get isBleEnabled;
10
bool get hasBluetooth;
11
+ bool get isConnecting;
12
13
bool isConnected(WalletType type);
14
@@ -26,4 +27,6 @@ abstract class HardwareWalletViewModel {
27
Future<void> initWallet(WalletBase wallet);
28
29
String? interpretErrorCode(String error) => null;
30
+
31
+ Future<void> close() async {}
32
}
lib/view_model/hardware_wallet/ledger_view_model.dart
+119
-73
@@ -1,50 +1,47 @@
1
-import 'dart:async';
2
-import 'dart:io';
3
-
4
-import 'package:cake_wallet/bitcoin/bitcoin.dart';
5
-import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
6
-import 'package:cake_wallet/evm/evm.dart';
7
-import 'package:cake_wallet/generated/i18n.dart';
8
-import 'package:cake_wallet/main.dart';
9
-import 'package:cake_wallet/monero/monero.dart';
10
-import 'package:cake_wallet/routes.dart';
11
-import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
12
-import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
13
-import 'package:cake_wallet/wallet_type_utils.dart';
14
-import 'package:cw_core/hardware/device_connection_type.dart';
15
-import 'package:cw_core/hardware/hardware_wallet_service.dart';
16
-import 'package:cw_core/utils/print_verbose.dart';
17
-import 'package:cw_core/wallet_base.dart';
18
-import 'package:cw_core/wallet_info.dart';
19
-import 'package:cw_core/wallet_type.dart';
20
-import 'package:flutter/widgets.dart';
21
-import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as sdk;
22
-import 'package:mobx/mobx.dart';
23
-import 'package:permission_handler/permission_handler.dart';
24
-
25
-part 'ledger_view_model.g.dart';
1
+import "dart:async";
2
+import "dart:io";
3
+
4
+import "package:cake_wallet/bitcoin/bitcoin.dart";
5
+import "package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
6
+import "package:cake_wallet/evm/evm.dart";
7
+import "package:cake_wallet/generated/i18n.dart";
8
+import "package:cake_wallet/main.dart";
9
+import "package:cake_wallet/monero/monero.dart";
10
+import "package:cake_wallet/routes.dart";
11
+import "package:cake_wallet/src/screens/connect_device/connect_device_page.dart";
12
+import "package:cake_wallet/store/app_store.dart";
13
+import "package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart";
14
+import "package:cake_wallet/wallet_type_utils.dart";
15
+import "package:cw_core/hardware/device_connection_type.dart";
16
+import "package:cw_core/hardware/hardware_wallet_service.dart";
17
+import "package:cw_core/utils/print_verbose.dart";
18
+import "package:cw_core/wallet_base.dart";
19
+import "package:cw_core/wallet_info.dart";
20
+import "package:cw_core/wallet_type.dart";
21
+import "package:flutter/widgets.dart";
22
+import "package:ledger_flutter_plus/ledger_flutter_plus.dart" as sdk;
23
+import "package:mobx/mobx.dart";
24
+import "package:permission_handler/permission_handler.dart";
25
+
26
+part "ledger_view_model.g.dart";
27
28
class LedgerViewModel = LedgerViewModelBase with _$LedgerViewModel;
29
30
abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
30
- late final sdk.LedgerInterface ledgerPlusBLE;
31
- late final sdk.LedgerInterface ledgerPlusUSB;
32
-
33
- bool get _doesSupportHardwareWallets {
34
- if (isMoneroOnly) {
35
- return DeviceConnectionType.supportedConnectionTypes(
36
- WalletType.monero, HardwareWalletType.ledger, Platform.isIOS)
37
- .isNotEmpty;
38
- }
39
-
40
- return true;
41
- }
42
-
43
- LedgerViewModelBase() {
31
+ LedgerViewModelBase(AppStore appStore) {
32
if (_doesSupportHardwareWallets) {
33
reaction((_) => isBleEnabled, (_) {
46
- if (isBleEnabled) _initBLE();
34
+ if (isBleEnabled) {
35
+ _initBLE();
36
+ }
37
});
38
+
39
+ reaction((_) => appStore.wallet, (wallet) async {
40
+ if (wallet != null && wallet.type != _connectingWalletType) {
41
+ await close();
42
+ }
43
+ });
44
+
45
updateBleState();
46
47
if (!Platform.isIOS) {
@@ -53,6 +50,21 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
50
}
51
}
52
53
+ late final sdk.LedgerInterface ledgerPlusBLE;
54
+ late final sdk.LedgerInterface ledgerPlusUSB;
55
+
56
+ bool get _doesSupportHardwareWallets {
57
+ if (isMoneroOnly) {
58
+ return DeviceConnectionType.supportedConnectionTypes(
59
+ WalletType.monero,
60
+ HardwareWalletType.ledger,
61
+ Platform.isIOS,
62
+ ).isNotEmpty;
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
@override
69
HardwareWalletType get hardwareWalletType => HardwareWalletType.ledger;
70
@@ -60,6 +72,10 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
72
@observable
73
bool isBleEnabled = false;
74
75
+ @override
76
+ @observable
77
+ bool isConnecting = false;
78
+
79
@override
80
bool get hasBluetooth => true;
81
@@ -69,9 +85,11 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
85
if (isBleEnabled && !_bleIsInitialized) {
86
ledgerPlusBLE = sdk.LedgerInterface.ble(
87
onPermissionRequest: (_) async {
72
- if (Platform.isMacOS) return true;
88
+ if (Platform.isMacOS) {
89
+ return true;
90
+ }
91
74
- Map<Permission, PermissionStatus> statuses = await [
92
+ final statuses = await [
93
Permission.bluetoothScan,
94
Permission.bluetoothConnect,
95
Permission.bluetoothAdvertise,
@@ -79,7 +97,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
97
98
return statuses.values.where((status) => status.isDenied).isEmpty;
99
},
82
- bleOptions: sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)),
100
+ bleOptions: sdk.BluetoothOptions(maxScanDuration: const Duration(minutes: 5)),
101
);
102
_bleIsInitialized = true;
103
}
@@ -91,28 +109,41 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
109
110
final newState = bleState == sdk.AvailabilityState.poweredOn;
111
94
- if (newState != isBleEnabled) isBleEnabled = newState;
112
+ if (newState != isBleEnabled) {
113
+ isBleEnabled = newState;
114
+ }
115
}
116
117
@override
118
Stream<HardwareWalletDevice> scanForBleDevices() =>
99
- ledgerPlusBLE.scan().map((d) => LedgerHardwareWalletDevice(d));
119
+ ledgerPlusBLE.scan().map(LedgerHardwareWalletDevice.new);
120
121
@override
102
- Future<List<HardwareWalletDevice>> getAllUsbDevices() => ledgerPlusUSB.devices
103
- .then((devices) => devices.map((d) => LedgerHardwareWalletDevice(d)).toList());
122
+ Future<List<HardwareWalletDevice>> getAllUsbDevices() =>
123
+ ledgerPlusUSB.devices.then((devices) => devices.map(LedgerHardwareWalletDevice.new).toList());
124
125
@override
126
Future<void> stopScanning() async {
107
- if (_bleIsInitialized) await ledgerPlusBLE.stopScanning();
108
- if (!Platform.isIOS) await ledgerPlusUSB.stopScanning();
127
+ if (_bleIsInitialized) {
128
+ await ledgerPlusBLE.stopScanning();
129
+ }
130
+ if (!Platform.isIOS) {
131
+ await ledgerPlusUSB.stopScanning();
132
+ }
133
}
134
135
@override
136
+ @action
137
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
113
- if (!(device is LedgerHardwareWalletDevice)) return false;
114
- if (_isConnecting) return false;
115
- _isConnecting = true;
138
+ if (device is! LedgerHardwareWalletDevice) {
139
+ return false;
140
+ }
141
+
142
+ if (isConnecting) {
143
+ return false;
144
+ }
145
+
146
+ isConnecting = true;
147
_connectingWalletType = type;
148
if (isConnected(type)) {
149
try {
@@ -123,34 +154,36 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
154
final ledger =
155
device.connectionType == HardwareWalletConnectionType.ble ? ledgerPlusBLE : ledgerPlusUSB;
156
126
- if (_connectionChangeSubscription == null) {
127
- _connectionChangeSubscription =
128
- ledger.deviceStateChanges(device.device.id).listen(_connectionChangeListener);
129
- }
157
+ _connectionChangeSubscription ??=
158
+ ledger.deviceStateChanges(device.device.id).listen(_connectionChangeListener);
159
160
try {
161
_connection = await ledger.connect(device.device);
133
- _isConnecting = false;
162
+ isConnecting = false;
163
return true;
164
} catch (e) {
165
printV(e);
166
}
138
- _isConnecting = false;
167
+ isConnecting = false;
168
return false;
169
}
170
171
StreamSubscription<sdk.BleConnectionState>? _connectionChangeSubscription;
172
sdk.LedgerConnection? _connection;
144
- bool _isConnecting = false;
173
WalletType? _connectingWalletType;
174
+ bool suppressMoneroReconnectPrompt = false;
175
176
void _connectionChangeListener(sdk.BleConnectionState event) {
148
- printV('Ledger Device State Changed: $event');
149
- if (event == sdk.BleConnectionState.disconnected && !_isConnecting) {
177
+ printV("Ledger Device State Changed: $event");
178
+ if (event == sdk.BleConnectionState.disconnected && !isConnecting) {
179
_connection = null;
180
if (_connectingWalletType == WalletType.monero) {
181
monero!.resetLedgerConnection();
182
183
+ if (suppressMoneroReconnectPrompt) {
184
+ return;
185
+ }
186
+
187
Navigator.of(navigatorKey.currentContext!).pushNamed(
188
Routes.connectDevices,
189
arguments: ConnectDevicePageParams(
@@ -158,7 +191,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
191
hardwareWalletType: HardwareWalletType.ledger,
192
allowChangeWallet: true,
193
isReconnect: true,
161
- onConnectDevice: (context, ledgerVM) async {
194
+ onConnectDevice: (context, ledgerVM) {
195
if (context.mounted && Navigator.of(context).canPop()) {
196
Navigator.of(context).pop();
197
}
@@ -170,26 +203,24 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
203
}
204
205
@override
173
- bool isConnected(WalletType type) => _connection != null && !(_connection!.isDisconnected);
206
+ bool isConnected(WalletType type) => _connection != null && !_connection!.isDisconnected;
207
208
sdk.LedgerConnection get connection => _connection!;
209
210
@override
178
- Future<void> initWallet(WalletBase wallet) async {
211
+ Future<void> initWallet(WalletBase wallet) {
212
switch (wallet.type) {
213
case WalletType.monero:
214
return monero!.setLedgerConnection(wallet, connection);
215
case WalletType.bitcoin:
183
- return bitcoin!
184
- .setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
216
+ return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
217
case WalletType.litecoin:
186
- return bitcoin!
187
- .setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
218
+ return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
219
case WalletType.ethereum:
220
case WalletType.polygon:
190
- return evm!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
221
+ return evm!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
222
default:
192
- throw Exception('Unexpected wallet type: ${wallet.type} for ledger');
223
+ throw Exception("Unexpected wallet type: ${wallet.type} for ledger");
224
}
225
}
226
@@ -216,16 +247,31 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
247
return error;
248
}
249
219
- var errorRegex = RegExp(r'(?:0x\S*?|[0-9a-f]{4})(?= )').firstMatch(error.toString());
250
+ final errorRegex = RegExp(r"(?:0x\S*?|[0-9a-f]{4})(?= )").firstMatch(error.toString());
251
221
- String errorCode = errorRegex?.group(0).toString().replaceAll("0x", "") ?? "";
252
+ final errorCode = errorRegex?.group(0).toString().replaceAll("0x", "") ?? "";
253
if (errorCode.contains("6985")) {
254
return S.current.ledger_error_tx_rejected_by_user;
255
} else if (errorCode.contains("5515")) {
256
return S.current.ledger_error_device_locked;
226
- } else if (["6e01", "6a87", "6d02", "6511", "6e00"].any((e) => errorCode.contains(e))) {
257
+ } else if (["6e01", "6a87", "6d02", "6511", "6e00"].any(errorCode.contains)) {
258
return S.current.ledger_error_wrong_app;
259
}
260
return null;
261
}
262
+
263
+ @override
264
+ Future<void> close() async {
265
+ try {
266
+ printV("Resetting ledger VM");
267
+ await _connectionChangeSubscription?.cancel();
268
+ await _connection?.disconnect().catchError((_) {});
269
+
270
+ _connectingWalletType = null;
271
+ _connectionChangeSubscription = null;
272
+ _connection = null;
273
+ isConnecting = false;
274
+ await stopScanning();
275
+ } catch (_) {}
276
+ }
277
}
lib/view_model/hardware_wallet/trezor_connect_view_model.dart
+8
-4
@@ -82,6 +82,10 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
82
@override
83
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
84
85
+ @override
86
+ @observable
87
+ bool isConnecting = false;
88
+
89
@override
90
@observable
91
bool isBleEnabled = false;
@@ -112,7 +116,6 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
116
if (!Platform.isIOS) await trezorUSB.stopScanning();
117
}
118
115
- bool _isConnecting = false;
119
sdk.TrezorClient? _client;
120
121
Completer<String?>? _pinCompleter;
@@ -123,11 +126,12 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
126
void setParingPin(String pin) => _pinCompleter?.complete(pin);
127
128
@override
129
+ @action
130
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type,
131
[bool isRetry = false]) async {
132
if (!(device is TrezorHardwareWalletDevice)) return false;
129
- if (_isConnecting) return false;
130
- _isConnecting = true;
133
+ if (isConnecting) return false;
134
+ isConnecting = true;
135
paringState = TrezorParingState.initial;
136
137
try {
@@ -182,7 +186,7 @@ abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with S
186
printV(e);
187
return false;
188
} finally {
185
- _isConnecting = false;
189
+ isConnecting = false;
190
}
191
}
192
lib/view_model/wallet_list/wallet_list_view_model.dart
+1
-2
@@ -77,8 +77,7 @@ abstract class WalletListViewModelBase with Store {
77
if (walletItem.type == WalletType.haven) {
78
return;
79
}
80
- // bool switchingToSameWalletType = walletItem.type == _appStore.wallet?.type;
81
- // await _appStore.wallet?.close(shouldCleanup: !switchingToSameWalletType);
80
+
81
final wallet = await _walletLoadingService.load(walletItem.type, walletItem.name);
82
await _appStore.changeCurrentWallet(wallet);
83
updateList();
pubspec_base.yaml
+4
-10
@@ -125,7 +125,7 @@ dependencies:
125
ledger_flutter_plus:
126
git:
127
url: https://github.com/vespr-wallet/ledger-flutter-plus
128
- ref: 0ccfbe26298e8974dfd4e891f34719c8348562f9
128
+ ref: eaf49764ad0863e8dec97ad30ff6aea0a1d1bd28
129
trezor_connect:
130
git:
131
url: https://github.com/cake-tech/trezor_connect
@@ -201,14 +201,8 @@ dependency_overrides:
201
ffi: 2.1.0
202
ledger_flutter_plus:
203
git:
204
- url: https://github.com/cake-tech/ledger-flutter-plus
205
- ref: 7bcb00380783f8611f68f5cd63fec96261fea2c2
206
- ledger_usb_plus:
207
- git:
208
- # url: https://github.com/konstantinullrich/ledger-usb-plus
209
- # ref: dad482c847c0d0eedceabbb40d3478e64b65e630
210
- url: https://github.com/cake-tech/ledger-usb-plus
211
- ref: 734c3d6f8267ba52ad73b6e1e78e570c07298890
204
+ url: https://github.com/vespr-wallet/ledger-flutter-plus
205
+ ref: eaf49764ad0863e8dec97ad30ff6aea0a1d1bd28
206
socks5_proxy:
207
git:
208
url: https://github.com/LacticWhale/socks_dart.git
@@ -231,7 +225,7 @@ dependency_overrides:
225
universal_ble:
226
git:
227
url: https://github.com/cake-tech/universal_ble.git
234
- ref: f7044558ad111a156987614971e08c583c80175c
228
+ ref: 6735c1bdf7f42c501d51c20564eb957c3a12b981
229
230
flutter_icons:
231
image_path: "assets/images/app_logo.png"
tool/configure.dart
+1
@@ -475,6 +475,7 @@ WalletCredentials createMoneroNewWalletCredentials({required String name, requir
475
Future<void> setLedgerConnection(Object wallet, ledger.LedgerConnection connection);
476
void resetLedgerConnection();
477
void setGlobalLedgerConnection(ledger.LedgerConnection connection);
478
+ bool hasGlobalLedgerConnection();
479
String? getLastLedgerCommand();
480
void setHardwareWalletService(Object wallet, HardwareWalletService service);
481
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);