@ledgerhq/live-common 26.0.0 → 26.1.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +1 -1
  2. package/lib/__tests__/hw/getDeviceInfo.js +152 -0
  3. package/lib/__tests__/hw/getDeviceInfo.js.map +1 -1
  4. package/lib/api/Manager.d.ts +2 -1
  5. package/lib/api/Manager.d.ts.map +1 -1
  6. package/lib/api/Manager.js +37 -2
  7. package/lib/api/Manager.js.map +1 -1
  8. package/lib/ble/hooks/useBleDevicePairing.d.ts +18 -0
  9. package/lib/ble/hooks/useBleDevicePairing.d.ts.map +1 -0
  10. package/lib/ble/hooks/useBleDevicePairing.js +57 -0
  11. package/lib/ble/hooks/useBleDevicePairing.js.map +1 -0
  12. package/lib/ble/hooks/useBleDevicePairing.test.d.ts +2 -0
  13. package/lib/ble/hooks/useBleDevicePairing.test.d.ts.map +1 -0
  14. package/lib/ble/hooks/useBleDevicePairing.test.js +159 -0
  15. package/lib/ble/hooks/useBleDevicePairing.test.js.map +1 -0
  16. package/lib/ble/hooks/useBleDevicesScanning.d.ts +25 -0
  17. package/lib/ble/hooks/useBleDevicesScanning.d.ts.map +1 -0
  18. package/lib/ble/hooks/useBleDevicesScanning.js +117 -0
  19. package/lib/ble/hooks/useBleDevicesScanning.js.map +1 -0
  20. package/lib/ble/hooks/useBleDevicesScanning.test.d.ts +2 -0
  21. package/lib/ble/hooks/useBleDevicesScanning.test.d.ts.map +1 -0
  22. package/lib/ble/hooks/useBleDevicesScanning.test.js +281 -0
  23. package/lib/ble/hooks/useBleDevicesScanning.test.js.map +1 -0
  24. package/lib/ble/types.d.ts +21 -0
  25. package/lib/ble/types.d.ts.map +1 -0
  26. package/lib/ble/types.js +3 -0
  27. package/lib/ble/types.js.map +1 -0
  28. package/lib/errors.d.ts +2 -0
  29. package/lib/errors.d.ts.map +1 -1
  30. package/lib/errors.js +4 -2
  31. package/lib/errors.js.map +1 -1
  32. package/lib/families/cardano/js-estimateMaxSpendable.d.ts.map +1 -1
  33. package/lib/families/cardano/js-estimateMaxSpendable.js +2 -0
  34. package/lib/families/cardano/js-estimateMaxSpendable.js.map +1 -1
  35. package/lib/families/cosmos/specs.js +2 -2
  36. package/lib/families/cosmos/specs.js.map +1 -1
  37. package/lib/families/elrond/specs.js +1 -1
  38. package/lib/families/elrond/specs.js.map +1 -1
  39. package/lib/families/polkadot/bridge.integration.test.js +3 -3
  40. package/lib/families/polkadot/bridge.integration.test.js.map +1 -1
  41. package/lib/families/polkadot/specs.d.ts.map +1 -1
  42. package/lib/families/polkadot/specs.js +4 -3
  43. package/lib/families/polkadot/specs.js.map +1 -1
  44. package/lib/featureFlags/provider.d.ts +5 -1
  45. package/lib/featureFlags/provider.d.ts.map +1 -1
  46. package/lib/featureFlags/provider.js +6 -4
  47. package/lib/featureFlags/provider.js.map +1 -1
  48. package/lib/featureFlags/useFeature.d.ts.map +1 -1
  49. package/lib/featureFlags/useFeature.js +3 -1
  50. package/lib/featureFlags/useFeature.js.map +1 -1
  51. package/lib/generated/account.d.ts.map +1 -1
  52. package/lib/generated/account.js.map +1 -1
  53. package/lib/generated/bridge/js.d.ts.map +1 -1
  54. package/lib/generated/bridge/js.js.map +1 -1
  55. package/lib/generated/bridge/mock.d.ts.map +1 -1
  56. package/lib/generated/bridge/mock.js.map +1 -1
  57. package/lib/generated/cli-transaction.d.ts.map +1 -1
  58. package/lib/generated/cli-transaction.js.map +1 -1
  59. package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
  60. package/lib/generated/deviceTransactionConfig.js.map +1 -1
  61. package/lib/generated/exchange.d.ts.map +1 -1
  62. package/lib/generated/exchange.js.map +1 -1
  63. package/lib/generated/hw-getAddress.d.ts.map +1 -1
  64. package/lib/generated/hw-getAddress.js.map +1 -1
  65. package/lib/generated/hw-signMessage.d.ts.map +1 -1
  66. package/lib/generated/hw-signMessage.js.map +1 -1
  67. package/lib/generated/mock.d.ts.map +1 -1
  68. package/lib/generated/mock.js.map +1 -1
  69. package/lib/generated/platformAdapter.d.ts.map +1 -1
  70. package/lib/generated/platformAdapter.js.map +1 -1
  71. package/lib/generated/presync.d.ts.map +1 -1
  72. package/lib/generated/presync.js.map +1 -1
  73. package/lib/generated/specs.d.ts.map +1 -1
  74. package/lib/generated/specs.js.map +1 -1
  75. package/lib/generated/speculos-deviceActions.d.ts.map +1 -1
  76. package/lib/generated/speculos-deviceActions.js.map +1 -1
  77. package/lib/generated/transaction.d.ts.map +1 -1
  78. package/lib/generated/transaction.js.map +1 -1
  79. package/lib/generated/types.d.ts.map +1 -1
  80. package/lib/hw/actions/installLanguage.d.ts +21 -0
  81. package/lib/hw/actions/installLanguage.d.ts.map +1 -0
  82. package/lib/hw/actions/installLanguage.js +260 -0
  83. package/lib/hw/actions/installLanguage.js.map +1 -0
  84. package/lib/hw/attemptToQuitApp.d.ts +10 -0
  85. package/lib/hw/attemptToQuitApp.d.ts.map +1 -0
  86. package/lib/hw/attemptToQuitApp.js +22 -0
  87. package/lib/hw/attemptToQuitApp.js.map +1 -0
  88. package/lib/hw/connectManager.d.ts +2 -5
  89. package/lib/hw/connectManager.d.ts.map +1 -1
  90. package/lib/hw/connectManager.js +2 -14
  91. package/lib/hw/connectManager.js.map +1 -1
  92. package/lib/hw/getDeviceInfo.d.ts.map +1 -1
  93. package/lib/hw/getDeviceInfo.js +6 -3
  94. package/lib/hw/getDeviceInfo.js.map +1 -1
  95. package/lib/hw/getVersion.d.ts +3 -3
  96. package/lib/hw/getVersion.d.ts.map +1 -1
  97. package/lib/hw/getVersion.js +53 -5
  98. package/lib/hw/getVersion.js.map +1 -1
  99. package/lib/hw/installLanguage.d.ts +17 -0
  100. package/lib/hw/installLanguage.d.ts.map +1 -0
  101. package/lib/hw/installLanguage.js +173 -0
  102. package/lib/hw/installLanguage.js.map +1 -0
  103. package/lib/hw/uninstallLanguage.d.ts +15 -0
  104. package/lib/hw/uninstallLanguage.d.ts.map +1 -0
  105. package/lib/hw/uninstallLanguage.js +121 -0
  106. package/lib/hw/uninstallLanguage.js.map +1 -0
  107. package/lib/manager/hooks.d.ts +2 -1
  108. package/lib/manager/hooks.d.ts.map +1 -1
  109. package/lib/manager/hooks.js +13 -1
  110. package/lib/manager/hooks.js.map +1 -1
  111. package/lib/manager/index.d.ts +2 -1
  112. package/lib/manager/index.d.ts.map +1 -1
  113. package/lib/manager/index.js +16 -0
  114. package/lib/manager/index.js.map +1 -1
  115. package/lib/manager/localization.d.ts +3 -0
  116. package/lib/manager/localization.d.ts.map +1 -0
  117. package/lib/manager/localization.js +15 -0
  118. package/lib/manager/localization.js.map +1 -0
  119. package/lib/platform/react.d.ts.map +1 -1
  120. package/lib/platform/react.js +6 -3
  121. package/lib/platform/react.js.map +1 -1
  122. package/package.json +18 -18
  123. package/src/__tests__/hw/getDeviceInfo.ts +137 -0
  124. package/src/api/Manager.ts +50 -2
  125. package/src/ble/hooks/useBleDevicePairing.test.ts +91 -0
  126. package/src/ble/hooks/useBleDevicePairing.ts +51 -0
  127. package/src/ble/hooks/useBleDevicesScanning.test.ts +252 -0
  128. package/src/ble/hooks/useBleDevicesScanning.ts +147 -0
  129. package/src/ble/types.ts +42 -0
  130. package/src/errors.ts +9 -0
  131. package/src/families/cardano/js-estimateMaxSpendable.ts +2 -0
  132. package/src/families/cosmos/specs.ts +2 -2
  133. package/src/families/elrond/specs.ts +1 -1
  134. package/src/families/polkadot/bridge.integration.test.ts +3 -3
  135. package/src/families/polkadot/specs.ts +4 -3
  136. package/src/featureFlags/provider.tsx +11 -1
  137. package/src/featureFlags/useFeature.ts +6 -2
  138. package/src/generated/account.ts +0 -8
  139. package/src/generated/bridge/js.ts +0 -18
  140. package/src/generated/bridge/mock.ts +0 -8
  141. package/src/generated/cli-transaction.ts +0 -17
  142. package/src/generated/deviceTransactionConfig.ts +15 -31
  143. package/src/generated/exchange.ts +0 -5
  144. package/src/generated/hw-getAddress.ts +0 -18
  145. package/src/generated/hw-signMessage.ts +0 -3
  146. package/src/generated/mock.ts +0 -3
  147. package/src/generated/platformAdapter.ts +0 -4
  148. package/src/generated/presync.ts +0 -1
  149. package/src/generated/specs.ts +0 -17
  150. package/src/generated/speculos-deviceActions.ts +0 -17
  151. package/src/generated/transaction.ts +0 -18
  152. package/src/generated/types.ts +7 -4
  153. package/src/hw/actions/installLanguage.ts +343 -0
  154. package/src/hw/attemptToQuitApp.ts +32 -0
  155. package/src/hw/connectManager.ts +5 -26
  156. package/src/hw/getDeviceInfo.ts +6 -0
  157. package/src/hw/getVersion.ts +77 -3
  158. package/src/hw/installLanguage.ts +172 -0
  159. package/src/hw/uninstallLanguage.ts +127 -0
  160. package/src/manager/hooks.ts +21 -1
  161. package/src/manager/index.ts +16 -0
  162. package/src/manager/localization.ts +19 -0
  163. package/src/platform/react.ts +7 -3
