@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
126 lines (106 loc) • 4.61 kB
text/typescript
import type { Dispatch, MutableRefObject, SetStateAction } from "react";
import type { BigNumber } from "bignumber.js";
import type { Unit } from "@ledgerhq/types-cryptoassets";
import { formatAmountForInput, formatFiatForInput, shouldSyncInput } from "./amountInput";
type SyncAmountInputsParams = {
cryptoAmount: BigNumber;
fiatAmount: BigNumber;
transactionUseAllAmount: boolean;
inputMode: "fiat" | "crypto";
cryptoInputValue: string;
fiatInputValue: string;
locale: string;
accountUnit: Unit;
fiatUnit: Unit;
lastTransactionAmountRef: MutableRefObject<BigNumber | null>;
lastFiatAmountRef: MutableRefObject<BigNumber | null>;
lastUseAllAmountRef: MutableRefObject<boolean>;
lastUserInputTimeRef: MutableRefObject<number>;
setCryptoInputValue: Dispatch<SetStateAction<string>>;
setFiatInputValue: Dispatch<SetStateAction<string>>;
};
type SyncFlags = {
useAllAmountChanged: boolean;
cryptoAmountChanged: boolean;
fiatAmountChanged: boolean;
canSyncWithBridge: boolean;
isQuickAction: boolean;
inputsEmptyButHaveAmount: boolean;
};
const INPUT_SYNC_DELAY_MS = 200;
const shouldRunCryptoSync = ({
cryptoAmountChanged,
canSyncWithBridge,
inputsEmptyButHaveAmount,
}: SyncFlags) => (cryptoAmountChanged && canSyncWithBridge) || inputsEmptyButHaveAmount;
const shouldRunFiatSync = ({
cryptoAmountChanged,
fiatAmountChanged,
canSyncWithBridge,
inputsEmptyButHaveAmount,
}: SyncFlags) =>
((cryptoAmountChanged || fiatAmountChanged) && canSyncWithBridge) || inputsEmptyButHaveAmount;
function syncCryptoInput(params: SyncAmountInputsParams, flags: SyncFlags): void {
if (!shouldRunCryptoSync(flags)) return;
params.lastTransactionAmountRef.current = params.cryptoAmount;
const shouldSyncCrypto = shouldSyncInput({
isQuickAction: flags.isQuickAction,
useAllAmountChanged: flags.useAllAmountChanged,
isActiveInput: params.inputMode === "crypto",
hasInputValue: params.cryptoInputValue.length > 0,
});
if (shouldSyncCrypto || flags.inputsEmptyButHaveAmount) {
const formatted = formatAmountForInput(params.accountUnit, params.cryptoAmount, params.locale);
params.setCryptoInputValue(formatted);
}
}
function syncFiatInput(params: SyncAmountInputsParams, flags: SyncFlags): void {
if (!shouldRunFiatSync(flags)) return;
params.lastFiatAmountRef.current = params.fiatAmount;
const shouldSyncFiat = shouldSyncInput({
isQuickAction: flags.isQuickAction,
useAllAmountChanged: flags.useAllAmountChanged,
isActiveInput: params.inputMode === "fiat",
hasInputValue: params.fiatInputValue.length > 0,
});
if (shouldSyncFiat || flags.inputsEmptyButHaveAmount) {
const formatted = formatFiatForInput(params.fiatUnit, params.fiatAmount, params.locale);
params.setFiatInputValue(formatted);
}
}
export function syncAmountInputs(params: SyncAmountInputsParams): void {
const useAllAmountChanged = params.transactionUseAllAmount !== params.lastUseAllAmountRef.current;
const lastCrypto = params.lastTransactionAmountRef.current;
const lastFiat = params.lastFiatAmountRef.current;
const cryptoAmountChanged =
lastCrypto === null || !params.cryptoAmount.eq(lastCrypto) || useAllAmountChanged;
const fiatAmountChanged = lastFiat === null || !params.fiatAmount.eq(lastFiat);
const timeSinceUserInput = Date.now() - (params.lastUserInputTimeRef.current ?? 0);
// When useAllAmount is active and amount changes, always sync immediately
// This handles fee changes that affect the max amount (e.g., switching from medium to fast fees)
const isUseAllAmountActive = params.transactionUseAllAmount && cryptoAmountChanged;
const canSyncWithBridge =
useAllAmountChanged || isUseAllAmountActive || timeSinceUserInput > INPUT_SYNC_DELAY_MS;
const isQuickAction = params.lastUserInputTimeRef.current === 0;
// After remount (e.g. back from CustomFees), repopulate inputs explicitly
const inputsEmptyButHaveAmount =
params.cryptoAmount.gt(0) &&
params.cryptoInputValue.length === 0 &&
params.fiatInputValue.length === 0 &&
canSyncWithBridge;
const flags: SyncFlags = {
useAllAmountChanged,
cryptoAmountChanged,
fiatAmountChanged,
canSyncWithBridge,
isQuickAction,
inputsEmptyButHaveAmount,
};
// Consume useAllAmount toggle so it doesn't stay true across renders and bypass typing-delay guards
if (flags.useAllAmountChanged) {
params.lastUseAllAmountRef.current = params.transactionUseAllAmount;
params.lastUserInputTimeRef.current = 0;
}
syncCryptoInput(params, flags);
syncFiatInput(params, flags);
}