@ledgerhq/live-common 21.26.3 → 21.30.0

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 (262) hide show
  1. package/lib/__tests__/currencies.js +1 -1
  2. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.synced.unit.test.js +27 -26
  3. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.synced.unit.test.js.map +1 -1
  4. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.syncing.integration.test.js +9 -7
  5. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.syncing.integration.test.js.map +1 -1
  6. package/lib/__tests__/test-helpers/bridge.js +1 -1
  7. package/lib/__tests__/test-helpers/bridge.js.map +1 -1
  8. package/lib/apps/hw.d.ts.map +1 -1
  9. package/lib/apps/hw.js +8 -3
  10. package/lib/apps/hw.js.map +1 -1
  11. package/lib/apps/support.d.ts.map +1 -1
  12. package/lib/apps/support.js +2 -1
  13. package/lib/apps/support.js.map +1 -1
  14. package/lib/bridge/jsHelpers.js +1 -1
  15. package/lib/bridge/jsHelpers.js.map +1 -1
  16. package/lib/data/icons/react/index.d.ts +1 -0
  17. package/lib/data/icons/react/index.d.ts.map +1 -1
  18. package/lib/data/icons/react/index.js +4 -2
  19. package/lib/data/icons/react/index.js.map +1 -1
  20. package/lib/data/icons/react/sol.js +3 -3
  21. package/lib/data/icons/react/sol.js.map +1 -1
  22. package/lib/data/icons/react/tyc.d.ts +8 -0
  23. package/lib/data/icons/react/tyc.d.ts.map +1 -0
  24. package/lib/data/icons/react/tyc.js +33 -0
  25. package/lib/data/icons/react/tyc.js.map +1 -0
  26. package/lib/data/icons/reactNative/index.d.ts +1 -0
  27. package/lib/data/icons/reactNative/index.d.ts.map +1 -1
  28. package/lib/data/icons/reactNative/index.js +4 -2
  29. package/lib/data/icons/reactNative/index.js.map +1 -1
  30. package/lib/data/icons/reactNative/sol.js +3 -3
  31. package/lib/data/icons/reactNative/sol.js.map +1 -1
  32. package/lib/data/icons/reactNative/tyc.d.ts +8 -0
  33. package/lib/data/icons/reactNative/tyc.d.ts.map +1 -0
  34. package/lib/data/icons/reactNative/tyc.js +34 -0
  35. package/lib/data/icons/reactNative/tyc.js.map +1 -0
  36. package/lib/env.d.ts +20 -6
  37. package/lib/env.d.ts.map +1 -1
  38. package/lib/env.js +16 -2
  39. package/lib/env.js.map +1 -1
  40. package/lib/env.react.d.ts +1 -1
  41. package/lib/env.react.d.ts.map +1 -1
  42. package/lib/exchange/hw-app-exchange/Exchange.d.ts +12 -15
  43. package/lib/exchange/hw-app-exchange/Exchange.d.ts.map +1 -1
  44. package/lib/exchange/hw-app-exchange/Exchange.js +4 -13
  45. package/lib/exchange/hw-app-exchange/Exchange.js.map +1 -1
  46. package/lib/exchange/index.d.ts.map +1 -1
  47. package/lib/exchange/index.js +9 -2
  48. package/lib/exchange/index.js.map +1 -1
  49. package/lib/exchange/platform/completeExchange.d.ts +6 -0
  50. package/lib/exchange/platform/completeExchange.d.ts.map +1 -0
  51. package/lib/exchange/platform/completeExchange.js +24 -0
  52. package/lib/exchange/platform/completeExchange.js.map +1 -0
  53. package/lib/exchange/platform/serialization.d.ts +4 -0
  54. package/lib/exchange/platform/serialization.d.ts.map +1 -0
  55. package/lib/exchange/platform/serialization.js +57 -0
  56. package/lib/exchange/platform/serialization.js.map +1 -0
  57. package/lib/exchange/{swap → platform}/startExchange.d.ts +0 -0
  58. package/lib/exchange/platform/startExchange.d.ts.map +1 -0
  59. package/lib/exchange/{swap → platform}/startExchange.js +3 -3
  60. package/lib/exchange/platform/startExchange.js.map +1 -0
  61. package/lib/exchange/platform/transfer/completeExchange.d.ts +5 -0
  62. package/lib/exchange/platform/transfer/completeExchange.d.ts.map +1 -0
  63. package/lib/exchange/platform/transfer/completeExchange.js +198 -0
  64. package/lib/exchange/platform/transfer/completeExchange.js.map +1 -0
  65. package/lib/exchange/platform/transfer/index.d.ts +5 -0
  66. package/lib/exchange/platform/transfer/index.d.ts.map +1 -0
  67. package/lib/exchange/platform/transfer/index.js +49 -0
  68. package/lib/exchange/platform/transfer/index.js.map +1 -0
  69. package/lib/exchange/platform/types.d.ts +59 -0
  70. package/lib/exchange/platform/types.d.ts.map +1 -0
  71. package/lib/exchange/platform/types.js +3 -0
  72. package/lib/exchange/platform/types.js.map +1 -0
  73. package/lib/exchange/sell/checkSignatureAndPrepare.js +1 -2
  74. package/lib/exchange/sell/checkSignatureAndPrepare.js.map +1 -1
  75. package/lib/exchange/sell/getTransactionId.js +1 -2
  76. package/lib/exchange/sell/getTransactionId.js.map +1 -1
  77. package/lib/exchange/sell/index.d.ts +6 -0
  78. package/lib/exchange/sell/index.d.ts.map +1 -1
  79. package/lib/exchange/sell/index.js +3 -3
  80. package/lib/exchange/sell/index.js.map +1 -1
  81. package/lib/exchange/swap/completeExchange.d.ts +2 -13
  82. package/lib/exchange/swap/completeExchange.d.ts.map +1 -1
  83. package/lib/exchange/swap/completeExchange.js +2 -4
  84. package/lib/exchange/swap/completeExchange.js.map +1 -1
  85. package/lib/exchange/swap/hooks/usePickDefaultAccount.d.ts.map +1 -1
  86. package/lib/exchange/swap/hooks/usePickDefaultAccount.js +4 -9
  87. package/lib/exchange/swap/hooks/usePickDefaultAccount.js.map +1 -1
  88. package/lib/exchange/swap/initSwap.d.ts.map +1 -1
  89. package/lib/exchange/swap/initSwap.js +3 -4
  90. package/lib/exchange/swap/initSwap.js.map +1 -1
  91. package/lib/exchange/swap/types.d.ts +0 -12
  92. package/lib/exchange/swap/types.d.ts.map +1 -1
  93. package/lib/exchange/swap/utils/index.d.ts +6 -1
  94. package/lib/exchange/swap/utils/index.d.ts.map +1 -1
  95. package/lib/exchange/swap/utils/index.js +8 -1
  96. package/lib/exchange/swap/utils/index.js.map +1 -1
  97. package/lib/exchange/swap/utils/index.test.js +27 -0
  98. package/lib/exchange/swap/utils/index.test.js.map +1 -1
  99. package/lib/exchange/testCurrencyConfig/data/coins.d.ts +8 -0
  100. package/lib/exchange/testCurrencyConfig/data/coins.d.ts.map +1 -0
  101. package/lib/exchange/testCurrencyConfig/data/coins.js +21 -0
  102. package/lib/exchange/testCurrencyConfig/data/coins.js.map +1 -0
  103. package/lib/exchange/testCurrencyConfig/index.d.ts +8 -0
  104. package/lib/exchange/testCurrencyConfig/index.d.ts.map +1 -0
  105. package/lib/exchange/testCurrencyConfig/index.js +68 -0
  106. package/lib/exchange/testCurrencyConfig/index.js.map +1 -0
  107. package/lib/families/algorand/api/algodv2.d.ts +2 -1
  108. package/lib/families/algorand/api/algodv2.d.ts.map +1 -1
  109. package/lib/families/algorand/api/algodv2.js +2 -2
  110. package/lib/families/algorand/api/algodv2.js.map +1 -1
  111. package/lib/families/algorand/js-broadcast.js +1 -1
  112. package/lib/families/algorand/js-broadcast.js.map +1 -1
  113. package/lib/families/algorand/js-signOperation.d.ts.map +1 -1
  114. package/lib/families/algorand/js-signOperation.js +1 -1
  115. package/lib/families/algorand/js-signOperation.js.map +1 -1
  116. package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
  117. package/lib/families/bitcoin/wallet-btc/wallet.js +8 -2
  118. package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
  119. package/lib/families/ethereum/datasets/ethereum.scanAccounts.1.js +1 -1
  120. package/lib/families/ethereum/datasets/ethereum.scanAccounts.1.js.map +1 -1
  121. package/lib/families/ethereum/nft.integration.test.js +1 -36
  122. package/lib/families/ethereum/nft.integration.test.js.map +1 -1
  123. package/lib/families/ethereum/specs.d.ts.map +1 -1
  124. package/lib/families/ethereum/specs.js +2 -0
  125. package/lib/families/ethereum/specs.js.map +1 -1
  126. package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
  127. package/lib/families/ethereum/synchronisation.js +2 -7
  128. package/lib/families/ethereum/synchronisation.js.map +1 -1
  129. package/lib/families/solana/api/chain/index.d.ts +2 -2
  130. package/lib/families/solana/api/chain/index.d.ts.map +1 -1
  131. package/lib/families/solana/api/chain/index.js +4 -4
  132. package/lib/families/solana/api/chain/index.js.map +1 -1
  133. package/lib/families/solana/bridge/bridge.js +5 -5
  134. package/lib/families/solana/bridge/bridge.js.map +1 -1
  135. package/lib/families/solana/bridge/js.js +3 -3
  136. package/lib/families/solana/bridge/js.js.map +1 -1
  137. package/lib/families/solana/bridge/mock.js +1 -1
  138. package/lib/families/solana/bridge/mock.js.map +1 -1
  139. package/lib/families/solana/utils.d.ts +1 -2
  140. package/lib/families/solana/utils.d.ts.map +1 -1
  141. package/lib/families/solana/utils.js +11 -9
  142. package/lib/families/solana/utils.js.map +1 -1
  143. package/lib/families/tezos/datasets/tezos.scanAccounts.1.js +1 -1
  144. package/lib/families/tezos/datasets/tezos.scanAccounts.1.js.map +1 -1
  145. package/lib/hw/actions/app.d.ts +3 -0
  146. package/lib/hw/actions/app.d.ts.map +1 -1
  147. package/lib/hw/actions/app.js +7 -1
  148. package/lib/hw/actions/app.js.map +1 -1
  149. package/lib/hw/actions/completeExchange.d.ts +1 -0
  150. package/lib/hw/actions/completeExchange.d.ts.map +1 -1
  151. package/lib/hw/actions/completeExchange.js +4 -3
  152. package/lib/hw/actions/completeExchange.js.map +1 -1
  153. package/lib/hw/connectApp.d.ts +5 -0
  154. package/lib/hw/connectApp.d.ts.map +1 -1
  155. package/lib/hw/connectApp.js +37 -26
  156. package/lib/hw/connectApp.js.map +1 -1
  157. package/lib/market/MarketDataProvider.d.ts +26 -0
  158. package/lib/market/MarketDataProvider.d.ts.map +1 -0
  159. package/lib/market/MarketDataProvider.js +339 -0
  160. package/lib/market/MarketDataProvider.js.map +1 -0
  161. package/lib/market/api/api.d.ts +15 -0
  162. package/lib/market/api/api.d.ts.map +1 -0
  163. package/lib/market/api/api.js +261 -0
  164. package/lib/market/api/api.js.map +1 -0
  165. package/lib/market/api/api.mock.d.ts +15 -0
  166. package/lib/market/api/api.mock.d.ts.map +1 -0
  167. package/lib/market/api/api.mock.js +454 -0
  168. package/lib/market/api/api.mock.js.map +1 -0
  169. package/lib/market/types.d.ts +83 -0
  170. package/lib/market/types.d.ts.map +1 -0
  171. package/lib/market/types.js +3 -0
  172. package/lib/market/types.js.map +1 -0
  173. package/lib/market/utils/countervalueFormatter.d.ts +8 -0
  174. package/lib/market/utils/countervalueFormatter.d.ts.map +1 -0
  175. package/lib/market/utils/countervalueFormatter.js +18 -0
  176. package/lib/market/utils/countervalueFormatter.js.map +1 -0
  177. package/lib/market/utils/rangeDataTable.d.ts +10 -0
  178. package/lib/market/utils/rangeDataTable.d.ts.map +1 -0
  179. package/lib/market/utils/rangeDataTable.js +36 -0
  180. package/lib/market/utils/rangeDataTable.js.map +1 -0
  181. package/lib/nft/support.d.ts.map +1 -1
  182. package/lib/nft/support.js +1 -1
  183. package/lib/nft/support.js.map +1 -1
  184. package/lib/platform/PlatformAppProvider/api/api.mock.d.ts.map +1 -1
  185. package/lib/platform/PlatformAppProvider/api/api.mock.js +27 -0
  186. package/lib/platform/PlatformAppProvider/api/api.mock.js.map +1 -1
  187. package/lib/types/manager.d.ts +7 -0
  188. package/lib/types/manager.d.ts.map +1 -1
  189. package/lib/types/manager.js +7 -0
  190. package/lib/types/manager.js.map +1 -1
  191. package/package.json +17 -17
  192. package/src/.DS_Store +0 -0
  193. package/src/__tests__/__snapshots__/all.libcore.ts.snap +221 -212
  194. package/src/__tests__/currencies.ts +1 -1
  195. package/src/__tests__/families/bitcoin/wallet-btc/xpub.synced.unit.test.ts +26 -25
  196. package/src/__tests__/families/bitcoin/wallet-btc/xpub.syncing.integration.test.ts +2 -0
  197. package/src/__tests__/test-helpers/bridge.ts +1 -1
  198. package/src/apps/hw.ts +7 -0
  199. package/src/apps/support.ts +1 -0
  200. package/src/bridge/jsHelpers.ts +1 -1
  201. package/src/data/flags/.DS_Store +0 -0
  202. package/src/data/icons/react/index.tsx +1 -0
  203. package/src/data/icons/react/sol.tsx +1 -1
  204. package/src/data/icons/react/tyc.tsx +12 -0
  205. package/src/data/icons/reactNative/index.tsx +1 -0
  206. package/src/data/icons/reactNative/sol.tsx +1 -1
  207. package/src/data/icons/reactNative/tyc.tsx +13 -0
  208. package/src/data/icons/svg/SOL.svg +8 -1
  209. package/src/data/icons/svg/TYC.svg +6 -0
  210. package/src/env.ts +16 -2
  211. package/src/exchange/hw-app-exchange/Exchange.ts +22 -18
  212. package/src/exchange/index.ts +19 -1
  213. package/src/exchange/platform/completeExchange.ts +42 -0
  214. package/src/exchange/platform/serialization.ts +78 -0
  215. package/src/exchange/{swap → platform}/startExchange.ts +6 -2
  216. package/src/exchange/platform/transfer/completeExchange.ts +187 -0
  217. package/src/exchange/platform/transfer/index.ts +66 -0
  218. package/src/exchange/platform/types.ts +76 -0
  219. package/src/exchange/sell/checkSignatureAndPrepare.ts +2 -2
  220. package/src/exchange/sell/getTransactionId.ts +2 -2
  221. package/src/exchange/sell/index.ts +1 -1
  222. package/src/exchange/swap/completeExchange.ts +41 -50
  223. package/src/exchange/swap/hooks/usePickDefaultAccount.ts +4 -9
  224. package/src/exchange/swap/initSwap.ts +4 -7
  225. package/src/exchange/swap/types.ts +1 -14
  226. package/src/exchange/swap/utils/index.test.ts +44 -1
  227. package/src/exchange/swap/utils/index.ts +12 -1
  228. package/src/exchange/testCurrencyConfig/data/coins.ts +20 -0
  229. package/src/exchange/testCurrencyConfig/index.ts +24 -0
  230. package/src/families/algorand/api/algodv2.ts +2 -2
  231. package/src/families/algorand/js-broadcast.ts +1 -1
  232. package/src/families/algorand/js-signOperation.ts +8 -3
  233. package/src/families/bitcoin/wallet-btc/wallet.ts +6 -2
  234. package/src/families/ethereum/datasets/ethereum.scanAccounts.1.ts +43 -43
  235. package/src/families/ethereum/nft.integration.test.ts +1 -18
  236. package/src/families/ethereum/specs.ts +2 -0
  237. package/src/families/ethereum/synchronisation.ts +1 -6
  238. package/src/families/solana/api/chain/index.ts +8 -7
  239. package/src/families/solana/bridge/bridge.ts +11 -11
  240. package/src/families/solana/bridge/js.ts +3 -3
  241. package/src/families/solana/bridge/mock.ts +1 -1
  242. package/src/families/solana/utils.ts +9 -8
  243. package/src/families/tezos/datasets/tezos.scanAccounts.1.ts +39 -39
  244. package/src/hw/actions/app.ts +17 -1
  245. package/src/hw/actions/completeExchange.ts +9 -3
  246. package/src/hw/connectApp.ts +53 -31
  247. package/src/market/MarketDataProvider.tsx +440 -0
  248. package/src/market/api/api.mock.ts +374 -0
  249. package/src/market/api/api.ts +227 -0
  250. package/src/market/types.ts +92 -0
  251. package/src/market/utils/countervalueFormatter.ts +24 -0
  252. package/src/market/utils/rangeDataTable.ts +40 -0
  253. package/src/nft/support.ts +1 -3
  254. package/src/platform/PlatformAppProvider/api/api.mock.ts +27 -0
  255. package/src/types/manager.ts +7 -0
  256. package/lib/exchange/swap/startExchange.d.ts.map +0 -1
  257. package/lib/exchange/swap/startExchange.js.map +0 -1
  258. package/lib/families/ethereum/versions.d.ts +0 -2
  259. package/lib/families/ethereum/versions.d.ts.map +0 -1
  260. package/lib/families/ethereum/versions.js +0 -5
  261. package/lib/families/ethereum/versions.js.map +0 -1
  262. package/src/families/ethereum/versions.ts +0 -1
