Improve client performance

This commit is contained in:
koalasat
2025-07-10 18:23:45 +02:00
parent fb73c963f0
commit cd031149de
8 changed files with 73 additions and 96 deletions

View File

@ -4,9 +4,14 @@ import FederationTable from '../../components/FederationTable';
import { t } from 'i18next'; import { t } from 'i18next';
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
import { Garage } from '../../models';
import { Origin, Origins } from '../../models/Coordinator.model';
import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
const Coordinators = (): React.JSX.Element => { const Coordinators = (): React.JSX.Element => {
const { federation, addNewCoordinator } = useContext<UseFederationStoreType>(FederationContext); const { settings, origin, hostUrl } = useContext<UseAppStoreType>(AppContext);
const { federation, setFederationUpdatedAt } =
useContext<UseFederationStoreType>(FederationContext);
const { garage } = useContext<UseGarageStoreType>(GarageContext); const { garage } = useContext<UseGarageStoreType>(GarageContext);
const [newAlias, setNewAlias] = useState<string>(''); const [newAlias, setNewAlias] = useState<string>('');
const [newUrl, setNewUrl] = useState<string>(''); const [newUrl, setNewUrl] = useState<string>('');
@ -15,6 +20,34 @@ const Coordinators = (): React.JSX.Element => {
// Regular expression to match a valid .onion URL // Regular expression to match a valid .onion URL
const onionUrlPattern = /^((http|https):\/\/)?[a-zA-Z2-7]{16,56}\.onion$\/?/; const onionUrlPattern = /^((http|https):\/\/)?[a-zA-Z2-7]{16,56}\.onion$\/?/;
const addNewCoordinator: (alias: string, garage: Garage, url: string) => void = (
alias,
garage,
url,
) => {
if (!federation.getCoordinator(alias)) {
const attributes: object = {
longAlias: alias,
shortAlias: alias,
federated: false,
enabled: true,
};
const origins: Origins = {
clearnet: undefined,
onion: url as Origin,
i2p: undefined,
};
if (settings.network === 'mainnet') {
attributes.mainnet = origins;
} else {
attributes.testnet = origins;
}
federation.addCoordinator(origin, settings, hostUrl, attributes);
garage.syncCoordinator(federation, alias);
setFederationUpdatedAt(new Date().toISOString());
}
};
const addCoordinator: () => void = () => { const addCoordinator: () => void = () => {
if (federation.getCoordinator(newAlias)) { if (federation.getCoordinator(newAlias)) {
setError(t('Alias already exists')); setError(t('Alias already exists'));
@ -24,7 +57,7 @@ const Coordinators = (): React.JSX.Element => {
if (!/^((http|https):\/\/)/.test(fullNewUrl)) { if (!/^((http|https):\/\/)/.test(fullNewUrl)) {
fullNewUrl = `http://${newUrl}`; fullNewUrl = `http://${newUrl}`;
} }
addNewCoordinator(newAlias, fullNewUrl); addNewCoordinator(newAlias, garage, fullNewUrl);
garage.syncCoordinator(federation, newAlias); garage.syncCoordinator(federation, newAlias);
setNewAlias(''); setNewAlias('');
setNewUrl(''); setNewUrl('');

View File

@ -343,11 +343,9 @@ const BookTable = ({
flex: 2, flex: 2,
renderCell: (params: { row: PublicOrder }) => { renderCell: (params: { row: PublicOrder }) => {
const currencyCode = String(currencyDict[params.row.currency.toString()]); const currencyCode = String(currencyDict[params.row.currency.toString()]);
const coordinator = const limits = federation.getLimits(params.row.coordinatorShortAlias);
federation.getCoordinator(params.row.coordinatorShortAlias) ??
federation.getCoordinators()[0];
const premium = parseFloat(params.row.premium); const premium = parseFloat(params.row.premium);
const limitPrice = coordinator.limits[params.row.currency.toString()]?.price; const limitPrice = limits[params.row.currency.toString()]?.price;
const price = (limitPrice ?? 1) * (1 + premium / 100); const price = (limitPrice ?? 1) * (1 + premium / 100);
return ( return (
@ -501,16 +499,13 @@ const BookTable = ({
type: 'number', type: 'number',
flex: 1, flex: 1,
renderCell: (params: { row: PublicOrder }) => { renderCell: (params: { row: PublicOrder }) => {
const coordinator = const limits = federation.getLimits(params.row.coordinatorShortAlias);
federation.getCoordinator(params.row.coordinatorShortAlias) ??
federation.getCoordinators()[0];
const amount = const amount =
params.row.has_range === true params.row.has_range === true
? parseFloat(params.row.max_amount) ? parseFloat(params.row.max_amount)
: parseFloat(params.row.amount); : parseFloat(params.row.amount);
const premium = parseFloat(params.row.premium); const premium = parseFloat(params.row.premium);
const price = const price = (limits[params.row.currency.toString()]?.price ?? 1) * (1 + premium / 100);
(coordinator.limits[params.row.currency.toString()]?.price ?? 1) * (1 + premium / 100);
const satoshisNow = (100000000 * amount) / price; const satoshisNow = (100000000 * amount) / price;
return ( return (

View File

@ -388,7 +388,7 @@ const CoordinatorDialog = ({ open = false, onClose, shortAlias }: Props): React.
coordinator.shortAlias, coordinator.shortAlias,
); );
} }
coordinator?.loadInfo(); if (!coordinator.info) coordinator?.loadInfo();
} }
}, [open]); }, [open]);

View File

@ -57,6 +57,7 @@ const FederationTable = ({
const mobile = windowSize.width < 44; const mobile = windowSize.width < 44;
useEffect(() => { useEffect(() => {
federation.loadInfo();
loadRatings(); loadRatings();
}, []); }, []);
@ -240,7 +241,7 @@ const FederationTable = ({
onClickCoordinator(params.row.shortAlias); onClickCoordinator(params.row.shortAlias);
}} }}
> >
{Boolean(params.row.loadingLimits) && Boolean(params.row.enabled) ? ( {Boolean(params.row.loadingInfo) && Boolean(params.row.enabled) ? (
<CircularProgress thickness={0.35 * fontSize} size={1.5 * fontSize} /> <CircularProgress thickness={0.35 * fontSize} size={1.5 * fontSize} />
) : params.row.limits !== undefined ? ( ) : params.row.limits !== undefined ? (
<Link color='success' /> <Link color='success' />

View File

@ -86,31 +86,26 @@ const MakerForm = ({
const amountSafeThresholds = [1.03, 0.98]; const amountSafeThresholds = [1.03, 0.98];
useEffect(() => {
federation
.loadInfo()
.then(() => {})
.catch((error) => {
console.error('Error loading info:', error);
});
}, []);
useEffect(() => { useEffect(() => {
setCurrencyCode(currencyDict[fav.currency === 0 ? 1 : fav.currency]); setCurrencyCode(currencyDict[fav.currency === 0 ? 1 : fav.currency]);
}, [federationUpdatedAt]); }, [federationUpdatedAt]);
useEffect(() => { useEffect(() => {
updateCoordinatorInfo(); updateCoordinatorInfo();
}, [maker.coordinator, federationUpdatedAt]); }, [maker.coordinator]);
const updateCoordinatorInfo = (): void => { const updateCoordinatorInfo = (): void => {
if (maker.coordinator != null) { if (maker.coordinator != null) {
const newLimits = federation.getCoordinator(maker.coordinator)?.limits; const coordinator = federation.getCoordinator(maker.coordinator);
if (newLimits && Object.keys(newLimits).length !== 0) { coordinator.loadInfo();
updateAmountLimits(newLimits, fav.currency, maker.premium); coordinator.loadLimits(() => {
updateCurrentPrice(newLimits, fav.currency, maker.premium); const newLimits = coordinator.limits;
setLimits(newLimits); if (newLimits && Object.keys(newLimits).length !== 0) {
} updateAmountLimits(newLimits, fav.currency, maker.premium);
updateCurrentPrice(newLimits, fav.currency, maker.premium);
setLimits(newLimits);
}
});
} }
}; };

View File

@ -102,8 +102,7 @@ const SelectCoordinator: React.FC<SelectCoordinatorProps> = ({
flipHorizontally={false} flipHorizontally={false}
small={true} small={true}
/> />
{(coordinator?.limits === undefined || {(coordinator?.loadingInfo || coordinator?.loadingLimits) && (
Object.keys(coordinator?.limits).length === 0) && (
<CircularProgress <CircularProgress
size={49} size={49}
thickness={5} thickness={5}

View File

@ -4,8 +4,6 @@ import { Federation, Settings } from '../models';
import { AppContext, type UseAppStoreType } from './AppContext'; import { AppContext, type UseAppStoreType } from './AppContext';
import { GarageContext, type UseGarageStoreType } from './GarageContext'; import { GarageContext, type UseGarageStoreType } from './GarageContext';
import type Coordinator from '../models/Coordinator.model';
import { type Origin, type Origins } from '../models/Coordinator.model';
export interface CurrentOrderIdProps { export interface CurrentOrderIdProps {
id: number | null; id: number | null;
@ -18,18 +16,16 @@ export interface FederationContextProviderProps {
export interface UseFederationStoreType { export interface UseFederationStoreType {
federation: Federation; federation: Federation;
coordinatorUpdatedAt: string;
federationUpdatedAt: string; federationUpdatedAt: string;
addNewCoordinator: (alias: string, url: string) => void; setFederationUpdatedAt: (federationUpdatedAt: string) => void;
} }
const initialFederation = new Federation('onion', new Settings(), ''); const initialFederation = new Federation('onion', new Settings(), '');
export const initialFederationContext: UseFederationStoreType = { export const initialFederationContext: UseFederationStoreType = {
federation: initialFederation, federation: initialFederation,
coordinatorUpdatedAt: '',
federationUpdatedAt: '', federationUpdatedAt: '',
addNewCoordinator: () => {}, setFederationUpdatedAt: () => {},
}; };
export const FederationContext = createContext<UseFederationStoreType>(initialFederationContext); export const FederationContext = createContext<UseFederationStoreType>(initialFederationContext);
@ -37,13 +33,10 @@ export const FederationContext = createContext<UseFederationStoreType>(initialFe
export const FederationContextProvider = ({ export const FederationContextProvider = ({
children, children,
}: FederationContextProviderProps): React.JSX.Element => { }: FederationContextProviderProps): React.JSX.Element => {
const { settings, page, origin, hostUrl, open, torStatus, client, fav } = const { settings, page, origin, hostUrl, torStatus, client, fav } =
useContext<UseAppStoreType>(AppContext); useContext<UseAppStoreType>(AppContext);
const { setMaker, garage } = useContext<UseGarageStoreType>(GarageContext); const { setMaker } = useContext<UseGarageStoreType>(GarageContext);
const [federation] = useState(initialFederationContext.federation); const [federation] = useState(initialFederationContext.federation);
const [coordinatorUpdatedAt, setCoordinatorUpdatedAt] = useState<string>(
new Date().toISOString(),
);
const [federationUpdatedAt, setFederationUpdatedAt] = useState<string>(new Date().toISOString()); const [federationUpdatedAt, setFederationUpdatedAt] = useState<string>(new Date().toISOString());
useEffect(() => { useEffect(() => {
@ -61,54 +54,16 @@ export const FederationContextProvider = ({
} }
}, [settings.network, settings.useProxy, torStatus, settings.connection]); }, [settings.network, settings.useProxy, torStatus, settings.connection]);
const addNewCoordinator: (alias: string, url: string) => void = (alias, url) => {
if (!federation.getCoordinator(alias)) {
const attributes: object = {
longAlias: alias,
shortAlias: alias,
federated: false,
enabled: true,
};
const origins: Origins = {
clearnet: undefined,
onion: url as Origin,
i2p: undefined,
};
if (settings.network === 'mainnet') {
attributes.mainnet = origins;
} else {
attributes.testnet = origins;
}
federation.addCoordinator(origin, settings, hostUrl, attributes);
const newCoordinator: Coordinator = federation.getCoordinator(alias);
newCoordinator.loadLimits(() => {
setCoordinatorUpdatedAt(new Date().toISOString());
});
garage.syncCoordinator(federation, alias);
setFederationUpdatedAt(new Date().toISOString());
}
};
useEffect(() => { useEffect(() => {
if (page === 'offers') void federation.loadBook(); if (page === 'offers') void federation.loadBook();
}, [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]);
return ( return (
<FederationContext.Provider <FederationContext.Provider
value={{ value={{
federation, federation,
coordinatorUpdatedAt,
federationUpdatedAt, federationUpdatedAt,
addNewCoordinator, setFederationUpdatedAt,
}} }}
> >
{children} {children}

View File

@ -1,6 +1,7 @@
import { import {
Coordinator, Coordinator,
type Exchange, type Exchange,
LimitList,
type Origin, type Origin,
type PublicOrder, type PublicOrder,
type Settings, type Settings,
@ -90,7 +91,7 @@ export class Federation {
coordinators.forEach((c) => c.updateUrl(origin, settings, hostUrl)); coordinators.forEach((c) => c.updateUrl(origin, settings, hostUrl));
this.roboPool.updateRelays(hostUrl, Object.values(this.coordinators)); this.roboPool.updateRelays(hostUrl, Object.values(this.coordinators));
void this.loadLimits(); coordinators[0].loadLimits();
if (this.connection === 'nostr') { if (this.connection === 'nostr') {
this.loadBookNostr(coordinator !== 'any'); this.loadBookNostr(coordinator !== 'any');
@ -194,20 +195,6 @@ export class Federation {
} }
}; };
loadLimits = async (): Promise<void> => {
this.loading = true;
this.exchange.onlineCoordinators = 0;
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
this.updateEnabledCoordinators();
for (const coor of Object.values(this.coordinators)) {
coor.loadLimits(() => {
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
this.onCoordinatorSaved();
});
}
};
loadBook = async (): Promise<void> => { loadBook = async (): Promise<void> => {
if (this.connection !== 'api') return; if (this.connection !== 'api') return;
@ -229,6 +216,18 @@ export class Federation {
this.triggerHook('onFederationUpdate'); this.triggerHook('onFederationUpdate');
}; };
getLimits = (shortAlias: string): LimitList => {
console.log('shortAlias', shortAlias);
let limits = this.coordinators[shortAlias]?.limits || {};
console.log('limits pre', Object.keys(limits).length);
if (Object.keys(limits).length === 0) {
limits = this.getCoordinators()[0]?.limits;
}
console.log('limits', Object.keys(limits).length);
return limits;
};
// Coordinators // Coordinators
getCoordinators = (): Coordinator[] => { getCoordinators = (): Coordinator[] => {
return Object.values(this.coordinators); return Object.values(this.coordinators);