robosats/frontend/src/contexts/GarageContext.tsx

163 lines
4.8 KiB
TypeScript
Raw Normal View History

2024-01-12 21:05:15 +00:00
import React, {
createContext,
type Dispatch,
useState,
type SetStateAction,
useEffect,
2024-01-15 09:33:47 +00:00
type ReactNode,
2024-08-15 14:26:04 +00:00
useContext,
useRef,
2024-01-12 21:05:15 +00:00
} from 'react';
import { defaultMaker, type Maker, Garage } from '../models';
2023-11-26 18:00:50 +00:00
import { systemClient } from '../services/System';
2024-08-15 14:26:04 +00:00
import { type UseAppStoreType, AppContext } from './AppContext';
import { type UseFederationStoreType, FederationContext } from './FederationContext';
2024-01-12 21:05:15 +00:00
export interface GarageContextProviderProps {
children: ReactNode;
}
export interface UseGarageStoreType {
garage: Garage;
maker: Maker;
setMaker: Dispatch<SetStateAction<Maker>>;
2024-08-15 14:26:04 +00:00
setDelay: Dispatch<SetStateAction<number>>;
fetchSlotActiveOrder: () => void;
slotUpdatedAt: string;
}
export const initialGarageContext: UseGarageStoreType = {
garage: new Garage(),
maker: defaultMaker,
setMaker: () => {},
2024-08-15 14:26:04 +00:00
setDelay: () => {},
fetchSlotActiveOrder: () => {},
slotUpdatedAt: '',
};
2024-08-15 14:26:04 +00:00
const defaultDelay = 5000;
// Refresh delays (ms) according to Order status
const statusToDelay = [
3000, // 'Waiting for maker bond'
35000, // 'Public'
180000, // 'Paused'
3000, // 'Waiting for taker bond'
999999, // 'Cancelled'
999999, // 'Expired'
8000, // 'Waiting for trade collateral and buyer invoice'
8000, // 'Waiting only for seller trade collateral'
8000, // 'Waiting only for buyer invoice'
10000, // 'Sending fiat - In chatroom'
10000, // 'Fiat sent - In chatroom'
100000, // 'In dispute'
999999, // 'Collaboratively cancelled'
10000, // 'Sending satoshis to buyer'
60000, // 'Sucessful trade'
30000, // 'Failed lightning network routing'
300000, // 'Wait for dispute resolution'
300000, // 'Maker lost dispute'
300000, // 'Taker lost dispute'
];
export const GarageContext = createContext<UseGarageStoreType>(initialGarageContext);
2024-01-12 21:05:15 +00:00
export const GarageContextProvider = ({ children }: GarageContextProviderProps): JSX.Element => {
// All garage data structured
2024-08-15 14:26:04 +00:00
const { settings, torStatus, open, page } = useContext<UseAppStoreType>(AppContext);
const pageRef = useRef(page);
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext);
const [garage] = useState<Garage>(initialGarageContext.garage);
const [maker, setMaker] = useState<Maker>(initialGarageContext.maker);
2024-08-15 14:26:04 +00:00
const [slotUpdatedAt, setSlotUpdatedAt] = useState<string>(new Date().toISOString());
const [lastOrderCheckAt] = useState<number>(+new Date());
const lastOrderCheckAtRef = useRef(lastOrderCheckAt);
const [delay, setDelay] = useState<number>(defaultDelay);
const [timer, setTimer] = useState<NodeJS.Timer | undefined>(() =>
setInterval(() => null, delay),
);
2024-08-15 14:26:04 +00:00
const onSlotUpdated = (): void => {
setSlotUpdatedAt(new Date().toISOString());
};
useEffect(() => {
2024-08-15 14:26:04 +00:00
setMaker((maker) => {
return { ...maker, coordinator: sortedCoordinators[0] };
}); // default MakerForm coordinator is decided via sorted lottery
garage.registerHook('onSlotUpdate', onSlotUpdated);
clearInterval(timer);
fetchSlotActiveOrder();
return () => clearTimeout(timer);
}, []);
2024-08-15 14:26:04 +00:00
useEffect(() => {
if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) {
const token = garage.getSlot()?.token;
if (token) void garage.fetchRobot(federation, token);
}
}, [settings.network, settings.useProxy, torStatus]);
2023-11-26 18:00:50 +00:00
useEffect(() => {
if (window.NativeRobosats !== undefined && !systemClient.loading) {
garage.loadSlots();
}
}, [systemClient.loading]);
2024-08-15 14:26:04 +00:00
useEffect(() => {
pageRef.current = page;
}, [page]);
// use effects to fetchRobots on Profile open
useEffect(() => {
const slot = garage.getSlot();
if (open.profile && slot?.hashId && slot?.token) {
void garage.fetchRobot(federation, slot?.token); // refresh/update existing robot
}
}, [open.profile]);
const fetchSlotActiveOrder: () => void = () => {
const slot = garage?.getSlot();
if (slot?.activeOrder?.id) {
let delay =
slot.activeOrder.status >= 0 && slot.activeOrder.status <= 18
? statusToDelay[slot.activeOrder.status]
: defaultDelay;
if (pageRef.current !== 'order') delay = delay * 5;
if (+new Date() - lastOrderCheckAtRef.current >= delay) {
void slot.fetchActiveOrder(federation).finally(() => {
lastOrderCheckAtRef.current = +new Date();
resetInterval();
});
} else {
resetInterval();
}
} else {
resetInterval();
}
};
const resetInterval = (): void => {
clearInterval(timer);
setDelay(defaultDelay);
setTimer(setTimeout(() => fetchSlotActiveOrder(), defaultDelay));
};
2024-01-12 21:05:15 +00:00
return (
<GarageContext.Provider
value={{
garage,
maker,
setMaker,
2024-08-15 14:26:04 +00:00
setDelay,
fetchSlotActiveOrder,
slotUpdatedAt,
2024-01-12 21:05:15 +00:00
}}
>
{children}
</GarageContext.Provider>
);
};