@@ -11,6 +11,7 @@ type State = {
11
11
  completeExchangeResult: Transaction | null | undefined;
12
12
  completeExchangeError: Error | null | undefined;
13
13
  freezeReduxDevice: boolean;
14
+ completeExchangeRequested: boolean;
14
15
  isLoading: boolean;
15
16
  };
16
17
 
@@ -61,14 +62,19 @@ const mapResult = ({
61
62
  const initialState: State = {
62
63
  completeExchangeResult: null,
63
64
  completeExchangeError: null,
65
+ completeExchangeRequested: false,
64
66
  freezeReduxDevice: false,
65
- isLoading: false,
67
+ isLoading: true,
66
68
  };
67
69
 
68
70
  const reducer = (state: State, e: ExchangeRequestEvent) => {
69
71
  switch (e.type) {
70
72
  case "complete-exchange":
71
- return { ...state, isLoading: true, freezeReduxDevice: true };
73
+ return {
74
+ ...state,
75
+ completeExchangeStarted: true,
76
+ freezeReduxDevice: true,
77
+ };
72
78
 
73
79
  case "complete-exchange-error":
74
80
  return {
@@ -81,7 +87,7 @@ const reducer = (state: State, e: ExchangeRequestEvent) => {
81
87
  return {
82
88
  ...state,
83
89
  completeExchangeRequested: true,
84
- isLoading: true,
90
+ isLoading: false,
85
91
  };
86
92
  case "complete-exchange-result":
87
93
  return {
@@ -1,5 +1,5 @@
1
1
  import semver from "semver";
2
- import { Observable, concat, from, of, throwError, defer } from "rxjs";
2
+ import { Observable, concat, from, of, throwError, defer, merge } from "rxjs";
3
3
  import { mergeMap, concatMap, map, catchError, delay } from "rxjs/operators";
4
4
  import {
5
5
  TransportStatusError,
@@ -55,6 +55,11 @@ export type ConnectAppEvent =
55
55
  type: "disconnected";
56
56
  expected?: boolean;
57
57
  }
58
+ | {
59
+ type: "device-update-last-seen";
60
+ deviceInfo: DeviceInfo;
61
+ latestFirmware: FirmwareUpdateContext | null | undefined;
62
+ }
58
63
  | {
59
64
  type: "device-permission-requested";
60
65
  wording: string;
@@ -102,37 +107,54 @@ export const openAppFromDashboard = (
102
107
  transport: Transport,
103
108
  appName: string
104
109
  ): Observable<ConnectAppEvent> =>
105
- concat(
106
- of<ConnectAppEvent>({
107
- type: "ask-open-app",
108
- appName,
109
- }),
110
- defer(() => from(openApp(transport, appName))).pipe(
111
- concatMap(() =>
112
- of<ConnectAppEvent>({
113
- type: "device-permission-granted",
114
- })
115
- ),
116
- catchError((e) => {
117
- if (e && e instanceof TransportStatusError) {
118
- // @ts-expect-error TransportStatusError to be typed on ledgerjs
119
- switch (e.statusCode) {
120
- case 0x6984:
121
- case 0x6807:
122
- return streamAppInstall({
123
- transport,
124
- appNames: [appName],
125
- onSuccessObs: () =>
126
- from(openAppFromDashboard(transport, appName)),
127
- }) as Observable<ConnectAppEvent>;
128
- case 0x6985:
129
- case 0x5501:
130
- return throwError(new UserRefusedOnDevice());
131
- }
132
- }
110
+ from(getDeviceInfo(transport)).pipe(
111
+ mergeMap((deviceInfo) =>
112
+ merge(
113
+ // Nb Allows LLD/LLM to update lastSeenDevice, this can run in parallel
114
+ // since there are no more device exchanges.
115
+ from(manager.getLatestFirmwareForDevice(deviceInfo)).pipe(
116
+ concatMap((latestFirmware) =>
117
+ of<ConnectAppEvent>({
118
+ type: "device-update-last-seen",
119
+ deviceInfo,
120
+ latestFirmware,
121
+ })
122
+ )
123
+ ),
124
+ concat(
125
+ of<ConnectAppEvent>({
126
+ type: "ask-open-app",
127
+ appName,
128
+ }),
129
+ defer(() => from(openApp(transport, appName))).pipe(
130
+ concatMap(() =>
131
+ of<ConnectAppEvent>({
132
+ type: "device-permission-granted",
133
+ })
134
+ ),
135
+ catchError((e) => {
136
+ if (e && e instanceof TransportStatusError) {
137
+ // @ts-expect-error TransportStatusError to be typed on ledgerjs
138
+ switch (e.statusCode) {
139
+ case 0x6984:
140
+ case 0x6807:
141
+ return streamAppInstall({
142
+ transport,
143
+ appNames: [appName],
144
+ onSuccessObs: () =>
145
+ from(openAppFromDashboard(transport, appName)),
146
+ }) as Observable<ConnectAppEvent>;
147
+ case 0x6985:
148
+ case 0x5501:
149
+ return throwError(new UserRefusedOnDevice());
150
+ }
151
+ }
133
152
 
134
- return throwError(e);
135
- })
153
+ return throwError(e);
154
+ })
155
+ )
156
+ )
157
+ )
136
158
  )
137
159
  );
138
160
 
@@ -0,0 +1,440 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-function */
2
+ // @flow
3
+ import React, {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ ReactElement,
8
+ useReducer,
9
+ useEffect,
10
+ useState,
11
+ } from "react";
12
+
13
+ import { useDebounce } from "../hooks/useDebounce";
14
+ import { Currency } from "../types";
15
+ import {
16
+ State,
17
+ MarketDataApi,
18
+ MarketListRequestParams,
19
+ MarketCurrencyChartDataRequestParams,
20
+ CurrencyData,
21
+ SingleCoinState,
22
+ } from "./types";
23
+ import defaultFetchApi from "./api/api";
24
+
25
+ type Props = {
26
+ children: React.ReactNode;
27
+ fetchApi?: MarketDataApi;
28
+ countervalue?: Currency;
29
+ initState?: Partial<State>;
30
+ };
31
+ type API = {
32
+ refresh: (param?: MarketListRequestParams) => void;
33
+ refreshChart: (param?: MarketCurrencyChartDataRequestParams) => void;
34
+ selectCurrency: (key: string) => void;
35
+ loadNextPage: () => Promise<boolean>;
36
+ setCounterCurrency: (counterCurrency: string) => void;
37
+ };
38
+
39
+ export type MarketDataContextType = State & API;
40
+
41
+ const initialState: State = {
42
+ ready: false,
43
+ marketData: [],
44
+ selectedCurrency: undefined,
45
+ requestParams: {
46
+ range: "24h",
47
+ limit: 50,
48
+ ids: [],
49
+ starred: [],
50
+ orderBy: "market_cap",
51
+ order: "desc",
52
+ search: "",
53
+ liveCompatible: false,
54
+ },
55
+ page: 1,
56
+ chartRequestParams: {
57
+ range: "24h",
58
+ },
59
+ loading: false,
60
+ loadingChart: false,
61
+ endOfList: false,
62
+ error: undefined,
63
+ totalCoinsAvailable: 0,
64
+ supportedCounterCurrencies: [],
65
+ selectedCoinData: undefined,
66
+ counterCurrency: undefined,
67
+ };
68
+
69
+ const MarketDataContext = createContext<MarketDataContextType>({
70
+ ...initialState,
71
+ refresh: () => {},
72
+ refreshChart: () => {},
73
+ selectCurrency: () => {},
74
+ loadNextPage: () => Promise.resolve(true),
75
+ setCounterCurrency: () => {},
76
+ });
77
+
78
+ const ACTIONS = {
79
+ IS_READY: "IS_READY",
80
+
81
+ UPDATE_MARKET_DATA: "UPDATE_MARKET_DATA",
82
+ UPDATE_SINGLE_MARKET_DATA: "UPDATE_SINGLE_MARKET_DATA",
83
+ UPDATE_SINGLE_CHART_DATA: "UPDATE_SINGLE_CHART_DATA",
84
+
85
+ REFRESH_MARKET_DATA: "REFRESH_MARKET_DATA",
86
+ REFRESH_CHART_DATA: "REFRESH_CHART_DATA",
87
+
88
+ SET_LOADING: "SET_LOADING",
89
+ SET_LOADING_CHART: "SET_LOADING_CHART",
90
+ SET_ERROR: "SET_ERROR",
91
+
92
+ SELECT_CURRENCY: "SELECT_CURRENCY",
93
+ UPDATE_COUNTERVALUE: "UPDATE_COUNTERVALUE",
94
+ };
95
+
96
+ function marketDataReducer(state, action) {
97
+ switch (action.type) {
98
+ case ACTIONS.IS_READY:
99
+ return { ...state, ...action.payload, isReady: true };
100
+ case ACTIONS.UPDATE_MARKET_DATA: {
101
+ const newData = action.payload.marketData;
102
+ const page = action.payload.page || state.requestParams.page;
103
+ const marketData = [...state.marketData.map((data) => ({ ...data }))];
104
+ if (!newData.length || marketData.some(({ id }) => id === newData[0].id))
105
+ return { ...state, loading: false };
106
+
107
+ return {
108
+ ...state,
109
+ marketData: marketData.concat(newData),
110
+ endOfList: newData.length < state.requestParams.limit,
111
+ loading: false,
112
+ page,
113
+ };
114
+ }
115
+ case ACTIONS.UPDATE_SINGLE_MARKET_DATA: {
116
+ const marketData = [...state.marketData.map((data) => ({ ...data }))];
117
+ const index = marketData.findIndex(({ id }) => id === action.payload.id);
118
+ if (index >= 0) {
119
+ marketData[index] = { ...marketData[index], ...action.payload };
120
+ }
121
+ return { ...state, marketData, loading: false };
122
+ }
123
+ case ACTIONS.UPDATE_SINGLE_CHART_DATA: {
124
+ const marketData = [...state.marketData.map((data) => ({ ...data }))];
125
+ const index = marketData.findIndex(({ id }) => id === action.payload.id);
126
+ if (index >= 0) {
127
+ marketData[index].chartData = {
128
+ ...marketData[index].chartData,
129
+ ...action.payload.chartData,
130
+ };
131
+ }
132
+ return { ...state, marketData, loading: false };
133
+ }
134
+
135
+ case ACTIONS.REFRESH_MARKET_DATA: {
136
+ const requestParams = {
137
+ ...state.requestParams,
138
+ ...action.payload,
139
+ lastRequestTime: Date.now(),
140
+ };
141
+ return {
142
+ ...state,
143
+ marketData: [],
144
+ requestParams,
145
+ loading: true,
146
+ page: 1,
147
+ };
148
+ }
149
+ case ACTIONS.REFRESH_CHART_DATA: {
150
+ const chartRequestParams = {
151
+ ...state.chartRequestParams,
152
+ ...action.payload,
153
+ loadingChart: true,
154
+ lastRequestTime: Date.now(),
155
+ };
156
+ return { ...state, chartRequestParams };
157
+ }
158
+
159
+ case ACTIONS.UPDATE_COUNTERVALUE: {
160
+ const requestParams = {
161
+ ...state.requestParams,
162
+ lastRequestTime: Date.now(),
163
+ page: 1,
164
+ counterCurrency: action.payload,
165
+ };
166
+ const chartRequestParams = {
167
+ ...state.chartRequestParams,
168
+ lastRequestTime: Date.now(),
169
+ counterCurrency: action.payload,
170
+ };
171
+ return {
172
+ ...state,
173
+ counterCurrency: action.payload,
174
+ marketData: [],
175
+ requestParams,
176
+ chartRequestParams,
177
+ loading: true,
178
+ };
179
+ }
180
+
181
+ case ACTIONS.SET_LOADING:
182
+ return { ...state, loading: action.payload };
183
+ case ACTIONS.SET_LOADING_CHART:
184
+ return { ...state, loadingChart: action.payload };
185
+ case ACTIONS.SET_ERROR:
186
+ return { ...state, error: action.payload, loading: false };
187
+ case ACTIONS.SELECT_CURRENCY:
188
+ return {
189
+ ...state,
190
+ selectedCurrency: action.payload,
191
+ loading: !!action.payload,
192
+ };
193
+ default:
194
+ return state;
195
+ }
196
+ }
197
+
198
+ export const MarketDataProvider = ({
199
+ children,
200
+ fetchApi,
201
+ countervalue,
202
+ initState = {},
203
+ }: Props): ReactElement => {
204
+ const [state, dispatch] = useReducer(marketDataReducer, {
205
+ ...initialState,
206
+ ...initState,
207
+ });
208
+ const api = fetchApi || defaultFetchApi;
209
+ const {
210
+ marketData,
211
+ requestParams,
212
+ chartRequestParams,
213
+ loading,
214
+ loadingChart,
215
+ page,
216
+ } = useDebounce(state, 300);
217
+
218
+ const handleError = useCallback((payload) => {
219
+ dispatch({ type: ACTIONS.SET_ERROR, payload });
220
+ }, []);
221
+
222
+ useEffect(() => {
223
+ if (countervalue)
224
+ api.supportedCounterCurrencies().then(
225
+ (supportedCounterCurrencies) =>
226
+ api.setSupportedCoinsList().then((coins) => {
227
+ dispatch({
228
+ type: ACTIONS.IS_READY,
229
+ payload: {
230
+ totalCoinsAvailable: coins.length,
231
+ supportedCounterCurrencies,
232
+ },
233
+ });
234
+ dispatch({
235
+ type: ACTIONS.UPDATE_COUNTERVALUE,
236
+ payload: supportedCounterCurrencies.includes(countervalue.ticker)
237
+ ? countervalue.ticker
238
+ : "usd",
239
+ });
240
+ }, handleError),
241
+ handleError
242
+ );
243
+ }, [api, countervalue, handleError]);
244
+
245
+ useEffect(() => {
246
+ if (
247
+ chartRequestParams?.id &&
248
+ chartRequestParams?.counterCurrency &&
249
+ !loadingChart
250
+ ) {
251
+ const currentMarketData = marketData.find(
252
+ ({ id }: CurrencyData) => id === chartRequestParams.id
253
+ );
254
+ const range = chartRequestParams.range;
255
+
256
+ if (currentMarketData && !currentMarketData?.chartData?.[range]) {
257
+ api.currencyChartData(chartRequestParams).then(
258
+ (chartData) =>
259
+ dispatch({
260
+ type: ACTIONS.UPDATE_SINGLE_CHART_DATA,
261
+ payload: { id: chartRequestParams.id, chartData },
262
+ }),
263
+ handleError
264
+ );
265
+ } else
266
+ dispatch({
267
+ type: ACTIONS.SET_LOADING_CHART,
268
+ payload: false,
269
+ });
270
+ }
271
+ }, [chartRequestParams, marketData, loadingChart, api, handleError]);
272
+
273
+ useEffect(() => {
274
+ if (
275
+ chartRequestParams?.id &&
276
+ chartRequestParams?.counterCurrency &&
277
+ !loading
278
+ ) {
279
+ dispatch({ type: ACTIONS.SET_LOADING, payload: true });
280
+ api
281
+ .listPaginated({
282
+ ...chartRequestParams,
283
+ search: "",
284
+ starred: [],
285
+ liveCompatible: false,
286
+ ids: [chartRequestParams.id],
287
+ limit: 1,
288
+ page: 1,
289
+ })
290
+ .then(
291
+ ([{ chartData: _, ...marketData }]) =>
292
+ dispatch({
293
+ type: ACTIONS.UPDATE_SINGLE_MARKET_DATA,
294
+ payload: marketData,
295
+ }),
296
+ handleError
297
+ );
298
+ }
299
+ }, [api, chartRequestParams, handleError, loading]);
300
+
301
+ useEffect(() => {
302
+ if (requestParams?.counterCurrency) {
303
+ api.listPaginated(requestParams).then(
304
+ (marketData) =>
305
+ dispatch({
306
+ type: ACTIONS.UPDATE_MARKET_DATA,
307
+ payload: { marketData },
308
+ }),
309
+ handleError
310
+ );
311
+ }
312
+ }, [api, handleError, requestParams]);
313
+
314
+ const refresh = useCallback((payload = {}) => {
315
+ dispatch({ type: ACTIONS.REFRESH_MARKET_DATA, payload });
316
+ }, []);
317
+
318
+ const refreshChart = useCallback((payload = {}) => {
319
+ dispatch({ type: ACTIONS.REFRESH_CHART_DATA, payload });
320
+ }, []);
321
+
322
+ const selectCurrency = useCallback((id) => {
323
+ dispatch({
324
+ type: ACTIONS.REFRESH_CHART_DATA,
325
+ payload: {
326
+ id,
327
+ },
328
+ });
329
+ }, []);
330
+
331
+ const loadNextPage = useCallback(() => {
332
+ return new Promise((resolve, reject) => {
333
+ if (loading) {
334
+ reject(new Error());
335
+ } else {
336
+ const newPage = page + 1;
337
+ api.listPaginated({ ...requestParams, page: newPage }).then(
338
+ (marketData) => {
339
+ dispatch({
340
+ type: ACTIONS.UPDATE_MARKET_DATA,
341
+ payload: { marketData, page: newPage },
342
+ });
343
+ resolve(true);
344
+ },
345
+ (err) => {
346
+ handleError(err);
347
+ reject(new Error(err));
348
+ }
349
+ );
350
+ }
351
+ });
352
+ }, [loading, page, api, requestParams, handleError]);
353
+
354
+ const setCounterCurrency = useCallback(
355
+ (payload) => dispatch({ type: ACTIONS.UPDATE_COUNTERVALUE, payload }),
356
+ [dispatch]
357
+ );
358
+
359
+ const value = {
360
+ ...state,
361
+ refresh,
362
+ refreshChart,
363
+ selectCurrency,
364
+ loadNextPage,
365
+ setCounterCurrency,
366
+ };
367
+
368
+ return (
369
+ <MarketDataContext.Provider value={value}>
370
+ {children}
371
+ </MarketDataContext.Provider>
372
+ );
373
+ };
374
+
375
+ export function useMarketData(): MarketDataContextType {
376
+ return useContext(MarketDataContext);
377
+ }
378
+
379
+ type SingleCoinProviderData = SingleCoinState & {
380
+ refreshChart: (param?: MarketCurrencyChartDataRequestParams) => void;
381
+ setCounterCurrency: (counterCurrency: string) => void;
382
+ };
383
+
384
+ export function useSingleCoinMarketData(
385
+ currencyId: string
386
+ ): SingleCoinProviderData {
387
+ const {
388
+ marketData,
389
+ selectedCurrency,
390
+ chartRequestParams,
391
+ loading,
392
+ loadingChart,
393
+ error,
394
+ counterCurrency,
395
+ refreshChart,
396
+ setCounterCurrency,
397
+ supportedCounterCurrencies,
398
+ } = useContext(MarketDataContext);
399
+
400
+ const [state, setState] = useState<SingleCoinProviderData>({
401
+ selectedCoinData: undefined,
402
+ selectedCurrency,
403
+ chartRequestParams,
404
+ loading,
405
+ loadingChart,
406
+ error,
407
+ counterCurrency,
408
+ refreshChart,
409
+ setCounterCurrency,
410
+ supportedCounterCurrencies,
411
+ });
412
+
413
+ useEffect(() => {
414
+ if (marketData)
415
+ setState((s) => ({
416
+ ...s,
417
+ selectedCoinData:
418
+ marketData.find(({ id }) => id === currencyId) || s.selectedCoinData,
419
+ selectedCurrency,
420
+ chartRequestParams,
421
+ loading,
422
+ loadingChart,
423
+ error,
424
+ counterCurrency,
425
+ supportedCounterCurrencies,
426
+ }));
427
+ }, [
428
+ marketData,
429
+ currencyId,
430
+ selectedCurrency,
431
+ chartRequestParams,
432
+ loading,
433
+ loadingChart,
434
+ error,
435
+ counterCurrency,
436
+ supportedCounterCurrencies,
437
+ ]);
438
+
439
+ return state;
440
+ }