@@ -1,38 +1,21 @@
1
1
  import algorand from "../families/algorand/specs";
2
-
3
2
  import bitcoin from "../families/bitcoin/specs";
4
-
5
3
  import cardano from "../families/cardano/specs";
6
-
7
4
  import celo from "../families/celo/specs";
8
-
9
5
  import cosmos from "../families/cosmos/specs";
10
-
11
6
  import crypto_org from "../families/crypto_org/specs";
12
-
13
7
  import elrond from "../families/elrond/specs";
14
-
15
8
  import ethereum from "../families/ethereum/specs";
16
-
17
9
  import filecoin from "../families/filecoin/specs";
18
-
19
10
  import hedera from "../families/hedera/specs";
20
-
21
11
  import osmosis from "../families/osmosis/specs";
22
-
23
12
  import polkadot from "../families/polkadot/specs";
24
-
25
13
  import ripple from "../families/ripple/specs";
26
-
27
14
  import solana from "../families/solana/specs";
28
-
29
15
  import stellar from "../families/stellar/specs";
30
-
31
16
  import tezos from "../families/tezos/specs";
32
-
33
17
  import tron from "../families/tron/specs";
34
18
 
35
-
36
19
  export default {
37
20
  algorand,
38
21
  bitcoin,
@@ -1,38 +1,21 @@
1
1
  import algorand from "../families/algorand/speculos-deviceActions";
2
-
3
2
  import bitcoin from "../families/bitcoin/speculos-deviceActions";
4
-
5
3
  import cardano from "../families/cardano/speculos-deviceActions";
6
-
7
4
  import celo from "../families/celo/speculos-deviceActions";
8
-
9
5
  import cosmos from "../families/cosmos/speculos-deviceActions";
10
-
11
6
  import crypto_org from "../families/crypto_org/speculos-deviceActions";
12
-
13
7
  import elrond from "../families/elrond/speculos-deviceActions";
14
-
15
8
  import ethereum from "../families/ethereum/speculos-deviceActions";
16
-
17
9
  import filecoin from "../families/filecoin/speculos-deviceActions";
18
-
19
10
  import hedera from "../families/hedera/speculos-deviceActions";
20
-
21
11
  import osmosis from "../families/osmosis/speculos-deviceActions";
22
-
23
12
  import polkadot from "../families/polkadot/speculos-deviceActions";
24
-
25
13
  import ripple from "../families/ripple/speculos-deviceActions";
26
-
27
14
  import solana from "../families/solana/speculos-deviceActions";
28
-
29
15
  import stellar from "../families/stellar/speculos-deviceActions";
30
-
31
16
  import tezos from "../families/tezos/speculos-deviceActions";
32
-
33
17
  import tron from "../families/tron/speculos-deviceActions";
34
18
 
35
-
36
19
  export default {
37
20
  algorand,
38
21
  bitcoin,
@@ -1,40 +1,22 @@
1
1
  import algorand from "../families/algorand/transaction";
2
-
3
2
  import bitcoin from "../families/bitcoin/transaction";
4
-
5
3
  import cardano from "../families/cardano/transaction";
6
-
7
4
  import celo from "../families/celo/transaction";
8
-
9
5
  import cosmos from "../families/cosmos/transaction";
10
-
11
6
  import crypto_org from "../families/crypto_org/transaction";
12
-
13
7
  import elrond from "../families/elrond/transaction";
14
-
15
8
  import ethereum from "../families/ethereum/transaction";
16
-
17
9
  import filecoin from "../families/filecoin/transaction";
18
-
19
10
  import hedera from "../families/hedera/transaction";
20
-
21
11
  import neo from "../families/neo/transaction";
22
-
23
12
  import osmosis from "../families/osmosis/transaction";
24
-
25
13
  import polkadot from "../families/polkadot/transaction";
26
-
27
14
  import ripple from "../families/ripple/transaction";
28
-
29
15
  import solana from "../families/solana/transaction";
30
-
31
16
  import stellar from "../families/stellar/transaction";
32
-
33
17
  import tezos from "../families/tezos/transaction";
34
-
35
18
  import tron from "../families/tron/transaction";
36
19
 
37
-
38
20
  export default {
39
21
  algorand,
40
22
  bitcoin,
@@ -89,7 +89,8 @@ export type Transaction =
89
89
  | solanaTransaction
90
90
  | stellarTransaction
91
91
  | tezosTransaction
92
- | tronTransaction
92
+ | tronTransaction;
93
+
93
94
  export type TransactionRaw =
94
95
  | algorandTransactionRaw
95
96
  | bitcoinTransactionRaw
@@ -108,7 +109,8 @@ export type TransactionRaw =
108
109
  | solanaTransactionRaw
109
110
  | stellarTransactionRaw
110
111
  | tezosTransactionRaw
111
- | tronTransactionRaw
112
+ | tronTransactionRaw;
113
+
112
114
  export type TransactionStatus =
113
115
  | algorandTransactionStatus
114
116
  | bitcoinTransactionStatus
@@ -127,7 +129,8 @@ export type TransactionStatus =
127
129
  | solanaTransactionStatus
128
130
  | stellarTransactionStatus
129
131
  | tezosTransactionStatus
130
- | tronTransactionStatus
132
+ | tronTransactionStatus;
133
+
131
134
  export type TransactionStatusRaw =
132
135
  | algorandTransactionStatusRaw
133
136
  | bitcoinTransactionStatusRaw
@@ -146,4 +149,4 @@ export type TransactionStatusRaw =
146
149
  | solanaTransactionStatusRaw
147
150
  | stellarTransactionStatusRaw
148
151
  | tezosTransactionStatusRaw
149
- | tronTransactionStatusRaw
152
+ | tronTransactionStatusRaw;
@@ -0,0 +1,343 @@
1
+ import {
2
+ concat,
3
+ of,
4
+ EMPTY,
5
+ interval,
6
+ Observable,
7
+ TimeoutError,
8
+ throwError,
9
+ } from "rxjs";
10
+ import {
11
+ scan,
12
+ debounce,
13
+ debounceTime,
14
+ catchError,
15
+ switchMap,
16
+ tap,
17
+ distinctUntilChanged,
18
+ timeout,
19
+ } from "rxjs/operators";
20
+ import { useEffect, useState } from "react";
21
+ import { log } from "@ledgerhq/logs";
22
+ import type { DeviceInfo } from "@ledgerhq/types-live";
23
+ import { useReplaySubject } from "../../observable";
24
+ import type {
25
+ InstallLanguageEvent,
26
+ InstallLanguageRequest,
27
+ } from "../installLanguage";
28
+ import type { Action, Device } from "./types";
29
+ import isEqual from "lodash/isEqual";
30
+ import { ConnectManagerTimeout } from "../../errors";
31
+ import { currentMode } from "./app";
32
+ import {
33
+ DisconnectedDevice,
34
+ DisconnectedDeviceDuringOperation,
35
+ } from "@ledgerhq/errors";
36
+ import { getDeviceModel } from "@ledgerhq/devices";
37
+ import { Language } from "@ledgerhq/types-live";
38
+
39
+ type State = {
40
+ isLoading: boolean;
41
+ requestQuitApp: boolean;
42
+ unresponsive: boolean;
43
+ languageInstallationRequested?: boolean;
44
+ installingLanguage?: boolean;
45
+ languageInstalled?: boolean;
46
+ device: Device | null | undefined;
47
+ deviceInfo: DeviceInfo | null | undefined;
48
+ error: Error | null | undefined;
49
+ progress?: number;
50
+ };
51
+
52
+ type InstallLanguageAction = Action<Language, State, boolean | undefined>;
53
+
54
+ const mapResult = ({ languageInstalled }: State) => languageInstalled;
55
+
56
+ type Event =
57
+ | InstallLanguageEvent
58
+ | {
59
+ type: "error";
60
+ error: Error;
61
+ }
62
+ | {
63
+ type: "deviceChange";
64
+ device: Device | null | undefined;
65
+ };
66
+
67
+ const getInitialState = (device?: Device | null | undefined): State => ({
68
+ isLoading: !!device,
69
+ requestQuitApp: false,
70
+ unresponsive: false,
71
+ device,
72
+ deviceInfo: null,
73
+ error: null,
74
+ });
75
+
76
+ const reducer = (state: State, e: Event): State => {
77
+ switch (e.type) {
78
+ case "unresponsiveDevice":
79
+ return { ...state, unresponsive: true, isLoading: false };
80
+
81
+ case "deviceChange":
82
+ return getInitialState(e.device);
83
+
84
+ case "error":
85
+ return {
86
+ ...getInitialState(state.device),
87
+ error: e.error,
88
+ isLoading: false,
89
+ };
90
+ case "appDetected":
91
+ return {
92
+ ...state,
93
+ unresponsive: false,
94
+ requestQuitApp: true,
95
+ isLoading: false,
96
+ };
97
+ case "devicePermissionRequested":
98
+ return {
99
+ ...state,
100
+ unresponsive: false,
101
+ languageInstallationRequested: true,
102
+ isLoading: false,
103
+ };
104
+ case "languageInstalled":
105
+ return {
106
+ ...state,
107
+ unresponsive: false,
108
+ languageInstallationRequested: false,
109
+ isLoading: false,
110
+ installingLanguage: false,
111
+ languageInstalled: true,
112
+ };
113
+
114
+ case "progress":
115
+ return {
116
+ ...state,
117
+ unresponsive: false,
118
+ languageInstallationRequested: false,
119
+ isLoading: false,
120
+ installingLanguage: true,
121
+ progress: e.progress,
122
+ };
123
+ }
124
+ };
125
+
126
+ const implementations = {
127
+ // in this paradigm, we know that deviceSubject is reflecting the device events
128
+ // so we just trust deviceSubject to reflect the device context (switch between apps, dashboard,...)
129
+ event: ({ deviceSubject, installLanguage, language }) =>
130
+ deviceSubject.pipe(
131
+ debounceTime(1000),
132
+ switchMap((d) => installLanguage(d, language))
133
+ ),
134
+ // in this paradigm, we can't observe directly the device, so we have to poll it
135
+ polling: ({ deviceSubject, installLanguage, language }) =>
136
+ new Observable((o) => {
137
+ const POLLING = 2000;
138
+ const INIT_DEBOUNCE = 5000;
139
+ const DISCONNECT_DEBOUNCE = 5000;
140
+ const DEVICE_POLLING_TIMEOUT = 20000;
141
+ // this pattern allows to actually support events based (like if deviceSubject emits new device changes) but inside polling paradigm
142
+ let pollingOnDevice;
143
+ const sub = deviceSubject.subscribe((d) => {
144
+ if (d) {
145
+ pollingOnDevice = d;
146
+ }
147
+ });
148
+ let initT: NodeJS.Timeout | null = setTimeout(() => {
149
+ // initial timeout to unset the device if it's still not connected
150
+ o.next({
151
+ type: "deviceChange",
152
+ device: null,
153
+ });
154
+ device = null;
155
+ log("actions-install-language-event/polling", "device init timeout");
156
+ }, INIT_DEBOUNCE);
157
+ let connectSub;
158
+ let loopT;
159
+ let disconnectT;
160
+ let device = null; // used as internal state for polling
161
+
162
+ let stopDevicePollingError = null;
163
+
164
+ function loop() {
165
+ stopDevicePollingError = null;
166
+
167
+ if (!pollingOnDevice) {
168
+ loopT = setTimeout(loop, POLLING);
169
+ return;
170
+ }
171
+
172
+ log("actions-install-language-event/polling", "polling loop");
173
+ connectSub = installLanguage(pollingOnDevice, language)
174
+ .pipe(
175
+ timeout(DEVICE_POLLING_TIMEOUT),
176
+ catchError((err) => {
177
+ const productName = getDeviceModel(
178
+ pollingOnDevice.modelId
179
+ ).productName;
180
+ return err instanceof TimeoutError
181
+ ? of({
182
+ type: "error",
183
+ error: new ConnectManagerTimeout(undefined, {
184
+ productName,
185
+ }) as Error,
186
+ })
187
+ : throwError(err);
188
+ })
189
+ )
190
+ .subscribe({
191
+ next: (event) => {
192
+ if (initT && device) {
193
+ clearTimeout(initT);
194
+ initT = null;
195
+ }
196
+
197
+ if (disconnectT) {
198
+ // any connect app event unschedule the disconnect debounced event
199
+ clearTimeout(disconnectT);
200
+ disconnectT = null;
201
+ }
202
+
203
+ if (event.type === "error" && event.error) {
204
+ if (
205
+ event.error instanceof DisconnectedDevice ||
206
+ event.error instanceof DisconnectedDeviceDuringOperation
207
+ ) {
208
+ // disconnect on manager actions seems to trigger a type "error" instead of "disconnect"
209
+ // the disconnect event is delayed to debounce the reconnection that happens when switching apps
210
+ disconnectT = setTimeout(() => {
211
+ disconnectT = null;
212
+ // a disconnect will locally be remembered via locally setting device to null...
213
+ device = null;
214
+ o.next(event);
215
+ log(
216
+ "actions-install-language-event/polling",
217
+ "device disconnect timeout"
218
+ );
219
+ }, DISCONNECT_DEBOUNCE);
220
+ } else {
221
+ // These error events should stop polling
222
+ stopDevicePollingError = event.error;
223
+
224
+ // clear all potential polling loops
225
+ if (loopT) {
226
+ clearTimeout(loopT);
227
+ loopT = null;
228
+ }
229
+
230
+ // send in the event for the UI immediately
231
+ o.next(event);
232
+ }
233
+ } else if (event.type === "unresponsiveDevice") {
234
+ return; // ignore unresponsive case which happens for polling
235
+ } else {
236
+ if (device !== pollingOnDevice) {
237
+ // ...but any time an event comes back, it means our device was responding and need to be set back on in polling context
238
+ device = pollingOnDevice;
239
+ o.next({
240
+ type: "deviceChange",
241
+ device,
242
+ });
243
+ }
244
+
245
+ o.next(event);
246
+ }
247
+ },
248
+ complete: () => {
249
+ // start a new polling if available
250
+ if (!stopDevicePollingError) loopT = setTimeout(loop, POLLING);
251
+ },
252
+ error: (e) => {
253
+ o.error(e);
254
+ },
255
+ });
256
+ }
257
+
258
+ // delay a bit the first loop run in order to be async and wait pollingOnDevice
259
+ loopT = setTimeout(loop, 0);
260
+ return () => {
261
+ if (initT) clearTimeout(initT);
262
+ if (disconnectT) clearTimeout(disconnectT);
263
+ if (connectSub) connectSub.unsubscribe();
264
+ sub.unsubscribe();
265
+ clearTimeout(loopT);
266
+ };
267
+ }).pipe(distinctUntilChanged(isEqual)),
268
+ };
269
+ export const createAction = (
270
+ installLanuageExec: (
271
+ arg0: InstallLanguageRequest
272
+ ) => Observable<InstallLanguageEvent>
273
+ ): InstallLanguageAction => {
274
+ const installLanguage = (device, language: Language) => {
275
+ return concat(
276
+ of({
277
+ type: "deviceChange",
278
+ device,
279
+ }),
280
+ !device
281
+ ? EMPTY
282
+ : installLanuageExec({
283
+ deviceId: device.deviceId,
284
+ language: language,
285
+ }).pipe(
286
+ catchError((error: Error) =>
287
+ of({
288
+ type: "error",
289
+ error,
290
+ })
291
+ )
292
+ )
293
+ );
294
+ };
295
+
296
+ const useHook = (
297
+ device: Device | null | undefined,
298
+ language: Language
299
+ ): State => {
300
+ const [state, setState] = useState(() => getInitialState(device));
301
+ const deviceSubject = useReplaySubject(device);
302
+
303
+ useEffect(() => {
304
+ if (state.languageInstalled) return;
305
+
306
+ const impl = implementations[currentMode]({
307
+ deviceSubject,
308
+ installLanguage,
309
+ language,
310
+ });
311
+
312
+ const sub = impl
313
+ .pipe(
314
+ // debounce a bit the connect/disconnect event that we don't need
315
+ tap((e: Event) => log("actions-install-language-event", e.type, e)),
316
+ // we gather all events with a reducer into the UI state
317
+ scan(reducer, getInitialState()),
318
+ // we debounce the UI state to not blink on the UI
319
+ debounce((s: State) => {
320
+ if (s.installingLanguage || s.languageInstallationRequested) {
321
+ return EMPTY;
322
+ }
323
+
324
+ // default debounce (to be tweak)
325
+ return interval(1500);
326
+ })
327
+ ) // the state simply goes into a React state
328
+ .subscribe(setState);
329
+ return () => {
330
+ sub.unsubscribe();
331
+ };
332
+ }, [deviceSubject, language, state.languageInstalled]);
333
+
334
+ return {
335
+ ...state,
336
+ };
337
+ };
338
+
339
+ return {
340
+ useHook,
341
+ mapResult,
342
+ };
343
+ };
@@ -0,0 +1,32 @@
1
+ import { from, Observable, of, throwError } from "rxjs";
2
+ import { catchError, concatMap } from "rxjs/operators";
3
+ import appSupportsQuitApp from "../appSupportsQuitApp";
4
+ import type { AppAndVersion } from "./connectApp";
5
+ import quitApp from "./quitApp";
6
+
7
+ export type AttemptToQuitAppEvent =
8
+ | {
9
+ type: "unresponsiveDevice";
10
+ }
11
+ | {
12
+ type: "appDetected";
13
+ };
14
+
15
+ const attemptToQuitApp = (
16
+ transport,
17
+ appAndVersion?: AppAndVersion
18
+ ): Observable<AttemptToQuitAppEvent> =>
19
+ appAndVersion && appSupportsQuitApp(appAndVersion)
20
+ ? from(quitApp(transport)).pipe(
21
+ concatMap(() =>
22
+ of(<AttemptToQuitAppEvent>{
23
+ type: "unresponsiveDevice",
24
+ })
25
+ ),
26
+ catchError((e) => throwError(e))
27
+ )
28
+ : of({
29
+ type: "appDetected",
30
+ });
31
+
32
+ export default attemptToQuitApp;
@@ -9,12 +9,12 @@ import { listApps } from "../apps/hw";
9
9
  import { withDevice } from "./deviceAccess";
10
10
  import getDeviceInfo from "./getDeviceInfo";
11
11
  import getAppAndVersion from "./getAppAndVersion";
12
- import appSupportsQuitApp from "../appSupportsQuitApp";
13
12
  import { isDashboardName } from "./isDashboardName";
14
13
  import { DeviceNotOnboarded } from "../errors";
15
- import type { AppAndVersion } from "./connectApp";
16
- import quitApp from "./quitApp";
14
+
17
15
  import { DeviceInfo } from "@ledgerhq/types-live";
16
+ import attemptToQuitApp, { AttemptToQuitAppEvent } from "./attemptToQuitApp";
17
+
18
18
  export type Input = {
19
19
  devicePath: string;
20
20
  managerRequest:
@@ -24,13 +24,9 @@ export type Input = {
24
24
  | null
25
25
  | undefined;
26
26
  };
27
+
27
28
  export type ConnectManagerEvent =
28
- | {
29
- type: "appDetected";
30
- }
31
- | {
32
- type: "unresponsiveDevice";
33
- }
29
+ | AttemptToQuitAppEvent
34
30
  | {
35
31
  type: "osu";
36
32
  deviceInfo: DeviceInfo;
@@ -45,23 +41,6 @@ export type ConnectManagerEvent =
45
41
  }
46
42
  | ListAppsEvent;
47
43
 
48
- const attemptToQuitApp = (
49
- transport,
50
- appAndVersion?: AppAndVersion
51
- ): Observable<ConnectManagerEvent> =>
52
- appAndVersion && appSupportsQuitApp(appAndVersion)
53
- ? from(quitApp(transport)).pipe(
54
- concatMap(() =>
55
- of(<ConnectManagerEvent>{
56
- type: "unresponsiveDevice",
57
- })
58
- ),
59
- catchError((e) => throwError(e))
60
- )
61
- : of({
62
- type: "appDetected",
63
- });
64
-
65
44
  const cmd = ({
66
45
  devicePath,
67
46
  managerRequest,
@@ -59,6 +59,9 @@ export default async function getDeviceInfo(
59
59
  mcuVersion,
60
60
  mcuTargetId,
61
61
  flags,
62
+ bootloaderVersion,
63
+ hardwareVersion,
64
+ languageId,
62
65
  } = res;
63
66
  const isOSU = rawVersion.includes("-osu");
64
67
  const version = rawVersion.replace("-osu", "");
@@ -105,5 +108,8 @@ export default async function getDeviceInfo(
105
108
  managerAllowed,
106
109
  pinValidated,
107
110
  onboarded,
111
+ bootloaderVersion,
112
+ hardwareVersion,
113
+ languageId,
108
114
  };
109
115
  }