@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
100 lines • 3.92 kB
JavaScript
import invariant from "invariant";
import { useCallback, useEffect, useRef, useState } from "react";
import { catchError, concatMap, defer, from, map, scan } from "rxjs";
import { log } from "@ledgerhq/logs";
import { createAction as createAppAction } from "../../../../hw/actions/app";
import { withDevice } from "../../../../hw/deviceAccess";
import { viewKeyResolver } from "../../setup";
export const getViewKeyExec = (transport, request) => {
invariant(request.currency, "getViewKey: currency is required");
invariant(request.selectedAccounts.length > 0, "getViewKey: selectedAccounts cannot be empty");
const { selectedAccounts, currency } = request;
const total = selectedAccounts.length;
return from(selectedAccounts).pipe(concatMap(account => {
const { freshAddressPath: path } = account;
const options = { path, currency };
return defer(() => viewKeyResolver(transport, options)).pipe(map(result => {
const viewKey = result.viewKey ? result.viewKey : null;
log("hw", `getViewKey ${currency.id} on ${path}`, result);
return { accountId: account.id, viewKey };
}), catchError(e => {
log("hw", `getViewKey ${currency.id} on ${path} FAILED ${String(e)}`);
throw e;
}));
}), scan((acc, { accountId, viewKey }) => ({
viewKeys: { ...acc.viewKeys, [accountId]: viewKey },
completed: acc.completed + 1,
total,
}), { viewKeys: {}, completed: 0, total }));
};
const initialState = {
error: null,
result: null,
sharePending: false,
shareProgress: {
completed: 0,
total: 0,
viewKeys: {},
},
};
export const createAction = (connectAppExec, getViewKey) => {
const useHook = (reduxDevice, request) => {
const taskFired = useRef(false);
const [state, setState] = useState(initialState);
const appState = createAppAction(connectAppExec).useHook(reduxDevice, {
appName: request.appName,
dependencies: request.dependencies,
});
const { device, opened, inWrongDeviceForAccount, error } = appState;
const handleProgress = useCallback((progress) => {
const isComplete = progress.completed === progress.total;
setState(prev => ({
...prev,
error: null,
result: isComplete ? progress.viewKeys : null,
sharePending: !isComplete,
shareProgress: progress,
}));
}, []);
const handleError = useCallback((error) => {
setState(prev => ({
...prev,
error,
sharePending: false,
}));
taskFired.current = false;
}, []);
useEffect(() => {
if (!device || !opened || inWrongDeviceForAccount || error || taskFired.current) {
return;
}
taskFired.current = true;
setState({
...initialState,
sharePending: true,
shareProgress: {
completed: 0,
total: request.selectedAccounts.length,
viewKeys: {},
},
});
const subscription = withDevice(device.deviceId)(transport => getViewKey(transport, request)).subscribe({
next: handleProgress,
error: handleError,
});
return () => {
subscription.unsubscribe();
};
}, [device, opened, inWrongDeviceForAccount, error, request, handleProgress, handleError]);
return {
...appState,
...state,
error: appState.error || state.error,
};
};
return {
useHook,
mapResult: (state) => state.result,
};
};
//# sourceMappingURL=index.js.map