mirror of
https://github.com/RoboSats/robosats.git
synced 2025-02-26 07:09:01 +00:00
ui-update 2
This commit is contained in:
parent
bae02688d8
commit
739e6e1345
@ -2,38 +2,45 @@ import React, { useState, useContext, useEffect } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Button,
|
|
||||||
Grid,
|
|
||||||
LinearProgress,
|
|
||||||
Typography,
|
Typography,
|
||||||
Alert,
|
LinearProgress,
|
||||||
Select,
|
Select,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Box,
|
Box,
|
||||||
|
TextField,
|
||||||
|
SelectChangeEvent,
|
||||||
useTheme,
|
useTheme,
|
||||||
Tooltip,
|
useMediaQuery,
|
||||||
type SelectChangeEvent,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Bolt, Add, DeleteSweep, Logout, Download } from '@mui/icons-material';
|
import { Bolt, Add, DeleteSweep, Logout, Download, FileCopy } from '@mui/icons-material';
|
||||||
import RobotAvatar from '../../components/RobotAvatar';
|
import RobotAvatar from '../../components/RobotAvatar';
|
||||||
import TokenInput from './TokenInput';
|
import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
|
||||||
import { type Slot, type Robot } from '../../models';
|
|
||||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
|
||||||
import { genBase62Token } from '../../utils';
|
import { genBase62Token } from '../../utils';
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext';
|
||||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext';
|
||||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
|
||||||
|
const BUTTON_COLORS = {
|
||||||
|
primary: '#2196f3',
|
||||||
|
secondary: '#9c27b0',
|
||||||
|
text: '#ffffff',
|
||||||
|
hoverPrimary: '#4dabf5',
|
||||||
|
hoverSecondary: '#af52bf',
|
||||||
|
activePrimary: '#1976d2',
|
||||||
|
activeSecondary: '#7b1fa2',
|
||||||
|
deleteHover: '#ff6666',
|
||||||
|
};
|
||||||
|
|
||||||
|
const COLORS = {
|
||||||
|
shadow: '#000000',
|
||||||
|
};
|
||||||
|
|
||||||
interface RobotProfileProps {
|
interface RobotProfileProps {
|
||||||
robot: Robot;
|
|
||||||
setRobot: (state: Robot) => void;
|
|
||||||
setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void;
|
|
||||||
getGenerateRobot: (token: string, slot?: number) => void;
|
|
||||||
inputToken: string;
|
inputToken: string;
|
||||||
|
getGenerateRobot: (token: string) => void;
|
||||||
|
setInputToken: (token: string) => void;
|
||||||
logoutRobot: () => void;
|
logoutRobot: () => void;
|
||||||
setInputToken: (state: string) => void;
|
setView: (view: string) => void;
|
||||||
width: number;
|
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RobotProfile = ({
|
const RobotProfile = ({
|
||||||
@ -42,7 +49,6 @@ const RobotProfile = ({
|
|||||||
setInputToken,
|
setInputToken,
|
||||||
logoutRobot,
|
logoutRobot,
|
||||||
setView,
|
setView,
|
||||||
width,
|
|
||||||
}: RobotProfileProps): JSX.Element => {
|
}: RobotProfileProps): JSX.Element => {
|
||||||
const { windowSize } = useContext<UseAppStoreType>(AppContext);
|
const { windowSize } = useContext<UseAppStoreType>(AppContext);
|
||||||
const { garage, robotUpdatedAt, orderUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
const { garage, robotUpdatedAt, orderUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
||||||
@ -51,6 +57,7 @@ const RobotProfile = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
@ -82,278 +89,309 @@ const RobotProfile = ({
|
|||||||
).length;
|
).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container direction='column' alignItems='center' spacing={1} padding={1} paddingTop={2}>
|
<ProfileContainer $isMobile={isMobile}>
|
||||||
<Grid
|
<InfoSection colors={COLORS} $isMobile={isMobile}>
|
||||||
item
|
<NicknameTypography variant={isMobile ? "h6" : "h5"} align="center" $isMobile={isMobile}>
|
||||||
container
|
<BoltIcon $isMobile={isMobile} />
|
||||||
direction='column'
|
{slot?.nickname}
|
||||||
alignItems='center'
|
<BoltIcon $isMobile={isMobile} />
|
||||||
spacing={1}
|
</NicknameTypography>
|
||||||
sx={{ width: '100%' }}
|
<StyledRobotAvatar
|
||||||
>
|
|
||||||
<Grid item sx={{ height: '2.3em', position: 'relative' }}>
|
|
||||||
{slot?.nickname ? (
|
|
||||||
<Typography align='center' component='h5' variant='h5'>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{width < 19 ? null : (
|
|
||||||
<Bolt
|
|
||||||
sx={{
|
|
||||||
color: '#fcba03',
|
|
||||||
height: '1.5em',
|
|
||||||
width: '1.5em',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<b>{slot?.nickname}</b>
|
|
||||||
{width < 19 ? null : (
|
|
||||||
<Bolt
|
|
||||||
sx={{
|
|
||||||
color: '#fcba03',
|
|
||||||
height: '1.5em',
|
|
||||||
width: '1.5em',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Typography>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<b>{t('Building your robot!')}</b>
|
|
||||||
<LinearProgress />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item sx={{ width: `13.5em` }}>
|
|
||||||
<RobotAvatar
|
|
||||||
hashId={slot?.hashId}
|
hashId={slot?.hashId}
|
||||||
smooth={true}
|
smooth={true}
|
||||||
style={{ maxWidth: '12.5em', maxHeight: '12.5em' }}
|
|
||||||
placeholderType='generating'
|
placeholderType='generating'
|
||||||
imageStyle={{
|
style={{ width: isMobile ? '80px' : '120px', height: isMobile ? '80px' : '120px' }}
|
||||||
transform: '',
|
|
||||||
border: '2px solid #555',
|
|
||||||
filter: 'drop-shadow(1px 1px 1px #000000)',
|
|
||||||
height: `12.4em`,
|
|
||||||
width: `12.4em`,
|
|
||||||
}}
|
|
||||||
tooltip={t('This is your trading avatar')}
|
|
||||||
tooltipPosition='top'
|
|
||||||
/>
|
/>
|
||||||
{robot?.found && Boolean(slot?.lastShortAlias) ? (
|
<StatusTypography variant={isMobile ? "body2" : "body1"} align="center" $isMobile={isMobile}>
|
||||||
<Typography align='center' variant='h6'>
|
{loadingCoordinators > 0 && !robot?.activeOrderId ? t('Looking for orders!') : t('Ready to Trade')}
|
||||||
{t('Welcome back!')}
|
</StatusTypography>
|
||||||
</Typography>
|
{loadingCoordinators > 0 && !robot?.activeOrderId && <StyledLinearProgress $isMobile={isMobile} />}
|
||||||
) : (
|
<TokenBox $isMobile={isMobile}>
|
||||||
<></>
|
<CustomIconButton onClick={() => {
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{loadingCoordinators > 0 && !robot?.activeOrderId ? (
|
|
||||||
<Grid>
|
|
||||||
<b>{t('Looking for orders!')}</b>
|
|
||||||
<LinearProgress />
|
|
||||||
</Grid>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{Boolean(robot?.activeOrderId) && Boolean(slot?.hashId) ? (
|
|
||||||
<Grid item>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentOrderId({ id: robot?.activeOrderId, shortAlias: slot?.activeShortAlias });
|
|
||||||
navigate(
|
|
||||||
`/order/${String(slot?.activeShortAlias)}/${String(robot?.activeOrderId)}`,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Active order #{{orderID}}', { orderID: robot?.activeOrderId })}
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{Boolean(robot?.lastOrderId) && Boolean(slot?.hashId) ? (
|
|
||||||
<Grid item container direction='column' alignItems='center'>
|
|
||||||
<Grid item>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentOrderId({ id: robot?.lastOrderId, shortAlias: slot?.activeShortAlias });
|
|
||||||
navigate(`/order/${String(slot?.lastShortAlias)}/${String(robot?.lastOrderId)}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Last order #{{orderID}}', { orderID: robot?.lastOrderId })}
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Alert severity='warning'>
|
|
||||||
<Grid container direction='column' alignItems='center'>
|
|
||||||
<Grid item>
|
|
||||||
{t(
|
|
||||||
'Reusing trading identity degrades your privacy against other users, coordinators and observers.',
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
<Grid item sx={{ position: 'relative', right: '1em' }}>
|
|
||||||
<Button color='success' size='small' onClick={handleAddRobot}>
|
|
||||||
<Add />
|
|
||||||
{t('Add a new Robot')}
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Alert>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{!robot?.activeOrderId &&
|
|
||||||
slot?.hashId &&
|
|
||||||
!robot?.lastOrderId &&
|
|
||||||
loadingCoordinators === 0 ? (
|
|
||||||
<Grid item>{t('No existing orders found')}</Grid>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
container
|
|
||||||
direction='row'
|
|
||||||
justifyContent='stretch'
|
|
||||||
alignItems='stretch'
|
|
||||||
sx={{ width: '100%' }}
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
xs={2}
|
|
||||||
sx={{ display: 'flex', justifyContent: 'stretch', alignItems: 'stretch' }}
|
|
||||||
>
|
|
||||||
<Tooltip enterTouchDelay={0} enterDelay={300} enterNextDelay={1000} title={t('Logout')}>
|
|
||||||
<Button
|
|
||||||
sx={{ minWidth: '2em', width: '100%' }}
|
|
||||||
color='primary'
|
|
||||||
variant='outlined'
|
|
||||||
onClick={() => {
|
|
||||||
logoutRobot();
|
logoutRobot();
|
||||||
setView('welcome');
|
setView('welcome');
|
||||||
}}
|
}}>
|
||||||
>
|
<StyledLogoutIcon $isMobile={isMobile} />
|
||||||
<Logout />
|
</CustomIconButton>
|
||||||
</Button>
|
<StyledTextField
|
||||||
</Tooltip>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={10}>
|
|
||||||
<TokenInput
|
|
||||||
inputToken={inputToken}
|
|
||||||
editable={false}
|
|
||||||
label={t('Store your token safely')}
|
|
||||||
setInputToken={setInputToken}
|
|
||||||
onPressEnter={() => null}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item sx={{ width: '100%' }}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
backgroundColor: 'background.paper',
|
|
||||||
border: '1px solid',
|
|
||||||
borderRadius: '4px',
|
|
||||||
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid container direction='column' alignItems='center' spacing={2} padding={2}>
|
|
||||||
<Grid item sx={{ width: '100%' }}>
|
|
||||||
<Typography variant='caption'>{t('Robot Garage')}</Typography>
|
|
||||||
<Select
|
|
||||||
fullWidth
|
fullWidth
|
||||||
required={true}
|
value={inputToken}
|
||||||
inputProps={{
|
variant="standard"
|
||||||
style: { textAlign: 'center' },
|
$isMobile={isMobile}
|
||||||
|
InputProps={{
|
||||||
|
readOnly: true,
|
||||||
|
disableUnderline: true,
|
||||||
|
endAdornment: (
|
||||||
|
<CustomIconButton onClick={() => navigator.clipboard.writeText(inputToken)}>
|
||||||
|
<StyledFileCopyIcon $isMobile={isMobile} />
|
||||||
|
</CustomIconButton>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
|
/>
|
||||||
|
</TokenBox>
|
||||||
|
</InfoSection>
|
||||||
|
|
||||||
|
<RightSection $isMobile={isMobile}>
|
||||||
|
<TitleSection>
|
||||||
|
<TitleTypography variant={isMobile ? "subtitle1" : "h6"} align="center">
|
||||||
|
{t('Robot Garage')}
|
||||||
|
</TitleTypography>
|
||||||
|
</TitleSection>
|
||||||
|
<StyledSelect
|
||||||
value={loading ? 'loading' : garage.currentSlot}
|
value={loading ? 'loading' : garage.currentSlot}
|
||||||
onChange={handleChangeSlot}
|
onChange={handleChangeSlot}
|
||||||
|
$isMobile={isMobile}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<MenuItem key={'loading'} value={'loading'}>
|
<MenuItem key={'loading'} value={'loading'}>
|
||||||
<Typography>{t('Building...')}</Typography>
|
<Typography variant={isMobile ? "body2" : "body1"}>{t('Building...')}</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : (
|
) : (
|
||||||
Object.values(garage.slots).map((slot: Slot, index: number) => {
|
Object.values(garage.slots).map((slot: Slot, index: number) => (
|
||||||
return (
|
<StyledMenuItem key={index} value={slot.token} $isMobile={isMobile}>
|
||||||
<MenuItem key={index} value={slot.token}>
|
<MenuItemContent>
|
||||||
<Grid
|
<StyledMenuItemAvatar
|
||||||
container
|
|
||||||
direction='row'
|
|
||||||
justifyContent='flex-start'
|
|
||||||
alignItems='center'
|
|
||||||
style={{ height: '2.8em' }}
|
|
||||||
spacing={1}
|
|
||||||
>
|
|
||||||
<Grid item>
|
|
||||||
<RobotAvatar
|
|
||||||
hashId={slot?.hashId}
|
hashId={slot?.hashId}
|
||||||
smooth={true}
|
smooth={true}
|
||||||
style={{ width: '2.6em', height: '2.6em' }}
|
$isMobile={isMobile}
|
||||||
placeholderType='loading'
|
placeholderType='loading'
|
||||||
small={true}
|
small={true}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
<Typography variant={isMobile ? "body2" : "body1"}>{slot?.nickname}</Typography>
|
||||||
<Grid item>
|
</MenuItemContent>
|
||||||
<Typography variant={windowSize.width < 26 ? 'caption' : undefined}>
|
</StyledMenuItem>
|
||||||
{slot?.nickname}
|
))
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)}
|
)}
|
||||||
</Select>
|
</StyledSelect>
|
||||||
</Grid>
|
<ButtonContainer>
|
||||||
|
<StyledButton
|
||||||
<Grid item container direction='row' alignItems='center' justifyContent='space-evenly'>
|
$buttonColor={BUTTON_COLORS.primary}
|
||||||
<Grid item>
|
$hoverColor={BUTTON_COLORS.hoverPrimary}
|
||||||
<LoadingButton loading={loading} color='primary' onClick={handleAddRobot}>
|
$textColor={BUTTON_COLORS.text}
|
||||||
<Add /> <div style={{ width: '0.5em' }} />
|
$isMobile={isMobile}
|
||||||
{t('Add Robot')}
|
onClick={handleAddRobot}
|
||||||
</LoadingButton>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{window.NativeRobosats === undefined ? (
|
|
||||||
<Grid item>
|
|
||||||
<Button
|
|
||||||
color='primary'
|
|
||||||
onClick={() => {
|
|
||||||
garage.download();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Download />
|
<StyledAddIcon $isMobile={isMobile} /> {t('ADD ROBOT')}
|
||||||
</Button>
|
</StyledButton>
|
||||||
</Grid>
|
{window.NativeRobosats === undefined && (
|
||||||
) : null}
|
<StyledButton
|
||||||
|
$buttonColor={BUTTON_COLORS.secondary}
|
||||||
<Grid item>
|
$hoverColor={BUTTON_COLORS.hoverSecondary}
|
||||||
<Button
|
$textColor={BUTTON_COLORS.text}
|
||||||
color='primary'
|
$isMobile={isMobile}
|
||||||
|
onClick={() => garage.download()}
|
||||||
|
>
|
||||||
|
<StyledDownloadIcon $isMobile={isMobile} /> {t('DOWNLOAD')}
|
||||||
|
</StyledButton>
|
||||||
|
)}
|
||||||
|
<StyledButton
|
||||||
|
$buttonColor="transparent"
|
||||||
|
$hoverColor={BUTTON_COLORS.deleteHover}
|
||||||
|
$textColor="red"
|
||||||
|
$isMobile={isMobile}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
garage.delete();
|
garage.delete();
|
||||||
logoutRobot();
|
logoutRobot();
|
||||||
setView('welcome');
|
setView('welcome');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteSweep /> <div style={{ width: '0.5em' }} />
|
<StyledDeleteSweepIcon $isMobile={isMobile} /> {t('DELETE GARAGE')}
|
||||||
{t('Delete Garage')}
|
</StyledButton>
|
||||||
</Button>
|
</ButtonContainer>
|
||||||
</Grid>
|
</RightSection>
|
||||||
</Grid>
|
</ProfileContainer>
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Styled components
|
||||||
|
const ProfileContainer = styled(Box)<{ $isMobile: boolean }>(({ theme, $isMobile }) => ({
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: $isMobile ? '100%' : 1000,
|
||||||
|
margin: '0 auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: $isMobile ? 'column' : 'row',
|
||||||
|
border: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxShadow: $isMobile ? '4px 4px 0px #000000' : '8px 8px 0px #000000',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const InfoSection = styled(Box)<{ colors: typeof COLORS; $isMobile: boolean }>(({ theme, colors, $isMobile }) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
flexBasis: $isMobile ? 'auto' : 0,
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: $isMobile ? theme.spacing(2) : theme.spacing(4),
|
||||||
|
borderBottom: $isMobile ? '1px solid #000' : 'none',
|
||||||
|
borderRight: $isMobile ? 'none' : '2px solid #000',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const NicknameTypography = styled(Typography)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: $isMobile ? '8px' : '16px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const BoltIcon = styled(Bolt)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
color: '#fcba03',
|
||||||
|
height: $isMobile ? '0.8em' : '1em',
|
||||||
|
width: $isMobile ? '0.8em' : '1em',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledRobotAvatar = styled(RobotAvatar)({
|
||||||
|
'& img': {
|
||||||
|
border: '2px solid #555',
|
||||||
|
borderRadius: '50%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const StatusTypography = styled(Typography)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
marginBottom: $isMobile ? '8px' : '16px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledLinearProgress = styled(LinearProgress)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
width: '100%',
|
||||||
|
marginBottom: $isMobile ? '8px' : '16px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TokenBox = styled(Box)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
border: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||||
|
borderRadius: '4px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTextField = styled(TextField)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
'& .MuiInputBase-root': {
|
||||||
|
height: $isMobile ? '36px' : '48px',
|
||||||
|
padding: $isMobile ? '2px 4px' : '4px 8px',
|
||||||
|
fontSize: $isMobile ? '0.8rem' : '1rem',
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const RightSection = styled(Box)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
flexBasis: $isMobile ? 'auto' : 0,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TitleSection = styled(Box)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
borderBottom: `2px solid #000`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TitleTypography = styled(Typography)({
|
||||||
|
fontWeight: 'bold',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledSelect = styled(Select)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
width: '100%',
|
||||||
|
height: $isMobile ? '50px' : '80px',
|
||||||
|
borderBottom: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||||
|
borderRadius: 0,
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledMenuItem = styled(MenuItem)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
height: $isMobile ? '50px' : '80px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const MenuItemContent = styled(Box)({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledMenuItemAvatar = styled(RobotAvatar)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
width: $isMobile ? '24px' : '30px',
|
||||||
|
height: $isMobile ? '24px' : '30px',
|
||||||
|
marginRight: '8px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ButtonContainer = styled(Box)({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledButton = styled('button')<{
|
||||||
|
$buttonColor: string;
|
||||||
|
$hoverColor: string;
|
||||||
|
$textColor: string;
|
||||||
|
$isMobile: boolean;
|
||||||
|
}>(({ theme, $buttonColor, $hoverColor, $textColor, $isMobile }) => ({
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: 0,
|
||||||
|
backgroundColor: $buttonColor,
|
||||||
|
color: $textColor,
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
transition: 'background-color 0.3s ease, color 0.3s ease',
|
||||||
|
width: '100%',
|
||||||
|
height: $isMobile ? '40px' : '60px',
|
||||||
|
borderBottom: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: $hoverColor,
|
||||||
|
color: $buttonColor === 'transparent' ? '#fff' : $textColor,
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: $buttonColor === BUTTON_COLORS.primary ? BUTTON_COLORS.activePrimary : BUTTON_COLORS.activeSecondary,
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: 'none',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const CustomIconButton = styled('button')({
|
||||||
|
background: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
color: '#1976d2',
|
||||||
|
padding: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
transition: 'color 0.3s ease',
|
||||||
|
'&:hover': {
|
||||||
|
color: '#0d47a1',
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
color: '#002171',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledLogoutIcon = styled(Logout)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledFileCopyIcon = styled(FileCopy)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledAddIcon = styled(Add)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
marginRight: '8px',
|
||||||
|
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDownloadIcon = styled(Download)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
marginRight: '8px',
|
||||||
|
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDeleteSweepIcon = styled(DeleteSweep)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||||
|
marginRight: '8px',
|
||||||
|
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||||
|
}));
|
||||||
|
|
||||||
export default RobotProfile;
|
export default RobotProfile;
|
@ -1,7 +1,6 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Paper,
|
|
||||||
Grid,
|
Grid,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Box,
|
Box,
|
||||||
@ -129,7 +128,6 @@ const RobotPage = (): JSX.Element => {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<StyledMainBox>
|
<StyledMainBox>
|
||||||
<StyledPaper>
|
|
||||||
{view === 'welcome' && (
|
{view === 'welcome' && (
|
||||||
<Welcome setView={setView} getGenerateRobot={getGenerateRobot} width={1200} />
|
<Welcome setView={setView} getGenerateRobot={getGenerateRobot} width={1200} />
|
||||||
)}
|
)}
|
||||||
@ -164,7 +162,6 @@ const RobotPage = (): JSX.Element => {
|
|||||||
getRecoverRobot={getGenerateRobot}
|
getRecoverRobot={getGenerateRobot}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</StyledPaper>
|
|
||||||
</StyledMainBox>
|
</StyledMainBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -174,11 +171,13 @@ const RobotPage = (): JSX.Element => {
|
|||||||
const StyledConnectingBox = styled(Box)({
|
const StyledConnectingBox = styled(Box)({
|
||||||
width: '100vw',
|
width: '100vw',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
});
|
});
|
||||||
|
|
||||||
const StyledTorIconBox = styled(Box)({
|
const StyledTorIconBox = styled(Box)({
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: '4.6em',
|
top: '4.6em',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
});
|
});
|
||||||
|
|
||||||
const StyledMainBox = styled(Box)({
|
const StyledMainBox = styled(Box)({
|
||||||
@ -188,18 +187,9 @@ const StyledMainBox = styled(Box)({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: '2em',
|
padding: '2em',
|
||||||
});
|
|
||||||
|
|
||||||
const StyledPaper = styled(Paper)(({ theme }) => ({
|
|
||||||
width: '80vw',
|
|
||||||
maxWidth: '1200px',
|
|
||||||
maxHeight: '85vh',
|
|
||||||
overflow: 'auto',
|
|
||||||
overflowX: 'clip',
|
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
padding: '1em',
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
export default RobotPage;
|
export default RobotPage;
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { Button, Grid, List, ListItem, Paper, TextField, Typography } from '@mui/material';
|
import { Button, Grid, Paper, TextField, Typography, Box } from '@mui/material';
|
||||||
import SettingsForm from '../../components/SettingsForm';
|
import SettingsForm from '../../components/SettingsForm';
|
||||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||||
import FederationTable from '../../components/FederationTable';
|
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 { styled } from '@mui/system';
|
||||||
|
|
||||||
const SettingsPage = (): JSX.Element => {
|
const SettingsPage = (): JSX.Element => {
|
||||||
const { windowSize, navbarHeight } = useContext<UseAppStoreType>(AppContext);
|
const { windowSize, navbarHeight } = useContext<UseAppStoreType>(AppContext);
|
||||||
const { federation, addNewCoordinator } = useContext<UseFederationStoreType>(FederationContext);
|
const { federation, addNewCoordinator } = useContext<UseFederationStoreType>(FederationContext);
|
||||||
const maxHeight = (windowSize.height * 0.65)
|
const maxHeight = (windowSize.height * 0.65);
|
||||||
const [newAlias, setNewAlias] = useState<string>('');
|
const [newAlias, setNewAlias] = useState<string>('');
|
||||||
const [newUrl, setNewUrl] = useState<string>('');
|
const [newUrl, setNewUrl] = useState<string>('');
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>();
|
||||||
// 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 addCoordinator: () => void = () => {
|
const addCoordinator: () => void = () => {
|
||||||
@ -35,31 +36,22 @@ const SettingsPage = (): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<SettingsContainer elevation={12}>
|
||||||
elevation={12}
|
|
||||||
sx={{
|
|
||||||
padding: '0.6em',
|
|
||||||
width: '20.5em',
|
|
||||||
maxHeight: `${maxHeight}em`,
|
|
||||||
overflow: 'auto',
|
|
||||||
overflowX: 'clip',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item>
|
<LeftGrid item xs={12} md={6}>
|
||||||
<SettingsForm />
|
<SettingsForm />
|
||||||
</Grid>
|
</LeftGrid>
|
||||||
<Grid item>
|
<RightGrid item xs={12} md={6}>
|
||||||
|
<FederationTableWrapper>
|
||||||
<FederationTable maxHeight={18} />
|
<FederationTable maxHeight={18} />
|
||||||
</Grid>
|
</FederationTableWrapper>
|
||||||
<Grid item>
|
{error && (
|
||||||
<Typography align='center' component='h2' variant='subtitle2' color='secondary'>
|
<ErrorTypography align='center' component='h2' variant='subtitle2' color='secondary'>
|
||||||
{error}
|
{error}
|
||||||
</Typography>
|
</ErrorTypography>
|
||||||
</Grid>
|
)}
|
||||||
<List>
|
<InputContainer>
|
||||||
<ListItem>
|
<StyledTextField
|
||||||
<TextField
|
|
||||||
id='outlined-basic'
|
id='outlined-basic'
|
||||||
label={t('Alias')}
|
label={t('Alias')}
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
@ -69,7 +61,7 @@ const SettingsPage = (): JSX.Element => {
|
|||||||
setNewAlias(e.target.value);
|
setNewAlias(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<StyledTextField
|
||||||
id='outlined-basic'
|
id='outlined-basic'
|
||||||
label={t('URL')}
|
label={t('URL')}
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
@ -79,8 +71,7 @@ const SettingsPage = (): JSX.Element => {
|
|||||||
setNewUrl(e.target.value);
|
setNewUrl(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<StyledButton
|
||||||
sx={{ maxHeight: 38 }}
|
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onClick={addCoordinator}
|
onClick={addCoordinator}
|
||||||
variant='contained'
|
variant='contained'
|
||||||
@ -89,12 +80,83 @@ const SettingsPage = (): JSX.Element => {
|
|||||||
type='submit'
|
type='submit'
|
||||||
>
|
>
|
||||||
{t('Add')}
|
{t('Add')}
|
||||||
</Button>
|
</StyledButton>
|
||||||
</ListItem>
|
</InputContainer>
|
||||||
</List>
|
</RightGrid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</SettingsContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Styled Components
|
||||||
|
const SettingsContainer = styled(Paper)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
width: '80vw',
|
||||||
|
height: '70vh',
|
||||||
|
margin: '0 auto',
|
||||||
|
border: '2px solid #000',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxShadow: '8px 8px 0px #000',
|
||||||
|
[theme.breakpoints.down('md')]: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '90vw',
|
||||||
|
height: 'fit-content',
|
||||||
|
marginTop: '30rem',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const LeftGrid = styled(Grid)(({ theme }) => ({
|
||||||
|
padding: '2rem',
|
||||||
|
borderRight: '2px solid #000',
|
||||||
|
[theme.breakpoints.down('md')]: {
|
||||||
|
padding: '1rem',
|
||||||
|
borderRight: 'none',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const RightGrid = styled(Grid)(({ theme }) => ({
|
||||||
|
padding: '2rem',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
[theme.breakpoints.down('md')]: {
|
||||||
|
padding: '1rem',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const FederationTableWrapper = styled(Box)({
|
||||||
|
flexGrow: 1,
|
||||||
|
'& > *': {
|
||||||
|
width: '100% !important',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ErrorTypography = styled(Typography)({
|
||||||
|
// You can add specific styles for the error message here if needed
|
||||||
|
});
|
||||||
|
|
||||||
|
const InputContainer = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTextField = styled(TextField)(({ theme }) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledButton = styled(Button)(({ theme }) => ({
|
||||||
|
maxHeight: 40,
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
export default SettingsPage;
|
export default SettingsPage;
|
@ -1,78 +1,38 @@
|
|||||||
import React, { useCallback, useEffect, useState, useContext, useMemo } from 'react';
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Box, useTheme, Checkbox, CircularProgress, Typography, Grid } from '@mui/material';
|
import { Box, Checkbox, CircularProgress, Grid, Typography } from '@mui/material';
|
||||||
import { DataGrid, type GridColDef, type GridValidRowModel } from '@mui/x-data-grid';
|
import { DataGrid, type GridColDef, type GridValidRowModel } from '@mui/x-data-grid';
|
||||||
import { type Coordinator } from '../../models';
|
import { type Coordinator } from '../../models';
|
||||||
import RobotAvatar from '../RobotAvatar';
|
import RobotAvatar from '../RobotAvatar';
|
||||||
import { Link, LinkOff } from '@mui/icons-material';
|
import { Link, LinkOff } from '@mui/icons-material';
|
||||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||||
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||||
import headerStyleFix from '../DataGrid/HeaderFix';
|
import { styled } from '@mui/system';
|
||||||
|
|
||||||
interface FederationTableProps {
|
interface FederationTableProps {
|
||||||
maxWidth?: number;
|
maxWidth?: number;
|
||||||
maxHeight?: number;
|
|
||||||
fillContainer?: boolean;
|
fillContainer?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FederationTable = ({
|
const FederationTable = ({ maxWidth = 90, fillContainer = false }: FederationTableProps): JSX.Element => {
|
||||||
maxWidth = 90,
|
|
||||||
maxHeight = 50,
|
|
||||||
fillContainer = false,
|
|
||||||
}: FederationTableProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { federation, sortedCoordinators, coordinatorUpdatedAt, federationUpdatedAt } =
|
const { federation, sortedCoordinators, coordinatorUpdatedAt } = useContext<UseFederationStoreType>(FederationContext);
|
||||||
useContext<UseFederationStoreType>(FederationContext);
|
|
||||||
const { setOpen, settings } = useContext<UseAppStoreType>(AppContext);
|
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);
|
|
||||||
}
|
|
||||||
}, [coordinatorUpdatedAt, 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) => {
|
const aliasObj = useCallback((width: number) => {
|
||||||
return {
|
return {
|
||||||
field: 'longAlias',
|
field: 'longAlias',
|
||||||
headerName: t('Coordinator'),
|
headerName: t('Coordinator'),
|
||||||
width: width * fontSize,
|
width: width,
|
||||||
renderCell: (params: any) => {
|
renderCell: (params: any) => {
|
||||||
const coordinator = federation.coordinators[params.row.shortAlias];
|
const coordinator = federation.coordinators[params.row.shortAlias];
|
||||||
return (
|
return (
|
||||||
<Grid
|
<CoordinatorGrid
|
||||||
container
|
container
|
||||||
direction='row'
|
direction="row"
|
||||||
sx={{ cursor: 'pointer', position: 'relative', left: '-0.3em', width: '50em' }}
|
wrap="nowrap"
|
||||||
wrap='nowrap'
|
onClick={() => onClickCoordinator(params.row.shortAlias)}
|
||||||
onClick={() => {
|
alignItems="center"
|
||||||
onClickCoordinator(params.row.shortAlias);
|
|
||||||
}}
|
|
||||||
alignItems='center'
|
|
||||||
spacing={1}
|
spacing={1}
|
||||||
>
|
>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
@ -87,31 +47,29 @@ const FederationTable = ({
|
|||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography>{params.row.longAlias}</Typography>
|
<Typography>{params.row.longAlias}</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</CoordinatorGrid>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, []);
|
}, [federation.coordinators]);
|
||||||
|
|
||||||
const enabledObj = useCallback(
|
const enabledObj = useCallback(
|
||||||
(width: number) => {
|
(width: number) => {
|
||||||
return {
|
return {
|
||||||
field: 'enabled',
|
field: 'enabled',
|
||||||
headerName: t('Enabled'),
|
headerName: t('Enabled'),
|
||||||
width: width * fontSize,
|
width: width,
|
||||||
renderCell: (params: any) => {
|
renderCell: (params: any) => {
|
||||||
return (
|
return (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={params.row.enabled}
|
checked={params.row.enabled}
|
||||||
onClick={() => {
|
onClick={() => onEnableChange(params.row.shortAlias)}
|
||||||
onEnableChange(params.row.shortAlias);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[coordinatorUpdatedAt],
|
[coordinatorUpdatedAt]
|
||||||
);
|
);
|
||||||
|
|
||||||
const upObj = useCallback(
|
const upObj = useCallback(
|
||||||
@ -119,52 +77,41 @@ const FederationTable = ({
|
|||||||
return {
|
return {
|
||||||
field: 'up',
|
field: 'up',
|
||||||
headerName: t('Up'),
|
headerName: t('Up'),
|
||||||
width: width * fontSize,
|
width: width,
|
||||||
renderCell: (params: any) => {
|
renderCell: (params: any) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<UpStatusContainer onClick={() => onClickCoordinator(params.row.shortAlias)}>
|
||||||
style={{ cursor: 'pointer' }}
|
{params.row.loadingInfo && params.row.enabled ? (
|
||||||
onClick={() => {
|
<CircularProgress thickness={2} size={24} />
|
||||||
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 ? (
|
) : params.row.info !== undefined ? (
|
||||||
<Link color='success' />
|
<Link color="success" />
|
||||||
) : (
|
) : (
|
||||||
<LinkOff color='error' />
|
<LinkOff color="error" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</UpStatusContainer>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[coordinatorUpdatedAt],
|
[coordinatorUpdatedAt]
|
||||||
);
|
);
|
||||||
|
|
||||||
const columnSpecs = {
|
const columnSpecs = {
|
||||||
alias: {
|
alias: {
|
||||||
priority: 2,
|
|
||||||
order: 1,
|
|
||||||
normal: {
|
normal: {
|
||||||
width: 12.1,
|
width: 200,
|
||||||
object: aliasObj,
|
object: aliasObj,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
up: {
|
up: {
|
||||||
priority: 3,
|
|
||||||
order: 2,
|
|
||||||
normal: {
|
normal: {
|
||||||
width: 3.5,
|
width: 70,
|
||||||
object: upObj,
|
object: upObj,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
enabled: {
|
enabled: {
|
||||||
priority: 1,
|
|
||||||
order: 3,
|
|
||||||
normal: {
|
normal: {
|
||||||
width: 5,
|
width: 90,
|
||||||
object: enabledObj,
|
object: enabledObj,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -174,28 +121,16 @@ const FederationTable = ({
|
|||||||
columns: Array<GridColDef<GridValidRowModel>>;
|
columns: Array<GridColDef<GridValidRowModel>>;
|
||||||
width: number;
|
width: number;
|
||||||
} {
|
} {
|
||||||
const useSmall = maxWidth < 30;
|
|
||||||
const selectedColumns: object[] = [];
|
const selectedColumns: object[] = [];
|
||||||
let width: number = 0;
|
let width: number = 0;
|
||||||
|
|
||||||
for (const value of Object.values(columnSpecs)) {
|
for (const value of Object.values(columnSpecs)) {
|
||||||
const colWidth = Number(
|
const colWidth = value.normal.width;
|
||||||
useSmall && Boolean(value.small) ? value.small.width : value.normal.width,
|
const colObject = value.normal.object;
|
||||||
);
|
|
||||||
const colObject = useSmall && Boolean(value.small) ? value.small.object : value.normal.object;
|
|
||||||
|
|
||||||
if (width + colWidth < maxWidth || selectedColumns.length < 2) {
|
width += colWidth;
|
||||||
width = width + colWidth;
|
selectedColumns.push([colObject(colWidth), value.order]);
|
||||||
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) {
|
const columns: Array<GridColDef<GridValidRowModel>> = selectedColumns.map(function (item) {
|
||||||
return item[0];
|
return item[0];
|
||||||
@ -204,7 +139,7 @@ const FederationTable = ({
|
|||||||
return { columns, width: width * 0.9 };
|
return { columns, width: width * 0.9 };
|
||||||
};
|
};
|
||||||
|
|
||||||
const { columns, width } = filteredColumns();
|
const { columns } = filteredColumns();
|
||||||
|
|
||||||
const onEnableChange = function (shortAlias: string): void {
|
const onEnableChange = function (shortAlias: string): void {
|
||||||
if (federation.getCoordinator(shortAlias).enabled === true) {
|
if (federation.getCoordinator(shortAlias).enabled === true) {
|
||||||
@ -217,38 +152,65 @@ const FederationTable = ({
|
|||||||
const reorderedCoordinators = useMemo(() => {
|
const reorderedCoordinators = useMemo(() => {
|
||||||
return sortedCoordinators.reduce((coordinators, key) => {
|
return sortedCoordinators.reduce((coordinators, key) => {
|
||||||
coordinators[key] = federation.coordinators[key];
|
coordinators[key] = federation.coordinators[key];
|
||||||
|
|
||||||
return coordinators;
|
return coordinators;
|
||||||
}, {});
|
}, {});
|
||||||
}, [settings.network, federationUpdatedAt]);
|
}, [settings.network, coordinatorUpdatedAt]);
|
||||||
|
|
||||||
|
const onClickCoordinator = (shortAlias: string): void => {
|
||||||
|
setOpen((open) => {
|
||||||
|
return { ...open, coordinator: shortAlias };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<TableContainer fillContainer={fillContainer} maxWidth={maxWidth}>
|
||||||
sx={
|
<StyledDataGrid
|
||||||
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)}
|
rows={Object.values(reorderedCoordinators)}
|
||||||
getRowId={(params: Coordinator) => params.shortAlias}
|
getRowId={(params: Coordinator) => params.shortAlias}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
checkboxSelection={false}
|
checkboxSelection={false}
|
||||||
pageSize={pageSize}
|
autoHeight
|
||||||
rowsPerPageOptions={width < 22 ? [] : [0, pageSize, defaultPageSize * 2, 50, 100]}
|
hideFooter
|
||||||
onPageSizeChange={(newPageSize) => {
|
|
||||||
setPageSize(newPageSize);
|
|
||||||
setUseDefaultPageSize(false);
|
|
||||||
}}
|
|
||||||
hideFooter={true}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</TableContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Styled Components
|
||||||
|
const TableContainer = styled(Box, {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'fillContainer' && prop !== 'maxWidth',
|
||||||
|
})<{ fillContainer: boolean; maxWidth: number }>(({ theme, fillContainer, maxWidth }) => ({
|
||||||
|
width: fillContainer ? '100%' : `${maxWidth}em`,
|
||||||
|
height: 'fit-content',
|
||||||
|
border: '2px solid black',
|
||||||
|
overflow: 'hidden',
|
||||||
|
borderRadius: '8px',
|
||||||
|
boxShadow: 'none',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDataGrid = styled(DataGrid)(({ theme }) => ({
|
||||||
|
'& .MuiDataGrid-root': {
|
||||||
|
fontSize: { xs: '0.8rem', sm: '0.9rem', md: '1rem' },
|
||||||
|
},
|
||||||
|
'& .MuiDataGrid-cell': {
|
||||||
|
padding: { xs: '0.5rem', sm: '1rem' },
|
||||||
|
},
|
||||||
|
'& .MuiDataGrid-columnHeaders': {
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
borderBottom: '2px solid black',
|
||||||
|
fontSize: { xs: '0.8rem', sm: '0.9rem', md: '1rem' },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const CoordinatorGrid = styled(Grid)({
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'relative',
|
||||||
|
left: '-0.3em',
|
||||||
|
width: '50em',
|
||||||
|
});
|
||||||
|
|
||||||
|
const UpStatusContainer = styled('div')({
|
||||||
|
cursor: 'pointer',
|
||||||
|
});
|
||||||
|
|
||||||
export default FederationTable;
|
export default FederationTable;
|
@ -1,36 +1,36 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
import { type UseAppStoreType, AppContext } from '../../contexts/AppContext';
|
import { type UseAppStoreType, AppContext } from '../../contexts/AppContext';
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
|
||||||
Switch,
|
Switch,
|
||||||
useTheme,
|
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
Slider,
|
|
||||||
Typography,
|
Typography,
|
||||||
ToggleButtonGroup,
|
ToggleButtonGroup,
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
|
Box,
|
||||||
|
Slider,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import SelectLanguage from './SelectLanguage';
|
import SelectLanguage from './SelectLanguage';
|
||||||
import {
|
import {
|
||||||
Translate,
|
Translate,
|
||||||
Palette,
|
Palette,
|
||||||
LightMode,
|
|
||||||
DarkMode,
|
|
||||||
SettingsOverscan,
|
SettingsOverscan,
|
||||||
Link,
|
Link,
|
||||||
AccountBalance,
|
AccountBalance,
|
||||||
AttachMoney,
|
AttachMoney,
|
||||||
QrCode,
|
QrCode,
|
||||||
|
DarkMode,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { systemClient } from '../../services/System';
|
import { systemClient } from '../../services/System';
|
||||||
import { TorIcon } from '../Icons';
|
import { TorIcon } from '../Icons';
|
||||||
import SwapCalls from '@mui/icons-material/SwapCalls';
|
import SwapCalls from '@mui/icons-material/SwapCalls';
|
||||||
import { apiClient } from '../../services/api';
|
import { apiClient } from '../../services/api';
|
||||||
|
import { styled } from '@mui/system';
|
||||||
|
|
||||||
interface SettingsFormProps {
|
interface SettingsFormProps {
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
@ -38,8 +38,9 @@ interface SettingsFormProps {
|
|||||||
|
|
||||||
const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||||
const { fav, setFav, settings, setSettings } = useContext<UseAppStoreType>(AppContext);
|
const { fav, setFav, settings, setSettings } = useContext<UseAppStoreType>(AppContext);
|
||||||
const theme = useTheme();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const fontSizes = [
|
const fontSizes = [
|
||||||
{ label: 'XS', value: { basic: 12, pro: 10 } },
|
{ label: 'XS', value: { basic: 12, pro: 10 } },
|
||||||
{ label: 'S', value: { basic: 13, pro: 11 } },
|
{ label: 'S', value: { basic: 13, pro: 11 } },
|
||||||
@ -48,64 +49,59 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
|||||||
{ label: 'XL', value: { basic: 16, pro: 14 } },
|
{ label: 'XL', value: { basic: 16, pro: 14 } },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleToggleChange = (e, newValue) => {
|
||||||
|
if (newValue !== null) {
|
||||||
|
setFav({ ...fav, mode: newValue, currency: newValue === 'fiat' ? 0 : 1000 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNetworkChange = (e, newValue) => {
|
||||||
|
if (newValue !== null) {
|
||||||
|
setSettings({ ...settings, network: newValue });
|
||||||
|
systemClient.setItem('settings_network', newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={2}>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<List dense={dense}>
|
<List dense={dense}>
|
||||||
<ListItem>
|
{/* Language Settings */}
|
||||||
|
<StyledListItem>
|
||||||
|
<SettingHeader>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Translate />
|
<Translate />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<SelectLanguage
|
<Typography variant="subtitle1">
|
||||||
|
{t('Language Settings')}
|
||||||
|
</Typography>
|
||||||
|
</SettingHeader>
|
||||||
|
<StyledSelectLanguage
|
||||||
language={settings.language}
|
language={settings.language}
|
||||||
setLanguage={(language) => {
|
setLanguage={(language) => {
|
||||||
setSettings({ ...settings, language });
|
setSettings({ ...settings, language });
|
||||||
systemClient.setItem('settings_language', language);
|
systemClient.setItem('settings_language', language);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</StyledListItem>
|
||||||
|
|
||||||
<ListItem>
|
{/* Appearance Settings */}
|
||||||
|
<StyledListItem>
|
||||||
|
<SettingHeader>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Palette />
|
<Palette />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
<Typography variant="subtitle1">
|
||||||
|
{t('Appearance Settings')}
|
||||||
|
</Typography>
|
||||||
|
</SettingHeader>
|
||||||
|
<AppearanceSettingsBox>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
labelPlacement='end'
|
labelPlacement="end"
|
||||||
label={settings.mode === 'dark' ? t('Dark') : t('Light')}
|
label={t('Dark Mode')}
|
||||||
control={
|
control={
|
||||||
<Switch
|
<Switch
|
||||||
checked={settings.mode === 'dark'}
|
checked={settings.mode === 'dark'}
|
||||||
checkedIcon={
|
|
||||||
<Paper
|
|
||||||
elevation={3}
|
|
||||||
sx={{
|
|
||||||
width: '1.2em',
|
|
||||||
height: '1.2em',
|
|
||||||
borderRadius: '0.4em',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
position: 'relative',
|
|
||||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DarkMode sx={{ width: '0.8em', height: '0.8em', color: '#666' }} />
|
|
||||||
</Paper>
|
|
||||||
}
|
|
||||||
icon={
|
|
||||||
<Paper
|
|
||||||
elevation={3}
|
|
||||||
sx={{
|
|
||||||
width: '1.2em',
|
|
||||||
height: '1.2em',
|
|
||||||
borderRadius: '0.4em',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
padding: '0.07em',
|
|
||||||
position: 'relative',
|
|
||||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<LightMode sx={{ width: '0.67em', height: '0.67em', color: '#666' }} />
|
|
||||||
</Paper>
|
|
||||||
}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const mode = e.target.checked ? 'dark' : 'light';
|
const mode = e.target.checked ? 'dark' : 'light';
|
||||||
setSettings({ ...settings, mode });
|
setSettings({ ...settings, mode });
|
||||||
@ -114,49 +110,13 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{settings.mode === 'dark' ? (
|
{settings.mode === 'dark' && (
|
||||||
<>
|
<QRCodeSwitch
|
||||||
<ListItemIcon>
|
labelPlacement="end"
|
||||||
<QrCode />
|
label={t('QR Code Color')}
|
||||||
</ListItemIcon>
|
|
||||||
<FormControlLabel
|
|
||||||
sx={{ position: 'relative', right: '1.5em', width: '3em' }}
|
|
||||||
labelPlacement='end'
|
|
||||||
label={settings.lightQRs ? t('Light') : t('Dark')}
|
|
||||||
control={
|
control={
|
||||||
<Switch
|
<Switch
|
||||||
checked={!settings.lightQRs}
|
checked={!settings.lightQRs}
|
||||||
checkedIcon={
|
|
||||||
<Paper
|
|
||||||
elevation={3}
|
|
||||||
sx={{
|
|
||||||
width: '1.2em',
|
|
||||||
height: '1.2em',
|
|
||||||
borderRadius: '0.4em',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
position: 'relative',
|
|
||||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DarkMode sx={{ width: '0.8em', height: '0.8em', color: '#666' }} />
|
|
||||||
</Paper>
|
|
||||||
}
|
|
||||||
icon={
|
|
||||||
<Paper
|
|
||||||
elevation={3}
|
|
||||||
sx={{
|
|
||||||
width: '1.2em',
|
|
||||||
height: '1.2em',
|
|
||||||
borderRadius: '0.4em',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
padding: '0.07em',
|
|
||||||
position: 'relative',
|
|
||||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<LightMode sx={{ width: '0.67em', height: '0.67em', color: '#666' }} />
|
|
||||||
</Paper>
|
|
||||||
}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const lightQRs = !e.target.checked;
|
const lightQRs = !e.target.checked;
|
||||||
setSettings({ ...settings, lightQRs });
|
setSettings({ ...settings, lightQRs });
|
||||||
@ -165,17 +125,21 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
</ListItem>
|
</AppearanceSettingsBox>
|
||||||
|
</StyledListItem>
|
||||||
|
|
||||||
<ListItem>
|
{/* Font Size Settings */}
|
||||||
|
<StyledListItem>
|
||||||
|
<SettingHeader>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<SettingsOverscan />
|
<SettingsOverscan />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<Slider
|
<Typography variant="subtitle1">
|
||||||
|
{t('Font Size')}
|
||||||
|
</Typography>
|
||||||
|
</SettingHeader>
|
||||||
|
<StyledSlider
|
||||||
value={settings.fontSize}
|
value={settings.fontSize}
|
||||||
min={settings.frontend === 'basic' ? 12 : 10}
|
min={settings.frontend === 'basic' ? 12 : 10}
|
||||||
max={settings.frontend === 'basic' ? 16 : 14}
|
max={settings.frontend === 'basic' ? 16 : 14}
|
||||||
@ -185,80 +149,98 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
|||||||
setSettings({ ...settings, fontSize });
|
setSettings({ ...settings, fontSize });
|
||||||
systemClient.setItem(`settings_fontsize_${settings.frontend}`, fontSize.toString());
|
systemClient.setItem(`settings_fontsize_${settings.frontend}`, fontSize.toString());
|
||||||
}}
|
}}
|
||||||
valueLabelDisplay='off'
|
valueLabelDisplay="off"
|
||||||
|
track={false}
|
||||||
marks={fontSizes.map(({ label, value }) => ({
|
marks={fontSizes.map(({ label, value }) => ({
|
||||||
label: <Typography variant='caption'>{t(label)}</Typography>,
|
label: <Typography variant="caption">{t(label)}</Typography>,
|
||||||
value: settings.frontend === 'basic' ? value.basic : value.pro,
|
value: settings.frontend === 'basic' ? value.basic : value.pro,
|
||||||
}))}
|
}))}
|
||||||
track={false}
|
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</StyledListItem>
|
||||||
|
|
||||||
<ListItem>
|
{/* Currency Settings */}
|
||||||
|
<StyledListItem>
|
||||||
|
<SettingHeader>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<AccountBalance />
|
<AccountBalance />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ToggleButtonGroup
|
<Typography variant="subtitle1">
|
||||||
|
{t('Currency Settings')}
|
||||||
|
</Typography>
|
||||||
|
</SettingHeader>
|
||||||
|
<StyledToggleButtonGroup
|
||||||
exclusive={true}
|
exclusive={true}
|
||||||
value={fav.mode}
|
value={fav.mode}
|
||||||
onChange={(e, mode) => {
|
onChange={handleToggleChange}
|
||||||
setFav({ ...fav, mode, currency: mode === 'fiat' ? 0 : 1000 });
|
fullWidth
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ToggleButton value='fiat' color='primary'>
|
<StyledToggleButton value="fiat">
|
||||||
<AttachMoney />
|
<AttachMoney />
|
||||||
{t('Fiat')}
|
{t('Fiat')}
|
||||||
</ToggleButton>
|
</StyledToggleButton>
|
||||||
<ToggleButton value='swap' color='secondary'>
|
<StyledToggleButton value="swap">
|
||||||
<SwapCalls />
|
<SwapCalls />
|
||||||
{t('Swaps')}
|
{t('Swaps')}
|
||||||
</ToggleButton>
|
</StyledToggleButton>
|
||||||
</ToggleButtonGroup>
|
</StyledToggleButtonGroup>
|
||||||
</ListItem>
|
</StyledListItem>
|
||||||
|
|
||||||
<ListItem>
|
{/* Network Settings */}
|
||||||
|
<StyledListItem>
|
||||||
|
<SettingHeader>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Link />
|
<Link />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ToggleButtonGroup
|
<Typography variant="subtitle1">
|
||||||
|
{t('Network Settings')}
|
||||||
|
</Typography>
|
||||||
|
</SettingHeader>
|
||||||
|
<StyledToggleButtonGroup
|
||||||
exclusive={true}
|
exclusive={true}
|
||||||
value={settings.network}
|
value={settings.network}
|
||||||
onChange={(e, network) => {
|
onChange={handleNetworkChange}
|
||||||
setSettings({ ...settings, network });
|
fullWidth
|
||||||
systemClient.setItem('settings_network', network);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ToggleButton value='mainnet' color='primary'>
|
<StyledToggleButton value="mainnet">
|
||||||
{t('Mainnet')}
|
{t('Mainnet')}
|
||||||
</ToggleButton>
|
</StyledToggleButton>
|
||||||
<ToggleButton value='testnet' color='secondary'>
|
<StyledToggleButton value="testnet">
|
||||||
{t('Testnet')}
|
{t('Testnet')}
|
||||||
</ToggleButton>
|
</StyledToggleButton>
|
||||||
</ToggleButtonGroup>
|
</StyledToggleButtonGroup>
|
||||||
</ListItem>
|
</StyledListItem>
|
||||||
|
|
||||||
|
{/* Proxy Settings */}
|
||||||
{window.NativeRobosats !== undefined && (
|
{window.NativeRobosats !== undefined && (
|
||||||
<ListItem>
|
<StyledListItem>
|
||||||
|
<SettingHeader>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<TorIcon />
|
<TorIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ToggleButtonGroup
|
<Typography variant="subtitle1">
|
||||||
|
{t('Proxy Settings')}
|
||||||
|
</Typography>
|
||||||
|
</SettingHeader>
|
||||||
|
<StyledToggleButtonGroup
|
||||||
exclusive={true}
|
exclusive={true}
|
||||||
value={settings.useProxy}
|
value={settings.useProxy}
|
||||||
onChange={(_e, useProxy) => {
|
onChange={(_e, useProxy) => {
|
||||||
|
if (useProxy !== null) {
|
||||||
setSettings({ ...settings, useProxy });
|
setSettings({ ...settings, useProxy });
|
||||||
systemClient.setItem('settings_use_proxy', String(useProxy));
|
systemClient.setItem('settings_use_proxy', String(useProxy));
|
||||||
apiClient.useProxy = useProxy;
|
apiClient.useProxy = useProxy;
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
fullWidth
|
||||||
>
|
>
|
||||||
<ToggleButton value={true} color='primary'>
|
<StyledToggleButton value={true}>
|
||||||
{t('Build-in')}
|
{t('Built-in')}
|
||||||
</ToggleButton>
|
</StyledToggleButton>
|
||||||
<ToggleButton value={false} color='secondary'>
|
<StyledToggleButton value={false}>
|
||||||
{t('Disabled')}
|
{t('Disabled')}
|
||||||
</ToggleButton>
|
</StyledToggleButton>
|
||||||
</ToggleButtonGroup>
|
</StyledToggleButtonGroup>
|
||||||
</ListItem>
|
</StyledListItem>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -266,4 +248,79 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Styled Components
|
||||||
|
const StyledListItem = styled(ListItem)({
|
||||||
|
display: 'block',
|
||||||
|
});
|
||||||
|
|
||||||
|
const SettingHeader = styled(Box)({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: '0.5em',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledSelectLanguage = styled(SelectLanguage)(({ theme }) => ({
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '2px solid black',
|
||||||
|
boxShadow: '4px 4px 0px rgba(0, 0, 0, 1)',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const AppearanceSettingsBox = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
[theme.breakpoints.up('sm')]: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const QRCodeSwitch = styled(FormControlLabel)(({ theme }) => ({
|
||||||
|
marginLeft: 0,
|
||||||
|
[theme.breakpoints.up('sm')]: {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledSlider = styled(Slider)(({ theme }) => ({
|
||||||
|
'& .MuiSlider-thumb': {
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '2px solid black',
|
||||||
|
},
|
||||||
|
'& .MuiSlider-track': {
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '2px solid black',
|
||||||
|
},
|
||||||
|
'& .MuiSlider-rail': {
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '2px solid black',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledToggleButtonGroup = styled(ToggleButtonGroup)({
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledToggleButton = styled(ToggleButton)(({ theme }) => ({
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '2px solid black',
|
||||||
|
boxShadow: 'none',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
width: '100%',
|
||||||
|
'&.Mui-selected': {
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'initial',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
export default SettingsForm;
|
export default SettingsForm;
|
Loading…
Reference in New Issue
Block a user