1
1
<template >
2
2
<div class =" group" >
3
+ <template v-if =" ! accountFactory " >
4
+ <button :disabled =" !isUsbSupported" @click =" () => connect(false)" >Connect over USB</button >
5
+ <button :disabled =" !isBleSupported" @click =" () => connect(true)" >Connect over BLE</button >
6
+ </template >
7
+ <template v-else >
8
+ <div >
9
+ <div >Device</div >
10
+ <div >{{ accountFactory.transport.deviceModel.productName }}</div >
11
+ </div >
12
+ <div v-if =" accounts.length" >
13
+ <div >Accounts</div >
14
+ <div >{{ accounts.map((account) => account.address.slice(0, 8)).join(', ') }}</div >
15
+ </div >
16
+ </template >
3
17
<div v-if =" status" >
4
18
<div >Connection status</div >
5
19
<div >{{ status }}</div >
6
20
</div >
7
- <button v-else-if =" !accountFactory" @click =" connect" >Connect</button >
8
- <template v-else >
21
+ <template v-else-if =" accountFactory " >
9
22
<button @click =" disconnect" >Disconnect</button >
10
23
<button @click =" () => addAccount(true)" >Add Account</button >
11
24
<button @click =" () => addAccount(false)" >Add Account no Confirm</button >
12
25
<button v-if =" accounts.length > 1" @click =" switchAccount" >Switch Account</button >
13
26
<button @click =" discoverAccounts" >Discover Accounts</button >
14
27
<button @click =" switchNode" >Switch Node</button >
15
- <div v-if =" accounts.length" >
16
- <div >Accounts</div >
17
- <div >{{ accounts.map((account) => account.address.slice(0, 8)).join(', ') }}</div >
18
- </div >
19
28
</template >
20
29
</div >
21
30
</template >
22
31
23
32
<script >
24
33
import { AccountLedgerFactory } from ' @aeternity/aepp-sdk' ;
25
34
import { mapState } from ' vuex' ;
26
- import TransportWebUSB from ' @ledgerhq/hw-transport-webusb' ;
35
+ import TransportWebUsb from ' @ledgerhq/hw-transport-webusb' ;
36
+ import TransportWebBle from ' @ledgerhq/hw-transport-web-ble' ;
37
+ import { listen } from ' @ledgerhq/logs' ;
38
+
39
+ // TODO: remove after fixing https://github.com/LedgerHQ/ledgerjs/issues/352#issuecomment-615917351
40
+ class TransportWebBleAndroidFix extends TransportWebBle {
41
+ static async open (device , ... args ) {
42
+ if (! navigator .userAgent .includes (' Mobi' )) return super .open (device, ... args);
43
+ const getPrimaryServicesOrig = device .gatt ? .getPrimaryServices ;
44
+ if (getPrimaryServicesOrig == null ) return super .open (device, ... args);
45
+ device .gatt .getPrimaryServices = async () => {
46
+ const [service ] = await getPrimaryServicesOrig .call (device .gatt );
47
+ const getCharacteristicOrig = service .getCharacteristic ;
48
+ service .getCharacteristic = async (id ) => {
49
+ const characteristic = await getCharacteristicOrig .call (service, id);
50
+ if (id === ' 13d63400-2c97-0004-0002-4c6564676572' ) {
51
+ const writeValueOrig = characteristic .writeValue ;
52
+ let delayed = false ;
53
+ characteristic .writeValue = async (data ) => {
54
+ if (! delayed) {
55
+ await new Promise ((resolve ) => setTimeout (resolve, 250 ));
56
+ delayed = true ;
57
+ }
58
+ return writeValueOrig .call (characteristic, data);
59
+ };
60
+ }
61
+ return characteristic;
62
+ };
63
+ return [service];
64
+ };
65
+ return super .open (device, ... args);
66
+ }
67
+ }
27
68
28
69
export default {
29
70
created () {
@@ -32,27 +73,53 @@ export default {
32
73
data : () => ({
33
74
status: ' ' ,
34
75
accounts: [],
76
+ isUsbSupported: false ,
77
+ isBleSupported: false ,
35
78
}),
36
79
computed: mapState ([' aeSdk' ]),
37
80
methods: {
38
- async connect () {
81
+ async connect (isBle ) {
82
+ let transport;
39
83
try {
40
84
this .status = ' Waiting for Ledger response' ;
41
- const transport = await TransportWebUSB .create ();
42
- this .accountFactory = new AccountLedgerFactory (transport);
85
+ transport = await (isBle ? TransportWebBleAndroidFix : TransportWebUsb).create ();
86
+ transport .on (' disconnect' , () => this .reset ());
87
+ const factory = new AccountLedgerFactory (transport);
88
+ await factory .ensureReady ();
89
+ this .accountFactory = factory;
90
+ this .status = ' ' ;
43
91
} catch (error) {
44
- if (error .name === ' TransportOpenUserCancelled' ) return ;
92
+ transport? .close ();
93
+ if (error .name === ' TransportOpenUserCancelled' ) {
94
+ this .status = ' ' ;
95
+ return ;
96
+ }
97
+ if (error .name === ' LockedDeviceError' ) {
98
+ this .status = ' Device is locked, please unlock it' ;
99
+ return ;
100
+ }
101
+ if (error .message .includes (' UNKNOWN_APDU' )) {
102
+ this .status = ' Ensure that aeternity app is opened on Ledger HW' ;
103
+ return ;
104
+ }
105
+ if (error .name === ' UnsupportedVersionError' ) {
106
+ this .status = error .message ;
107
+ return ;
108
+ }
109
+ this .status = ' Unknown error' ;
45
110
throw error;
46
- } finally {
47
- this .status = ' ' ;
48
111
}
49
112
},
50
- async disconnect () {
113
+ reset () {
51
114
this .accountFactory = null ;
52
115
this .accounts = [];
53
116
this .$store .commit (' setAddress' , undefined );
54
117
if (Object .keys (this .aeSdk .accounts ).length ) this .aeSdk .removeAccount (this .aeSdk .address );
55
118
},
119
+ async disconnect () {
120
+ await this .accountFactory .transport .close ();
121
+ this .reset ();
122
+ },
56
123
async addAccount (confirm ) {
57
124
try {
58
125
this .status = ' Waiting for Ledger response' ;
@@ -95,5 +162,16 @@ export default {
95
162
this .$store .commit (' setAddress' , account .address );
96
163
},
97
164
},
165
+ async mounted () {
166
+ this .isUsbSupported = await TransportWebUsb .isSupported ();
167
+ this .isBleSupported = await TransportWebBle .isSupported ();
168
+ this .unsubscribeLedgerLog = listen (({ type, id, date, message }) => {
169
+ console .log (type, id, date .toLocaleTimeString (), message);
170
+ });
171
+ },
172
+ async beforeUnmount () {
173
+ if (this .accountFactory ) this .disconnect ();
174
+ this .unsubscribeLedgerLog ();
175
+ },
98
176
};
99
177
< / script>
0 commit comments