@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
86 lines • 4.56 kB
JavaScript
import { useState, useEffect, useCallback } from "react";
import isEqual from "lodash/isEqual";
import { DeviceOnboardingStatePollingError } from "@ledgerhq/errors";
import { getOnboardingStatePolling as defaultGetOnboardingStatePolling } from "../../hw/getOnboardingStatePolling";
/**
* Polls the current device onboarding state, and notify the hook consumer of
* any allowed errors and fatal errors
* @param getOnboardingStatePolling A polling function, by default set to live-common/hw/getOnboardingStatePolling.
* This dependency injection is needed for LLD to have the polling working on the internal thread
* @param device A Device object
* @param pollingPeriodMs The period in ms after which the device onboarding state is fetched again
* @param stopPolling Flag to stop or continue the polling
* @returns An object containing:
* - onboardingState: the device state during the onboarding
* - allowedError: any error that is allowed and does not stop the polling
* - fatalError: any error that is fatal and stops the polling
* - lockedDevice: a boolean set to true if the device is currently locked, false otherwise
* - resetStates: a function to reset the values of: onboardingState, allowedError, fatalError and lockedDevice
*/
export const useOnboardingStatePolling = ({ getOnboardingStatePolling = defaultGetOnboardingStatePolling, device, pollingPeriodMs, stopPolling = false, allowedErrorChecks = [], }) => {
const [onboardingState, setOnboardingState] = useState(null);
const [allowedError, setAllowedError] = useState(null);
const [fatalError, setFatalError] = useState(null);
const [lockedDevice, setLockedDevice] = useState(false);
useEffect(() => {
let onboardingStatePollingSubscription;
// If stopPolling is updated and set to true, the useEffect hook will call its
// cleanup function (return) and the polling won't restart with the below condition
if (device && !stopPolling) {
onboardingStatePollingSubscription = getOnboardingStatePolling({
deviceId: device.deviceId,
pollingPeriodMs,
allowedErrorChecks,
}).subscribe({
next: (onboardingStatePollingResult) => {
if (onboardingStatePollingResult) {
setFatalError(null);
setLockedDevice(onboardingStatePollingResult.lockedDevice);
// Only updates if the new allowedError is different
setAllowedError(prevAllowedError => getNewAllowedErrorIfChanged(prevAllowedError, onboardingStatePollingResult.allowedError));
// Does not update the onboarding state if an allowed error occurred
if (!onboardingStatePollingResult.allowedError) {
// Only updates if the new onboardingState is different
setOnboardingState(prevOnboardingState => getNewOnboardingStateIfChanged(prevOnboardingState, onboardingStatePollingResult.onboardingState));
}
}
},
error: error => {
setAllowedError(null);
setLockedDevice(false);
setFatalError(error instanceof Error
? error
: new DeviceOnboardingStatePollingError(`Error from: ${error?.name ?? error} ${error?.message}`));
},
});
}
return () => {
onboardingStatePollingSubscription?.unsubscribe();
};
}, [
device,
pollingPeriodMs,
setOnboardingState,
setAllowedError,
setFatalError,
stopPolling,
getOnboardingStatePolling,
]);
const resetStates = useCallback(() => {
setOnboardingState(null);
setAllowedError(null);
setFatalError(null);
setLockedDevice(false);
}, []);
return { onboardingState, allowedError, fatalError, lockedDevice, resetStates };
};
const getNewOnboardingStateIfChanged = (prevOnboardingState, newOnboardingState) => {
return isEqual(prevOnboardingState, newOnboardingState)
? prevOnboardingState
: newOnboardingState;
};
const getNewAllowedErrorIfChanged = (prevError, newError) => {
// Only interested if the errors are instances of the same Error class
return prevError?.constructor === newError?.constructor ? prevError : newError;
};
//# sourceMappingURL=useOnboardingStatePolling.js.map