mirror of
https://github.com/RoboSats/robosats.git
synced 2025-07-19 09:13:28 +00:00
255 lines
7.3 KiB
TypeScript
255 lines
7.3 KiB
TypeScript
import React, { useCallback, useEffect, useState, useContext, useMemo } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Box, useTheme, Checkbox, CircularProgress, Typography, Grid } from '@mui/material';
|
|
import { DataGrid, type GridColDef, type GridValidRowModel } from '@mui/x-data-grid';
|
|
import { type Coordinator } from '../../models';
|
|
import RobotAvatar from '../RobotAvatar';
|
|
import { Link, LinkOff } from '@mui/icons-material';
|
|
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
|
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
|
import headerStyleFix from '../DataGrid/HeaderFix';
|
|
|
|
interface FederationTableProps {
|
|
maxWidth?: number;
|
|
maxHeight?: number;
|
|
fillContainer?: boolean;
|
|
}
|
|
|
|
const FederationTable = ({
|
|
maxWidth = 90,
|
|
maxHeight = 50,
|
|
fillContainer = false,
|
|
}: FederationTableProps): JSX.Element => {
|
|
const { t } = useTranslation();
|
|
const { federation, sortedCoordinators, federationUpdatedAt } =
|
|
useContext<UseFederationStoreType>(FederationContext);
|
|
const { setOpen, settings } = useContext<UseAppStoreType>(AppContext);
|
|
const theme = useTheme();
|
|
const [pageSize, setPageSize] = useState<number>(0);
|
|
|
|
// all sizes in 'em'
|
|
const fontSize = theme.typography.fontSize;
|
|
const verticalHeightFrame = 3.3;
|
|
const verticalHeightRow = 3.27;
|
|
const defaultPageSize = Math.max(
|
|
Math.floor((maxHeight - verticalHeightFrame) / verticalHeightRow),
|
|
1,
|
|
);
|
|
const height = defaultPageSize * verticalHeightRow + verticalHeightFrame;
|
|
|
|
const [useDefaultPageSize, setUseDefaultPageSize] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (useDefaultPageSize) {
|
|
setPageSize(defaultPageSize);
|
|
}
|
|
}, [federationUpdatedAt]);
|
|
|
|
const localeText = {
|
|
MuiTablePagination: { labelRowsPerPage: t('Coordinators per page:') },
|
|
noResultsOverlayLabel: t('No coordinators found.'),
|
|
};
|
|
|
|
const onClickCoordinator = function (shortAlias: string): void {
|
|
setOpen((open) => {
|
|
return { ...open, coordinator: shortAlias };
|
|
});
|
|
};
|
|
|
|
const aliasObj = useCallback((width: number) => {
|
|
return {
|
|
field: 'longAlias',
|
|
headerName: t('Coordinator'),
|
|
width: width * fontSize,
|
|
renderCell: (params: any) => {
|
|
const coordinator = federation.coordinators[params.row.shortAlias];
|
|
return (
|
|
<Grid
|
|
container
|
|
direction='row'
|
|
sx={{ cursor: 'pointer', position: 'relative', left: '-0.3em', width: '50em' }}
|
|
wrap='nowrap'
|
|
onClick={() => {
|
|
onClickCoordinator(params.row.shortAlias);
|
|
}}
|
|
alignItems='center'
|
|
spacing={1}
|
|
>
|
|
<Grid item>
|
|
<RobotAvatar
|
|
shortAlias={coordinator.federated ? params.row.shortAlias : undefined}
|
|
hashId={coordinator.federated ? undefined : coordinator.mainnet.onion}
|
|
style={{ width: '3.215em', height: '3.215em' }}
|
|
smooth={true}
|
|
small={true}
|
|
/>
|
|
</Grid>
|
|
<Grid item>
|
|
<Typography>{params.row.longAlias}</Typography>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
},
|
|
};
|
|
}, []);
|
|
|
|
const enabledObj = useCallback(
|
|
(width: number) => {
|
|
return {
|
|
field: 'enabled',
|
|
headerName: t('Enabled'),
|
|
width: width * fontSize,
|
|
renderCell: (params: any) => {
|
|
return (
|
|
<Checkbox
|
|
checked={params.row.enabled}
|
|
onClick={() => {
|
|
onEnableChange(params.row.shortAlias);
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
};
|
|
},
|
|
[federationUpdatedAt],
|
|
);
|
|
|
|
const upObj = useCallback(
|
|
(width: number) => {
|
|
return {
|
|
field: 'up',
|
|
headerName: t('Up'),
|
|
width: width * fontSize,
|
|
renderCell: (params: any) => {
|
|
return (
|
|
<div
|
|
style={{ cursor: 'pointer' }}
|
|
onClick={() => {
|
|
onClickCoordinator(params.row.shortAlias);
|
|
}}
|
|
>
|
|
{Boolean(params.row.loadingInfo) && Boolean(params.row.enabled) ? (
|
|
<CircularProgress thickness={0.35 * fontSize} size={1.5 * fontSize} />
|
|
) : params.row.info !== undefined ? (
|
|
<Link color='success' />
|
|
) : (
|
|
<LinkOff color='error' />
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
};
|
|
},
|
|
[federationUpdatedAt],
|
|
);
|
|
|
|
const columnSpecs = {
|
|
alias: {
|
|
priority: 2,
|
|
order: 1,
|
|
normal: {
|
|
width: 12.1,
|
|
object: aliasObj,
|
|
},
|
|
},
|
|
up: {
|
|
priority: 3,
|
|
order: 2,
|
|
normal: {
|
|
width: 3.5,
|
|
object: upObj,
|
|
},
|
|
},
|
|
enabled: {
|
|
priority: 1,
|
|
order: 3,
|
|
normal: {
|
|
width: 5,
|
|
object: enabledObj,
|
|
},
|
|
},
|
|
};
|
|
|
|
const filteredColumns = function (): {
|
|
columns: Array<GridColDef<GridValidRowModel>>;
|
|
width: number;
|
|
} {
|
|
const useSmall = maxWidth < 30;
|
|
const selectedColumns: object[] = [];
|
|
let width: number = 0;
|
|
|
|
for (const value of Object.values(columnSpecs)) {
|
|
const colWidth = Number(
|
|
useSmall && Boolean(value.small) ? value.small.width : value.normal.width,
|
|
);
|
|
const colObject = useSmall && Boolean(value.small) ? value.small.object : value.normal.object;
|
|
|
|
if (width + colWidth < maxWidth || selectedColumns.length < 2) {
|
|
width = width + colWidth;
|
|
selectedColumns.push([colObject(colWidth, false), value.order]);
|
|
} else {
|
|
selectedColumns.push([colObject(colWidth, true), value.order]);
|
|
}
|
|
}
|
|
|
|
// sort columns by column.order value
|
|
selectedColumns.sort(function (first, second) {
|
|
return first[1] - second[1];
|
|
});
|
|
|
|
const columns: Array<GridColDef<GridValidRowModel>> = selectedColumns.map(function (item) {
|
|
return item[0];
|
|
});
|
|
|
|
return { columns, width: width * 0.9 };
|
|
};
|
|
|
|
const { columns, width } = filteredColumns();
|
|
|
|
const onEnableChange = function (shortAlias: string): void {
|
|
if (federation.getCoordinator(shortAlias).enabled === true) {
|
|
federation.disableCoordinator(shortAlias);
|
|
} else {
|
|
federation.enableCoordinator(shortAlias);
|
|
}
|
|
};
|
|
|
|
const reorderedCoordinators = useMemo(() => {
|
|
return sortedCoordinators.reduce((coordinators, key) => {
|
|
coordinators[key] = federation.coordinators[key];
|
|
|
|
return coordinators;
|
|
}, {});
|
|
}, [settings.network, federationUpdatedAt]);
|
|
|
|
return (
|
|
<Box
|
|
sx={
|
|
fillContainer
|
|
? { width: '100%', height: '100%' }
|
|
: { width: `${width}em`, height: `${height}em`, overflow: 'auto' }
|
|
}
|
|
>
|
|
<DataGrid
|
|
sx={headerStyleFix}
|
|
localeText={localeText}
|
|
rowHeight={3.714 * theme.typography.fontSize}
|
|
headerHeight={3.25 * theme.typography.fontSize}
|
|
rows={Object.values(reorderedCoordinators)}
|
|
getRowId={(params: Coordinator) => params.shortAlias}
|
|
columns={columns}
|
|
checkboxSelection={false}
|
|
pageSize={pageSize}
|
|
rowsPerPageOptions={width < 22 ? [] : [0, pageSize, defaultPageSize * 2, 50, 100]}
|
|
onPageSizeChange={(newPageSize) => {
|
|
setPageSize(newPageSize);
|
|
setUseDefaultPageSize(false);
|
|
}}
|
|
hideFooter={true}
|
|
/>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default FederationTable;
|