Federation coordinators list refactor

This commit is contained in:
koalasat
2024-10-18 12:36:26 +02:00
parent 3edd9280a5
commit 1695745cac
12 changed files with 53 additions and 56 deletions

View File

@ -25,7 +25,7 @@ const MakerPage = (): JSX.Element => {
const matches = useMemo(() => { const matches = useMemo(() => {
return filterOrders({ return filterOrders({
orders: Object.values(federation.book), federation,
baseFilter: { baseFilter: {
currency: fav.currency === 0 ? 1 : fav.currency, currency: fav.currency === 0 ? 1 : fav.currency,
type: fav.type, type: fav.type,

View File

@ -26,7 +26,7 @@ import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageCon
const RobotPage = (): JSX.Element => { const RobotPage = (): JSX.Element => {
const { torStatus, windowSize, settings, page, client } = useContext<UseAppStoreType>(AppContext); const { torStatus, windowSize, settings, page, client } = useContext<UseAppStoreType>(AppContext);
const { garage } = useContext<UseGarageStoreType>(GarageContext); const { garage } = useContext<UseGarageStoreType>(GarageContext);
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext); const { federation } = useContext<UseFederationStoreType>(FederationContext);
const { t } = useTranslation(); const { t } = useTranslation();
const params = useParams(); const params = useParams();
const urlToken = settings.selfhostedClient ? params.token : null; const urlToken = settings.selfhostedClient ? params.token : null;
@ -64,7 +64,7 @@ const RobotPage = (): JSX.Element => {
setInputToken(token); setInputToken(token);
genKey(token) genKey(token)
.then((key) => { .then((key) => {
garage.createRobot(token, sortedCoordinators, { garage.createRobot(token, Object.keys(federation.coordinators), {
token, token,
pubKey: key.publicKeyArmored, pubKey: key.publicKeyArmored,
encPrivKey: key.encryptedPrivateKeyArmored, encPrivKey: key.encryptedPrivateKeyArmored,

View File

@ -350,8 +350,9 @@ const BookControl = ({
<FlagWithProps code='ANY' /> <FlagWithProps code='ANY' />
</div> </div>
</MenuItem> </MenuItem>
{Object.values(federation.coordinators).map((coordinator) => {Object.values(federation.coordinators)
coordinator.enabled ? ( .filter((coord) => coord.enabled)
.map((coordinator) => (
<MenuItem <MenuItem
key={coordinator.shortAlias} key={coordinator.shortAlias}
value={coordinator.shortAlias} value={coordinator.shortAlias}
@ -367,10 +368,7 @@ const BookControl = ({
/> />
</div> </div>
</MenuItem> </MenuItem>
) : ( ))}
<></>
),
)}
</Select> </Select>
</Grid> </Grid>
</> </>

View File

@ -895,7 +895,7 @@ const BookTable = ({
const filteredOrders = useMemo(() => { const filteredOrders = useMemo(() => {
return showControls return showControls
? filterOrders({ ? filterOrders({
orders, federation,
baseFilter: fav, baseFilter: fav,
paymentMethods, paymentMethods,
}) })

View File

@ -21,8 +21,7 @@ const FederationTable = ({
fillContainer = false, fillContainer = false,
}: FederationTableProps): JSX.Element => { }: FederationTableProps): JSX.Element => {
const { t } = useTranslation(); const { t } = useTranslation();
const { federation, sortedCoordinators, federationUpdatedAt } = const { federation, federationUpdatedAt } = useContext<UseFederationStoreType>(FederationContext);
useContext<UseFederationStoreType>(FederationContext);
const { setOpen, settings } = useContext<UseAppStoreType>(AppContext); const { setOpen, settings } = useContext<UseAppStoreType>(AppContext);
const theme = useTheme(); const theme = useTheme();
const [pageSize, setPageSize] = useState<number>(0); const [pageSize, setPageSize] = useState<number>(0);
@ -214,14 +213,6 @@ const FederationTable = ({
} }
}; };
const reorderedCoordinators = useMemo(() => {
return sortedCoordinators.reduce((coordinators, key) => {
coordinators[key] = federation.coordinators[key];
return coordinators;
}, {});
}, [settings.network, federationUpdatedAt]);
return ( return (
<Box <Box
sx={ sx={
@ -235,7 +226,7 @@ const FederationTable = ({
localeText={localeText} localeText={localeText}
rowHeight={3.714 * theme.typography.fontSize} rowHeight={3.714 * theme.typography.fontSize}
headerHeight={3.25 * theme.typography.fontSize} headerHeight={3.25 * theme.typography.fontSize}
rows={Object.values(reorderedCoordinators)} rows={Object.values(federation.coordinators)}
getRowId={(params: Coordinator) => params.shortAlias} getRowId={(params: Coordinator) => params.shortAlias}
columns={columns} columns={columns}
checkboxSelection={false} checkboxSelection={false}

View File

@ -26,7 +26,7 @@ const SelectCoordinator: React.FC<SelectCoordinatorProps> = ({
setCoordinator, setCoordinator,
}) => { }) => {
const { setOpen } = useContext<UseAppStoreType>(AppContext); const { setOpen } = useContext<UseAppStoreType>(AppContext);
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext); const { federation } = useContext<UseFederationStoreType>(FederationContext);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
@ -109,18 +109,20 @@ const SelectCoordinator: React.FC<SelectCoordinatorProps> = ({
onChange={handleCoordinatorChange} onChange={handleCoordinatorChange}
disableUnderline disableUnderline
> >
{sortedCoordinators.map((shortAlias: string): JSX.Element | null => { {Object.keys(federation.coordinators).map(
let row: JSX.Element | null = null; (shortAlias: string): JSX.Element | null => {
const item = federation.getCoordinator(shortAlias); let row: JSX.Element | null = null;
if (item.enabled === true) { const item = federation.getCoordinator(shortAlias);
row = ( if (item.enabled === true) {
<MenuItem key={shortAlias} value={shortAlias}> row = (
<Typography>{item.longAlias}</Typography> <MenuItem key={shortAlias} value={shortAlias}>
</MenuItem> <Typography>{item.longAlias}</Typography>
); </MenuItem>
} );
return row; }
})} return row;
},
)}
</Select> </Select>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -27,7 +27,6 @@ export interface FederationContextProviderProps {
export interface UseFederationStoreType { export interface UseFederationStoreType {
federation: Federation; federation: Federation;
sortedCoordinators: string[];
coordinatorUpdatedAt: string; coordinatorUpdatedAt: string;
federationUpdatedAt: string; federationUpdatedAt: string;
addNewCoordinator: (alias: string, url: string) => void; addNewCoordinator: (alias: string, url: string) => void;
@ -35,7 +34,6 @@ export interface UseFederationStoreType {
export const initialFederationContext: UseFederationStoreType = { export const initialFederationContext: UseFederationStoreType = {
federation: new Federation('onion', new Settings(), ''), federation: new Federation('onion', new Settings(), ''),
sortedCoordinators: [],
coordinatorUpdatedAt: '', coordinatorUpdatedAt: '',
federationUpdatedAt: '', federationUpdatedAt: '',
addNewCoordinator: () => {}, addNewCoordinator: () => {},
@ -50,7 +48,6 @@ export const FederationContextProvider = ({
useContext<UseAppStoreType>(AppContext); useContext<UseAppStoreType>(AppContext);
const { setMaker, garage } = useContext<UseGarageStoreType>(GarageContext); const { setMaker, garage } = useContext<UseGarageStoreType>(GarageContext);
const [federation] = useState(new Federation(origin, settings, hostUrl)); const [federation] = useState(new Federation(origin, settings, hostUrl));
const [sortedCoordinators, setSortedCoordinators] = useState(federationLottery(federation));
const [coordinatorUpdatedAt, setCoordinatorUpdatedAt] = useState<string>( const [coordinatorUpdatedAt, setCoordinatorUpdatedAt] = useState<string>(
new Date().toISOString(), new Date().toISOString(),
); );
@ -58,7 +55,7 @@ export const FederationContextProvider = ({
useEffect(() => { useEffect(() => {
setMaker((maker) => { setMaker((maker) => {
return { ...maker, coordinator: sortedCoordinators[0] }; return { ...maker, coordinator: Object.keys(federation.coordinators)[0] };
}); // default MakerForm coordinator is decided via sorted lottery }); // default MakerForm coordinator is decided via sorted lottery
federation.registerHook('onFederationUpdate', () => { federation.registerHook('onFederationUpdate', () => {
setFederationUpdatedAt(new Date().toISOString()); setFederationUpdatedAt(new Date().toISOString());
@ -122,7 +119,6 @@ export const FederationContextProvider = ({
<FederationContext.Provider <FederationContext.Provider
value={{ value={{
federation, federation,
sortedCoordinators,
coordinatorUpdatedAt, coordinatorUpdatedAt,
federationUpdatedAt, federationUpdatedAt,
addNewCoordinator, addNewCoordinator,

View File

@ -66,7 +66,7 @@ export const GarageContextProvider = ({ children }: GarageContextProviderProps):
// All garage data structured // All garage data structured
const { settings, torStatus, open, page, client } = useContext<UseAppStoreType>(AppContext); const { settings, torStatus, open, page, client } = useContext<UseAppStoreType>(AppContext);
const pageRef = useRef(page); const pageRef = useRef(page);
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext); const { federation } = useContext<UseFederationStoreType>(FederationContext);
const [garage] = useState<Garage>(initialGarageContext.garage); const [garage] = useState<Garage>(initialGarageContext.garage);
const [maker, setMaker] = useState<Maker>(initialGarageContext.maker); const [maker, setMaker] = useState<Maker>(initialGarageContext.maker);
const [slotUpdatedAt, setSlotUpdatedAt] = useState<string>(new Date().toISOString()); const [slotUpdatedAt, setSlotUpdatedAt] = useState<string>(new Date().toISOString());
@ -83,7 +83,7 @@ export const GarageContextProvider = ({ children }: GarageContextProviderProps):
useEffect(() => { useEffect(() => {
setMaker((maker) => { setMaker((maker) => {
return { ...maker, coordinator: sortedCoordinators[0] }; return { ...maker, coordinator: Object.keys(federation.coordinators)[0] };
}); // default MakerForm coordinator is decided via sorted lottery }); // default MakerForm coordinator is decided via sorted lottery
garage.registerHook('onSlotUpdate', onSlotUpdated); garage.registerHook('onSlotUpdate', onSlotUpdated);
clearInterval(timer); clearInterval(timer);

View File

@ -126,7 +126,7 @@ export class Coordinator {
this.longAlias = value.longAlias; this.longAlias = value.longAlias;
this.shortAlias = value.shortAlias; this.shortAlias = value.shortAlias;
this.description = value.description; this.description = value.description;
this.federated = value.federated; this.federated = value.federated ?? false;
this.motto = value.motto; this.motto = value.motto;
this.color = value.color; this.color = value.color;
this.size_limit = value.badges.isFounder ? 21 * 100000000 : calculateSizeLimit(established); this.size_limit = value.badges.isFounder ? 21 * 100000000 : calculateSizeLimit(established);

View File

@ -1,4 +1,3 @@
import { SimplePool, VerifiedEvent, Event } from 'nostr-tools';
import { import {
Coordinator, Coordinator,
type Exchange, type Exchange,
@ -9,7 +8,7 @@ import {
} from '.'; } from '.';
import defaultFederation from '../../static/federation.json'; import defaultFederation from '../../static/federation.json';
import { systemClient } from '../services/System'; import { systemClient } from '../services/System';
import { getHost } from '../utils'; import { federationLottery, getHost } from '../utils';
import { coordinatorDefaultValues } from './Coordinator.model'; import { coordinatorDefaultValues } from './Coordinator.model';
import { updateExchangeInfo } from './Exchange.model'; import { updateExchangeInfo } from './Exchange.model';
import eventToPublicOrder from '../utils/nostr'; import eventToPublicOrder from '../utils/nostr';
@ -20,19 +19,25 @@ type FederationHooks = 'onFederationUpdate';
export class Federation { export class Federation {
constructor(origin: Origin, settings: Settings, hostUrl: string) { constructor(origin: Origin, settings: Settings, hostUrl: string) {
this.coordinators = Object.entries(defaultFederation).reduce( const coordinators = Object.entries(defaultFederation).reduce(
(acc: Record<string, Coordinator>, [key, value]: [string, any]) => { (acc: Record<string, Coordinator>, [key, value]: [string, any]) => {
if (getHost() !== '127.0.0.1:8000' && key === 'local') { if (getHost() !== '127.0.0.1:8000' && key === 'local') {
// Do not add `Local Dev` unless it is running on localhost // Do not add `Local Dev` unless it is running on localhost
return acc; return acc;
} else { } else {
acc[key] = new Coordinator(value, origin, settings, hostUrl); acc[key] = new Coordinator(value, origin, settings, hostUrl);
acc[key].federated = true;
return acc; return acc;
} }
}, },
{}, {},
); );
this.coordinators = {};
federationLottery().forEach((alias) => {
if (coordinators[alias]) this.coordinators[alias] = coordinators[alias];
});
this.exchange = { this.exchange = {
...defaultExchange, ...defaultExchange,
totalCoordinators: Object.keys(this.coordinators).length, totalCoordinators: Object.keys(this.coordinators).length,
@ -42,10 +47,10 @@ export class Federation {
onFederationUpdate: [], onFederationUpdate: [],
}; };
Object.keys(defaultFederation).forEach((key) => { Object.keys(this.coordinators).forEach((key) => {
if (key !== 'local' || getHost() === '127.0.0.1:8000') { if (key !== 'local' || getHost() === '127.0.0.1:8000') {
// Do not add `Local Dev` unless it is running on localhost // Do not add `Local Dev` unless it is running on localhost
this.addCoordinator(origin, settings, hostUrl, defaultFederation[key]); this.addCoordinator(origin, settings, hostUrl, this.coordinators[key]);
} }
}); });

View File

@ -11,14 +11,14 @@
// donate to the development fund. This is the only way envisioned to incentivize // donate to the development fund. This is the only way envisioned to incentivize
// donations to the development fund. // donations to the development fund.
import { type Federation } from '../models'; import defaultFederation from '../../static/federation.json';
export default function federationLottery(federation: Federation): string[] { export default function federationLottery(): string[] {
// Create an array to store the coordinator short aliases and their corresponding weights (chance) // Create an array to store the coordinator short aliases and their corresponding weights (chance)
const coordinatorChance: Array<{ shortAlias: string; chance: number }> = []; const coordinatorChance: Array<{ shortAlias: string; chance: number }> = [];
// Convert the `federation` object into an array of {shortAlias, chance} // Convert the `federation` object into an array of {shortAlias, chance}
Object.values(federation.coordinators).forEach((coor) => { Object.values(defaultFederation).forEach((coor) => {
const chance = coor.badges.donatesToDevFund > 50 ? 50 : coor.badges?.donatesToDevFund; const chance = coor.badges.donatesToDevFund > 50 ? 50 : coor.badges?.donatesToDevFund;
coordinatorChance.push({ shortAlias: coor.shortAlias, chance }); coordinatorChance.push({ shortAlias: coor.shortAlias, chance });
}); });

View File

@ -1,4 +1,4 @@
import { type PublicOrder, type Favorites } from '../models'; import { type PublicOrder, type Favorites, Federation } from '../models';
interface AmountFilter { interface AmountFilter {
amount: string; amount: string;
@ -8,9 +8,9 @@ interface AmountFilter {
} }
interface FilterOrders { interface FilterOrders {
orders: PublicOrder[]; federation: Federation;
baseFilter: Favorites; baseFilter: Favorites;
premium: number | null; premium?: number | null;
amountFilter?: AmountFilter | null; amountFilter?: AmountFilter | null;
paymentMethods?: string[]; paymentMethods?: string[];
} }
@ -60,13 +60,17 @@ const filterByPremium = function (order: PublicOrder, premium: number): boolean
}; };
const filterOrders = function ({ const filterOrders = function ({
orders, federation,
baseFilter, baseFilter,
premium = null, premium = null,
paymentMethods = [], paymentMethods = [],
amountFilter = null, amountFilter = null,
}: FilterOrders): PublicOrder[] { }: FilterOrders): PublicOrder[] {
const filteredOrders = orders.filter((order) => { const enabledCoordinators = Object.values(federation.coordinators)
.filter((coord) => coord.enabled)
.map((coord) => coord.shortAlias);
const filteredOrders = Object.values(federation.book).filter((order) => {
const coordinatorCheck = enabledCoordinators.includes(order.coordinatorShortAlias ?? '');
const typeChecks = order.type === baseFilter.type || baseFilter.type == null; const typeChecks = order.type === baseFilter.type || baseFilter.type == null;
const modeChecks = baseFilter.mode === 'fiat' ? !(order.currency === 1000) : true; const modeChecks = baseFilter.mode === 'fiat' ? !(order.currency === 1000) : true;
const premiumChecks = premium !== null ? filterByPremium(order, premium) : true; const premiumChecks = premium !== null ? filterByPremium(order, premium) : true;
@ -77,6 +81,7 @@ const filterOrders = function ({
const hostChecks = const hostChecks =
baseFilter.coordinator !== 'any' ? filterByHost(order, baseFilter.coordinator) : true; baseFilter.coordinator !== 'any' ? filterByHost(order, baseFilter.coordinator) : true;
return ( return (
coordinatorCheck &&
typeChecks && typeChecks &&
modeChecks && modeChecks &&
premiumChecks && premiumChecks &&