UNPKG

@ledgerhq/live-common

Version:
174 lines (159 loc) 4.51 kB
import { Observable, of, concat } from "rxjs"; import { scan, tap } from "rxjs/operators"; import { useEffect, useState } from "react"; import type { Action, Device } from "./types"; import type { AppState } from "./app"; import type { DeviceModelId } from "@ledgerhq/devices"; import { log } from "@ledgerhq/logs"; import { Exchange } from "../../exchange/types"; import { Transaction } from "../../generated/types"; type State = { completeExchangeResult: Transaction | null | undefined; completeExchangeError: Error | null | undefined; freezeReduxDevice: boolean; completeExchangeRequested: boolean; isLoading: boolean; estimatedFees: string | undefined; }; type CompleteExchangeState = AppState & State; type CompleteExchangeRequest = { deviceModelId?: DeviceModelId; deviceId?: string; provider: string; transaction: Transaction; binaryPayload: string; signature: string; exchange: Exchange; exchangeType: number; rateType?: number; swapId?: string; rate?: number; amountExpectedTo?: number; }; type Result = | { completeExchangeResult: Transaction; } | { completeExchangeError: Error; }; type CompleteExchangeAction = Action<CompleteExchangeRequest, CompleteExchangeState, Result>; export type ExchangeRequestEvent = | { type: "complete-exchange" } | { type: "complete-exchange-requested"; estimatedFees: string } | { type: "complete-exchange-error"; error: Error } | { type: "complete-exchange-result"; completeExchangeResult: Transaction }; const mapResult = ({ completeExchangeResult, completeExchangeError, }: CompleteExchangeState): Result | null | undefined => completeExchangeResult ? { completeExchangeResult, } : completeExchangeError ? { completeExchangeError, } : null; const initialState: State = { completeExchangeResult: null, completeExchangeError: null, completeExchangeRequested: false, freezeReduxDevice: false, isLoading: true, estimatedFees: undefined, }; const reducer = (state: State, e: ExchangeRequestEvent) => { switch (e.type) { case "complete-exchange": return { ...state, completeExchangeStarted: true, freezeReduxDevice: true, }; case "complete-exchange-error": return { ...state, completeExchangeError: e.error, isLoading: false, }; case "complete-exchange-requested": return { ...state, estimatedFees: e.estimatedFees, isLoading: false, }; case "complete-exchange-result": return { ...state, completeExchangeResult: e.completeExchangeResult, isLoading: false, }; } return state; }; function useFrozenValue<T>(value: T, frozen: boolean): T { const [state, setState] = useState(value); useEffect(() => { if (!frozen) { setState(value); } }, [value, frozen]); return state; } export const createAction = ( completeExchangeExec: (arg0: CompleteExchangeRequest) => Observable<ExchangeRequestEvent>, ): CompleteExchangeAction => { const useHook = ( reduxDevice: Device | null | undefined, completeExchangeRequest: CompleteExchangeRequest, ): CompleteExchangeState => { const [state, setState] = useState(initialState); const reduxDeviceFrozen = useFrozenValue(reduxDevice, state?.freezeReduxDevice); const { provider, transaction, binaryPayload, signature, exchange, exchangeType, rateType } = completeExchangeRequest; useEffect(() => { const sub = concat( of(<ExchangeRequestEvent>{ type: "complete-exchange", }), completeExchangeExec({ deviceId: reduxDeviceFrozen?.deviceId, deviceModelId: reduxDeviceFrozen?.modelId, provider, transaction, binaryPayload, signature, exchange, exchangeType, rateType, }), ) .pipe( tap(e => { log("actions-completeExchange-event", JSON.stringify(e)); }), scan(reducer, initialState), ) .subscribe(setState); return () => { sub.unsubscribe(); }; }, [ provider, transaction, binaryPayload, signature, exchange, exchangeType, rateType, reduxDeviceFrozen, ]); return { ...state, device: reduxDeviceFrozen } as CompleteExchangeState; }; return { useHook, mapResult, }; };