@ledgerhq/live-countervalues-react 0.2.44-nightly.0 → 0.2.44-nightly.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/lib/index.d.ts +29 -13
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +90 -121
- package/lib/index.js.map +1 -1
- package/lib-es/index.d.ts +29 -13
- package/lib-es/index.d.ts.map +1 -1
- package/lib-es/index.js +83 -114
- package/lib-es/index.js.map +1 -1
- package/package.json +5 -5
- package/src/index.tsx +137 -174
package/src/index.tsx
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
|
-
import { BigNumber } from "bignumber.js";
|
|
2
|
-
import React, {
|
|
3
|
-
createContext,
|
|
4
|
-
useMemo,
|
|
5
|
-
useContext,
|
|
6
|
-
useEffect,
|
|
7
|
-
useReducer,
|
|
8
|
-
useState,
|
|
9
|
-
useCallback,
|
|
10
|
-
ReactElement,
|
|
11
|
-
} from "react";
|
|
12
1
|
import { getAccountCurrency } from "@ledgerhq/coin-framework/account/helpers";
|
|
2
|
+
import api from "@ledgerhq/live-countervalues/api/index";
|
|
13
3
|
import {
|
|
14
|
-
initialState,
|
|
15
4
|
calculate,
|
|
16
|
-
loadCountervalues,
|
|
17
5
|
exportCountervalues,
|
|
18
6
|
importCountervalues,
|
|
19
7
|
inferTrackingPairForAccounts,
|
|
8
|
+
initialState,
|
|
9
|
+
loadCountervalues,
|
|
20
10
|
trackingPairForTopCoins,
|
|
21
11
|
} from "@ledgerhq/live-countervalues/logic";
|
|
22
|
-
import api from "@ledgerhq/live-countervalues/api/index";
|
|
23
12
|
import type {
|
|
24
13
|
CounterValuesState,
|
|
25
14
|
CounterValuesStateRaw,
|
|
@@ -28,8 +17,28 @@ import type {
|
|
|
28
17
|
} from "@ledgerhq/live-countervalues/types";
|
|
29
18
|
import { useDebounce } from "@ledgerhq/live-hooks/useDebounce";
|
|
30
19
|
import { log } from "@ledgerhq/logs";
|
|
31
|
-
import type { Account, AccountLike } from "@ledgerhq/types-live";
|
|
32
20
|
import type { Currency, Unit } from "@ledgerhq/types-cryptoassets";
|
|
21
|
+
import type { Account, AccountLike } from "@ledgerhq/types-live";
|
|
22
|
+
import { BigNumber } from "bignumber.js";
|
|
23
|
+
import React, {
|
|
24
|
+
ReactElement,
|
|
25
|
+
createContext,
|
|
26
|
+
useCallback,
|
|
27
|
+
useContext,
|
|
28
|
+
useEffect,
|
|
29
|
+
useMemo,
|
|
30
|
+
useReducer,
|
|
31
|
+
useState,
|
|
32
|
+
} from "react";
|
|
33
|
+
|
|
34
|
+
/** Bridge enabling platform-specific persistence of market-cap ids. */
|
|
35
|
+
export interface CountervaluesMarketcapBridge {
|
|
36
|
+
useIds(): string[];
|
|
37
|
+
useLastUpdated(): number | undefined;
|
|
38
|
+
setLoading(loading: boolean): void;
|
|
39
|
+
setIds(ids: string[]): void;
|
|
40
|
+
setError(message: string): void;
|
|
41
|
+
}
|
|
33
42
|
|
|
34
43
|
// Polling is the control object you get from the high level <PollingConsumer>{ polling => ...
|
|
35
44
|
export type Polling = {
|
|
@@ -48,6 +57,7 @@ export type Polling = {
|
|
|
48
57
|
// if the last polling failed, there will be an error
|
|
49
58
|
error: Error | null | undefined;
|
|
50
59
|
};
|
|
60
|
+
|
|
51
61
|
export type Props = {
|
|
52
62
|
children: React.ReactNode;
|
|
53
63
|
userSettings: CountervaluesSettings;
|
|
@@ -78,7 +88,6 @@ const CountervaluesUserSettingsContext = createContext<CountervaluesSettings>({
|
|
|
78
88
|
});
|
|
79
89
|
|
|
80
90
|
const CountervaluesContext = createContext<CounterValuesState>(initialState);
|
|
81
|
-
|
|
82
91
|
const CountervaluesMarketcapIdsContext = createContext<string[]>([]);
|
|
83
92
|
|
|
84
93
|
function trackingPairsHash(a: TrackingPair[]) {
|
|
@@ -90,80 +99,56 @@ function trackingPairsHash(a: TrackingPair[]) {
|
|
|
90
99
|
|
|
91
100
|
const marketcapRefresh = 30 * 60000;
|
|
92
101
|
const marketcapRefreshOnError = 60000;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
|
|
103
|
+
/** Provides market-cap ids via the supplied bridge. */
|
|
104
|
+
export function CountervaluesMarketcapProvider({
|
|
105
|
+
children,
|
|
106
|
+
bridge,
|
|
107
|
+
}: {
|
|
108
|
+
children: React.ReactNode;
|
|
109
|
+
bridge: CountervaluesMarketcapBridge;
|
|
110
|
+
}): ReactElement {
|
|
111
|
+
const ids = bridge.useIds();
|
|
112
|
+
const lastUpdated = bridge.useLastUpdated();
|
|
113
|
+
const [, forceUpdate] = useReducer(x => x + 1, 0);
|
|
101
114
|
|
|
102
115
|
useEffect(() => {
|
|
103
|
-
let timeout:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
|
|
119
|
+
if (!lastUpdated || now - lastUpdated > marketcapRefresh) {
|
|
120
|
+
bridge.setLoading(true);
|
|
121
|
+
api.fetchIdsSortedByMarketcap().then(
|
|
122
|
+
fetchedIds => {
|
|
123
|
+
bridge.setIds(fetchedIds);
|
|
124
|
+
timeout = setTimeout(() => forceUpdate(), marketcapRefresh);
|
|
125
|
+
},
|
|
126
|
+
error => {
|
|
127
|
+
log("countervalues", "error fetching marketcap ids " + error);
|
|
128
|
+
bridge.setError(error.message);
|
|
129
|
+
timeout = setTimeout(() => forceUpdate(), marketcapRefreshOnError);
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
} else {
|
|
133
|
+
timeout = setTimeout(() => forceUpdate(), marketcapRefresh - (now - lastUpdated));
|
|
134
|
+
}
|
|
135
|
+
|
|
114
136
|
return () => {
|
|
115
|
-
if (timeout)
|
|
116
|
-
clearTimeout(timeout);
|
|
117
|
-
}
|
|
137
|
+
if (timeout) clearTimeout(timeout);
|
|
118
138
|
};
|
|
119
|
-
}, [
|
|
139
|
+
}, [lastUpdated, bridge]);
|
|
120
140
|
|
|
121
|
-
return ids;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// infer the tracking pairs for the top coins that the portfolio needs to display itself
|
|
125
|
-
// if startDate is undefined, the feature is disabled
|
|
126
|
-
export function useTrackingPairsForTopCoins(
|
|
127
|
-
marketcapIds: string[],
|
|
128
|
-
countervalue: Currency,
|
|
129
|
-
size: number,
|
|
130
|
-
startDate: Date | undefined,
|
|
131
|
-
) {
|
|
132
|
-
const dateTimestamp = startDate?.getTime();
|
|
133
|
-
return useMemo(
|
|
134
|
-
() =>
|
|
135
|
-
dateTimestamp
|
|
136
|
-
? trackingPairForTopCoins(marketcapIds, size, countervalue, new Date(dateTimestamp))
|
|
137
|
-
: [],
|
|
138
|
-
[marketcapIds, countervalue, dateTimestamp, size],
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function useTrackingPairForAccounts(
|
|
143
|
-
accounts: Account[],
|
|
144
|
-
countervalue: Currency,
|
|
145
|
-
): TrackingPair[] {
|
|
146
|
-
// first we cache the tracking pairs with its hash
|
|
147
|
-
const c = useMemo(() => {
|
|
148
|
-
const pairs = inferTrackingPairForAccounts(accounts, countervalue);
|
|
149
|
-
return { pairs, hash: trackingPairsHash(pairs) };
|
|
150
|
-
}, [accounts, countervalue]);
|
|
151
|
-
// we only want to return the pairs when the hash changes
|
|
152
|
-
// to not recalculate pairs as fast as accounts resynchronizes
|
|
153
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
154
|
-
return useMemo(() => c.pairs, [c.hash]);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function CountervaluesMarketcap({ children }: { children: React.ReactNode }): ReactElement {
|
|
158
|
-
const marketcapIds = useMarketcap();
|
|
159
141
|
return (
|
|
160
|
-
<CountervaluesMarketcapIdsContext.Provider value={
|
|
142
|
+
<CountervaluesMarketcapIdsContext.Provider value={ids}>
|
|
161
143
|
{children}
|
|
162
144
|
</CountervaluesMarketcapIdsContext.Provider>
|
|
163
145
|
);
|
|
164
146
|
}
|
|
165
147
|
|
|
166
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Root countervalues provider (polling + calculation).
|
|
150
|
+
*/
|
|
151
|
+
export function CountervaluesProvider({
|
|
167
152
|
children,
|
|
168
153
|
userSettings,
|
|
169
154
|
pollInitDelay = 3 * 1000,
|
|
@@ -174,18 +159,20 @@ export function Countervalues({
|
|
|
174
159
|
const debouncedUserSettings = useDebounce(userSettings, debounceDelay);
|
|
175
160
|
const [{ state, pending, error }, dispatch] = useReducer(fetchReducer, initialFetchState);
|
|
176
161
|
|
|
162
|
+
// TODO this is always using the initial value, doesn't react to changes.
|
|
177
163
|
const marketcapIds = useContext(CountervaluesMarketcapIdsContext);
|
|
178
|
-
|
|
179
164
|
const { marketCapBatchingAfterRank } = userSettings;
|
|
180
|
-
|
|
181
|
-
|
|
165
|
+
|
|
166
|
+
const batchStrategySolver = useMemo(
|
|
167
|
+
() => ({
|
|
182
168
|
shouldBatchCurrencyFrom: (currency: Currency) => {
|
|
183
169
|
if (currency.type === "FiatCurrency") return false;
|
|
184
170
|
const i = marketcapIds.indexOf(currency.id);
|
|
185
171
|
return i === -1 || i > marketCapBatchingAfterRank;
|
|
186
172
|
},
|
|
187
|
-
}
|
|
188
|
-
|
|
173
|
+
}),
|
|
174
|
+
[marketCapBatchingAfterRank, marketcapIds],
|
|
175
|
+
);
|
|
189
176
|
|
|
190
177
|
// flag used to trigger a loadCountervalues
|
|
191
178
|
const [triggerLoad, setTriggerLoad] = useState(false);
|
|
@@ -198,28 +185,15 @@ export function Countervalues({
|
|
|
198
185
|
useEffect(() => {
|
|
199
186
|
if (pending || !triggerLoad) return;
|
|
200
187
|
setTriggerLoad(false);
|
|
201
|
-
dispatch({
|
|
202
|
-
type: "pending",
|
|
203
|
-
});
|
|
204
|
-
|
|
188
|
+
dispatch({ type: "pending" });
|
|
205
189
|
loadCountervalues(
|
|
206
190
|
state,
|
|
207
191
|
userSettings,
|
|
208
192
|
batchStrategySolver,
|
|
209
193
|
userSettings.granularitiesRates,
|
|
210
194
|
).then(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
type: "success",
|
|
214
|
-
payload: state,
|
|
215
|
-
});
|
|
216
|
-
},
|
|
217
|
-
error => {
|
|
218
|
-
dispatch({
|
|
219
|
-
type: "error",
|
|
220
|
-
payload: error,
|
|
221
|
-
});
|
|
222
|
-
},
|
|
195
|
+
newState => dispatch({ type: "success", payload: newState }),
|
|
196
|
+
e => dispatch({ type: "error", payload: e }),
|
|
223
197
|
);
|
|
224
198
|
}, [pending, state, userSettings, triggerLoad, batchStrategySolver]);
|
|
225
199
|
|
|
@@ -229,31 +203,25 @@ export function Countervalues({
|
|
|
229
203
|
dispatch({
|
|
230
204
|
type: "setCounterValueState",
|
|
231
205
|
payload: importCountervalues(savedState, userSettings),
|
|
232
|
-
});
|
|
233
|
-
}, [savedState]);
|
|
206
|
+
});
|
|
207
|
+
}, [savedState, userSettings]);
|
|
234
208
|
|
|
235
209
|
// manage the auto polling loop and the interface for user land to trigger a reload
|
|
236
210
|
const [isPolling, setIsPolling] = useState(true);
|
|
237
211
|
useEffect(() => {
|
|
238
212
|
if (!isPolling) return;
|
|
239
|
-
let pollingTimeout:
|
|
240
|
-
|
|
213
|
+
let pollingTimeout: ReturnType<typeof setTimeout>;
|
|
241
214
|
function pollingLoop() {
|
|
242
215
|
setTriggerLoad(true);
|
|
243
216
|
pollingTimeout = setTimeout(pollingLoop, autopollInterval);
|
|
244
217
|
}
|
|
245
|
-
|
|
246
218
|
pollingTimeout = setTimeout(pollingLoop, pollInitDelay);
|
|
247
219
|
return () => clearTimeout(pollingTimeout);
|
|
248
220
|
}, [autopollInterval, pollInitDelay, isPolling]);
|
|
249
221
|
|
|
250
222
|
const polling = useMemo<Polling>(
|
|
251
223
|
() => ({
|
|
252
|
-
wipe: () => {
|
|
253
|
-
dispatch({
|
|
254
|
-
type: "wipe",
|
|
255
|
-
});
|
|
256
|
-
},
|
|
224
|
+
wipe: () => dispatch({ type: "wipe" }),
|
|
257
225
|
poll: () => setTriggerLoad(true),
|
|
258
226
|
start: () => setIsPolling(true),
|
|
259
227
|
stop: () => setIsPolling(false),
|
|
@@ -273,65 +241,42 @@ export function Countervalues({
|
|
|
273
241
|
}
|
|
274
242
|
|
|
275
243
|
type Action =
|
|
276
|
-
| {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
| {
|
|
281
|
-
type: "error";
|
|
282
|
-
payload: Error;
|
|
283
|
-
}
|
|
284
|
-
| {
|
|
285
|
-
type: "pending";
|
|
286
|
-
}
|
|
287
|
-
| {
|
|
288
|
-
type: "wipe";
|
|
289
|
-
}
|
|
290
|
-
| {
|
|
291
|
-
type: "setCounterValueState";
|
|
292
|
-
payload: CounterValuesState;
|
|
293
|
-
};
|
|
244
|
+
| { type: "success"; payload: CounterValuesState }
|
|
245
|
+
| { type: "error"; payload: Error }
|
|
246
|
+
| { type: "pending" }
|
|
247
|
+
| { type: "wipe" }
|
|
248
|
+
| { type: "setCounterValueState"; payload: CounterValuesState };
|
|
294
249
|
|
|
295
|
-
type FetchState = {
|
|
296
|
-
|
|
297
|
-
pending: boolean;
|
|
298
|
-
error?: Error;
|
|
299
|
-
};
|
|
300
|
-
const initialFetchState: FetchState = {
|
|
301
|
-
state: initialState,
|
|
302
|
-
pending: false,
|
|
303
|
-
};
|
|
250
|
+
type FetchState = { state: CounterValuesState; pending: boolean; error?: Error };
|
|
251
|
+
const initialFetchState: FetchState = { state: initialState, pending: false };
|
|
304
252
|
|
|
305
253
|
function fetchReducer(state: FetchState, action: Action): FetchState {
|
|
306
254
|
switch (action.type) {
|
|
307
255
|
case "success":
|
|
308
|
-
return {
|
|
309
|
-
state: action.payload,
|
|
310
|
-
pending: false,
|
|
311
|
-
error: undefined,
|
|
312
|
-
};
|
|
313
|
-
|
|
256
|
+
return { state: action.payload, pending: false, error: undefined };
|
|
314
257
|
case "error":
|
|
315
258
|
return { ...state, pending: false, error: action.payload };
|
|
316
|
-
|
|
317
259
|
case "pending":
|
|
318
260
|
return { ...state, pending: true, error: undefined };
|
|
319
|
-
|
|
320
261
|
case "wipe":
|
|
321
|
-
return {
|
|
322
|
-
state: initialState,
|
|
323
|
-
pending: false,
|
|
324
|
-
error: undefined,
|
|
325
|
-
};
|
|
326
|
-
|
|
262
|
+
return { state: initialState, pending: false, error: undefined };
|
|
327
263
|
case "setCounterValueState":
|
|
328
264
|
return { ...state, state: action.payload };
|
|
329
|
-
|
|
330
265
|
default:
|
|
331
266
|
return state;
|
|
332
267
|
}
|
|
333
268
|
}
|
|
334
269
|
|
|
270
|
+
/** Returns market-cap ids. */
|
|
271
|
+
export function useMarketcapIds(): string[] {
|
|
272
|
+
return useContext(CountervaluesMarketcapIdsContext);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Returns the full countervalues state. */
|
|
276
|
+
export function useCountervaluesState(): CounterValuesState {
|
|
277
|
+
return useContext(CountervaluesContext);
|
|
278
|
+
}
|
|
279
|
+
|
|
335
280
|
// allows consumer to access the countervalues polling control object
|
|
336
281
|
export function useCountervaluesPolling(): Polling {
|
|
337
282
|
return useContext(CountervaluesPollingContext);
|
|
@@ -342,19 +287,9 @@ export function useCountervaluesUserSettingsContext(): CountervaluesSettings {
|
|
|
342
287
|
return useContext(CountervaluesUserSettingsContext);
|
|
343
288
|
}
|
|
344
289
|
|
|
345
|
-
// allows consumer to access the countervalues state
|
|
346
|
-
export function useCountervaluesState(): CounterValuesState {
|
|
347
|
-
return useContext(CountervaluesContext);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// allows consumer to access the coins ids sorted by marketcap. It's basically all the coins that the API supports.
|
|
351
|
-
export function useMarketcapIds(): string[] {
|
|
352
|
-
return useContext(CountervaluesMarketcapIdsContext);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
290
|
// provides an export of the countervalues state
|
|
356
291
|
export function useCountervaluesExport(): CounterValuesStateRaw {
|
|
357
|
-
const state =
|
|
292
|
+
const state = useCountervaluesState();
|
|
358
293
|
return useMemo(() => exportCountervalues(state), [state]);
|
|
359
294
|
}
|
|
360
295
|
|
|
@@ -379,7 +314,7 @@ export function useCalculateCountervalueCallback({
|
|
|
379
314
|
}): (from: Currency, value: BigNumber) => BigNumber | null | undefined {
|
|
380
315
|
const state = useCountervaluesState();
|
|
381
316
|
return useCallback(
|
|
382
|
-
(from: Currency, value: BigNumber)
|
|
317
|
+
(from: Currency, value: BigNumber) => {
|
|
383
318
|
const countervalue = calculate(state, {
|
|
384
319
|
value: value.toNumber(),
|
|
385
320
|
from,
|
|
@@ -392,6 +327,7 @@ export function useCalculateCountervalueCallback({
|
|
|
392
327
|
);
|
|
393
328
|
}
|
|
394
329
|
|
|
330
|
+
/** Helper for send-flow: returns fiat amount and reverse calculation. */
|
|
395
331
|
export function useSendAmount({
|
|
396
332
|
account,
|
|
397
333
|
fiatCurrency,
|
|
@@ -416,22 +352,49 @@ export function useSendAmount({
|
|
|
416
352
|
const fiatUnit = fiatCurrency.units[0];
|
|
417
353
|
const state = useCountervaluesState();
|
|
418
354
|
const calculateCryptoAmount = useCallback(
|
|
419
|
-
(fiatAmount: BigNumber) =>
|
|
420
|
-
|
|
355
|
+
(fiatAmount: BigNumber) =>
|
|
356
|
+
new BigNumber(
|
|
421
357
|
calculate(state, {
|
|
422
358
|
from: cryptoCurrency,
|
|
423
359
|
to: fiatCurrency,
|
|
424
360
|
value: fiatAmount.toNumber(),
|
|
425
361
|
reverse: true,
|
|
426
362
|
}) ?? 0,
|
|
427
|
-
)
|
|
428
|
-
return cryptoAmount;
|
|
429
|
-
},
|
|
363
|
+
),
|
|
430
364
|
[state, cryptoCurrency, fiatCurrency],
|
|
431
365
|
);
|
|
432
|
-
return {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
366
|
+
return { fiatAmount, fiatUnit, calculateCryptoAmount };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// infer the tracking pairs for the top coins that the portfolio needs to display itself
|
|
370
|
+
// if startDate is undefined, the feature is disabled
|
|
371
|
+
export function useTrackingPairsForTopCoins(
|
|
372
|
+
marketcapIds: string[],
|
|
373
|
+
countervalue: Currency,
|
|
374
|
+
size: number,
|
|
375
|
+
startDate: Date | undefined,
|
|
376
|
+
) {
|
|
377
|
+
const dateTimestamp = startDate?.getTime();
|
|
378
|
+
return useMemo(
|
|
379
|
+
() =>
|
|
380
|
+
dateTimestamp
|
|
381
|
+
? trackingPairForTopCoins(marketcapIds, size, countervalue, new Date(dateTimestamp))
|
|
382
|
+
: [],
|
|
383
|
+
[marketcapIds, countervalue, dateTimestamp, size],
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function useTrackingPairForAccounts(
|
|
388
|
+
accounts: Account[],
|
|
389
|
+
countervalue: Currency,
|
|
390
|
+
): TrackingPair[] {
|
|
391
|
+
// first we cache the tracking pairs with its hash
|
|
392
|
+
const c = useMemo(() => {
|
|
393
|
+
const pairs = inferTrackingPairForAccounts(accounts, countervalue);
|
|
394
|
+
return { pairs, hash: trackingPairsHash(pairs) };
|
|
395
|
+
}, [accounts, countervalue]);
|
|
396
|
+
// we only want to return the pairs when the hash changes
|
|
397
|
+
// to not recalculate pairs as fast as accounts resynchronizes
|
|
398
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
399
|
+
return useMemo(() => c.pairs, [c.hash]);
|
|
437
400
|
}
|