@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
74 lines • 2.95 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SYNC_SETTLE_GUARD_MS = void 0;
exports.useSyncLifecycle = useSyncLifecycle;
const react_1 = require("react");
function syncPhaseReducer(state, action) {
switch (action.type) {
case "SYNC_BEGIN":
return "syncing";
case "SYNC_COMPLETE":
return action.hasError ? "failed" : "synced";
default:
return state;
}
}
function getInitialSyncPhase(isBalanceLoading, hasAnySyncError) {
if (isBalanceLoading)
return "syncing";
if (hasAnySyncError)
return "failed";
return "synced";
}
/**
* After `isSyncSettled` becomes true while leaving a syncing cycle, wait this
* long before committing the phase. If `isSyncSettled` bounces back to false
* during this window (sub-account discovery, retry, etc.), the timer resets.
* This prevents the syncing → synced → failed flash.
*/
exports.SYNC_SETTLE_GUARD_MS = 3_000;
/**
* FSM driving the TopBar indicator and balance shimmer.
*
* Phases: syncing -> synced | failed.
*
* SYNC_BEGIN fires on cold start / manual refresh.
* SYNC_COMPLETE fires on settle, deferred by SYNC_SETTLE_GUARD_MS
* when leaving syncing to absorb bouncing settle states.
*/
function useSyncLifecycle(isBalanceLoading, stableSyncPending, hasAnySyncError) {
const isSyncSettled = !isBalanceLoading && !stableSyncPending;
const hasAnySyncErrorRef = (0, react_1.useRef)(hasAnySyncError);
hasAnySyncErrorRef.current = hasAnySyncError;
const prevSettledRef = (0, react_1.useRef)(isSyncSettled);
const wasSyncingRef = (0, react_1.useRef)(!isSyncSettled);
const [phase, dispatch] = (0, react_1.useReducer)(syncPhaseReducer, getInitialSyncPhase(isBalanceLoading, hasAnySyncError));
(0, react_1.useEffect)(() => {
if (isBalanceLoading) {
wasSyncingRef.current = true;
dispatch({ type: "SYNC_BEGIN" });
}
}, [isBalanceLoading]);
(0, react_1.useEffect)(() => {
const wasSettled = prevSettledRef.current;
prevSettledRef.current = isSyncSettled;
if (!isSyncSettled || wasSettled)
return;
if (wasSyncingRef.current) {
if (hasAnySyncErrorRef.current) {
wasSyncingRef.current = false;
dispatch({ type: "SYNC_COMPLETE", hasError: true });
return;
}
const timer = setTimeout(() => {
wasSyncingRef.current = false;
dispatch({ type: "SYNC_COMPLETE", hasError: hasAnySyncErrorRef.current });
}, exports.SYNC_SETTLE_GUARD_MS);
return () => clearTimeout(timer);
}
dispatch({ type: "SYNC_COMPLETE", hasError: hasAnySyncErrorRef.current });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSyncSettled]);
return phase;
}
//# sourceMappingURL=useSyncLifecycle.js.map