mirror of
https://github.com/RoboSats/robosats.git
synced 2025-02-20 12:19:00 +00:00
Adapt garage to coordinators
This commit is contained in:
parent
e870117a2c
commit
e8ec7f989a
@ -31,7 +31,7 @@ const BookPage = (): JSX.Element => {
|
||||
const chartWidthEm = width - maxBookTableWidth;
|
||||
|
||||
const onOrderClicked = function (id: number, shortAlias: string): void {
|
||||
if (garage.getSlot().robot.avatarLoaded) {
|
||||
if (garage.getSlot()?.avatarLoaded) {
|
||||
setDelay(10000);
|
||||
navigate(`/order/${shortAlias}/${id}`);
|
||||
} else {
|
||||
|
@ -40,22 +40,24 @@ const Main: React.FC = () => {
|
||||
setAvatarBaseUrl(federation.getCoordinator(sortedCoordinators[0]).getBaseUrl());
|
||||
}, [settings.network, settings.selfhostedClient, federation, sortedCoordinators]);
|
||||
|
||||
const onLoad = () => {
|
||||
garage.updateSlot({ avatarLoaded: true });
|
||||
};
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RobotAvatar
|
||||
style={{ display: 'none' }}
|
||||
nickname={garage.getSlot().robot.nickname}
|
||||
nickname={garage.getSlot()?.getRobot()?.nickname}
|
||||
baseUrl={federation.getCoordinator(sortedCoordinators[0]).getBaseUrl()}
|
||||
onLoad={() => {
|
||||
garage.updateRobot({ avatarLoaded: true });
|
||||
}}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
<Notifications
|
||||
page={page}
|
||||
openProfile={() => {
|
||||
setOpen({ ...closeAll, profile: true });
|
||||
}}
|
||||
rewards={garage.getSlot().robot.earnedRewards}
|
||||
rewards={garage.getSlot()?.getRobot()?.earnedRewards}
|
||||
windowWidth={windowSize?.width}
|
||||
/>
|
||||
{settings.network === 'testnet' ? (
|
||||
|
@ -51,7 +51,7 @@ const MakerPage = (): JSX.Element => {
|
||||
]);
|
||||
|
||||
const onOrderClicked = function (id: number): void {
|
||||
if (garage.getSlot().robot.avatarLoaded) {
|
||||
if (garage.getSlot()?.avatarLoaded) {
|
||||
navigate(`/order/${id}`);
|
||||
} else {
|
||||
setOpenNoRobot(true);
|
||||
|
@ -42,7 +42,7 @@ const NavBar = (): JSX.Element => {
|
||||
const tabSx = smallBar
|
||||
? {
|
||||
position: 'relative',
|
||||
bottom: garage.getSlot().robot.avatarLoaded ? '0.9em' : '0.13em',
|
||||
bottom: garage.getSlot()?.avatarLoaded ? '0.9em' : '0.13em',
|
||||
minWidth: '1em',
|
||||
}
|
||||
: { position: 'relative', bottom: '1em', minWidth: '2em' };
|
||||
@ -82,8 +82,9 @@ const NavBar = (): JSX.Element => {
|
||||
setPage(newPage);
|
||||
const param =
|
||||
newPage === 'order'
|
||||
? `${String(slot.activeOrderShortAlias)}/${String(
|
||||
slot.activeOrderId ?? slot.lastOrderId,
|
||||
? `${String(slot?.activeShortAlias)}/${String(
|
||||
slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId ??
|
||||
slot?.getRobot(slot?.lastShortAlias ?? '')?.lastOrderId,
|
||||
)}`
|
||||
: '';
|
||||
setTimeout(() => {
|
||||
@ -96,6 +97,8 @@ const NavBar = (): JSX.Element => {
|
||||
setOpen(closeAll);
|
||||
}, [page, setOpen]);
|
||||
|
||||
const slot = garage.getSlot();
|
||||
|
||||
return (
|
||||
<Paper
|
||||
elevation={6}
|
||||
@ -118,16 +121,16 @@ const NavBar = (): JSX.Element => {
|
||||
<Tab
|
||||
sx={{ ...tabSx, minWidth: '2.5em', width: '2.5em', maxWidth: '4em' }}
|
||||
value='none'
|
||||
disabled={garage.getSlot().robot.nickname === null}
|
||||
disabled={slot?.getRobot()?.nickname === null}
|
||||
onClick={() => {
|
||||
setOpen({ ...closeAll, profile: !open.profile });
|
||||
}}
|
||||
icon={
|
||||
garage.getSlot().robot.nickname != null && garage.getSlot().robot.avatarLoaded ? (
|
||||
slot?.getRobot()?.nickname != null && slot?.avatarLoaded ? (
|
||||
<RobotAvatar
|
||||
style={{ width: '2.3em', height: '2.3em', position: 'relative', top: '0.2em' }}
|
||||
avatarClass={theme.palette.mode === 'dark' ? 'navBarAvatarDark' : 'navBarAvatar'}
|
||||
nickname={garage.getSlot().robot.nickname}
|
||||
nickname={slot?.getRobot()?.nickname}
|
||||
baseUrl={hostUrl}
|
||||
/>
|
||||
) : (
|
||||
@ -162,7 +165,9 @@ const NavBar = (): JSX.Element => {
|
||||
sx={tabSx}
|
||||
label={smallBar ? undefined : t('Order')}
|
||||
value='order'
|
||||
disabled={!garage.getSlot().robot.avatarLoaded || !garage.getSlot().activeOrderId}
|
||||
disabled={
|
||||
!slot?.avatarLoaded || !slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId
|
||||
}
|
||||
icon={<Assignment />}
|
||||
iconPosition='start'
|
||||
/>
|
||||
|
@ -26,6 +26,7 @@ const OrderPage = (): JSX.Element => {
|
||||
const [tab, setTab] = useState<'order' | 'contract'>('contract');
|
||||
const [baseUrl, setBaseUrl] = useState<string>(hostUrl);
|
||||
const [currentOrder, setCurrentOrder] = useState<Order | null>(null);
|
||||
const [currentOrderId, setCurrentOrderId] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const coordinator = federation.getCoordinator(params.shortAlias ?? '');
|
||||
@ -38,30 +39,41 @@ const OrderPage = (): JSX.Element => {
|
||||
|
||||
setBaseUrl(`${url}${basePath}`);
|
||||
|
||||
if (currentOrder?.id !== Number(params.orderId)) {
|
||||
const coordinator = federation.getCoordinator(params.shortAlias ?? '');
|
||||
coordinator
|
||||
.fetchOrder(Number(params.orderId) ?? null, garage.getSlot().robot)
|
||||
.then((order) => {
|
||||
if (order?.bad_request !== undefined) {
|
||||
setBadOrder(order.bad_request);
|
||||
} else if (order !== null && order?.id !== null) {
|
||||
setCurrentOrder(order);
|
||||
if (order.is_participant) {
|
||||
garage.updateOrder(order as Order);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
const orderId = Number(params.orderId);
|
||||
if (orderId && currentOrderId !== orderId) setCurrentOrderId(orderId);
|
||||
}, [params]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentOrder(null);
|
||||
if (currentOrderId !== null) {
|
||||
const coordinator = federation.getCoordinator(params.shortAlias ?? '');
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot();
|
||||
if (robot && slot?.token) {
|
||||
federation.fetchRobot(garage, slot.token);
|
||||
coordinator
|
||||
.fetchOrder(currentOrderId, robot)
|
||||
.then((order) => {
|
||||
if (order?.bad_request !== undefined) {
|
||||
setBadOrder(order.bad_request);
|
||||
} else if (order !== null && order?.id !== null) {
|
||||
setCurrentOrder(order);
|
||||
if (order.is_participant) {
|
||||
garage.updateOrder(order as Order);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [currentOrderId]);
|
||||
|
||||
const onClickCoordinator = function (): void {
|
||||
if (currentOrder?.shortAlias != null) {
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: shortAlias };
|
||||
return { ...open, coordinator: currentOrder?.shortAlias };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -61,6 +61,9 @@ const Onboarding = ({
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Accordion expanded={step === '1'} disableGutters={true}>
|
||||
@ -122,7 +125,6 @@ const Onboarding = ({
|
||||
onClick={() => {
|
||||
setStep('2');
|
||||
getGenerateRobot(inputToken);
|
||||
garage.updateRobot({ nickname: undefined });
|
||||
}}
|
||||
variant='contained'
|
||||
size='large'
|
||||
@ -149,7 +151,7 @@ const Onboarding = ({
|
||||
<Grid container direction='column' alignItems='center' spacing={1}>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
{garage.getSlot().robot.avatarLoaded && Boolean(garage.getSlot().robot.nickname) ? (
|
||||
{slot?.avatarLoaded && Boolean(robot?.nickname) ? (
|
||||
t('This is your trading avatar')
|
||||
) : (
|
||||
<>
|
||||
@ -162,7 +164,7 @@ const Onboarding = ({
|
||||
|
||||
<Grid item sx={{ width: '13.5em' }}>
|
||||
<RobotAvatar
|
||||
nickname={garage.getSlot().robot.nickname}
|
||||
nickname={robot?.nickname}
|
||||
smooth={true}
|
||||
style={{ maxWidth: '12.5em', maxHeight: '12.5em' }}
|
||||
placeholderType='generating'
|
||||
@ -178,7 +180,7 @@ const Onboarding = ({
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{garage.getSlot().robot.avatarLoaded && Boolean(garage.getSlot().robot.nickname) ? (
|
||||
{slot?.avatarLoaded && Boolean(robot?.nickname) ? (
|
||||
<Grid item>
|
||||
<Typography align='center'>{t('Hi! My name is')}</Typography>
|
||||
<Typography component='h5' variant='h5'>
|
||||
@ -197,7 +199,7 @@ const Onboarding = ({
|
||||
width: '1.5em',
|
||||
}}
|
||||
/>
|
||||
<b>{garage.getSlot().robot.nickname}</b>
|
||||
<b>{robot.nickname}</b>
|
||||
<Bolt
|
||||
sx={{
|
||||
color: '#fcba03',
|
||||
@ -210,13 +212,7 @@ const Onboarding = ({
|
||||
</Grid>
|
||||
) : null}
|
||||
<Grid item>
|
||||
<Collapse
|
||||
in={
|
||||
!!(
|
||||
garage.getSlot().robot.avatarLoaded && Boolean(garage.getSlot().robot.nickname)
|
||||
)
|
||||
}
|
||||
>
|
||||
<Collapse in={!!(slot?.avatarLoaded && Boolean(robot?.nickname))}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setStep('3');
|
||||
|
@ -22,6 +22,7 @@ import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||
import { genBase62Token } from '../../utils';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
import { UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||
|
||||
interface RobotProfileProps {
|
||||
robot: Robot;
|
||||
@ -45,6 +46,7 @@ const RobotProfile = ({
|
||||
}: RobotProfileProps): JSX.Element => {
|
||||
const { windowSize, hostUrl } = useContext<UseAppStoreType>(AppContext);
|
||||
const { garage, robotUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
@ -53,7 +55,9 @@ const RobotProfile = ({
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (garage.getSlot().robot.nickname != null && garage.getSlot().robot.avatarLoaded) {
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot(sortedCoordinators[0]);
|
||||
if (robot?.nickname != null && slot?.avatarLoaded) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [robotUpdatedAt, loading]);
|
||||
@ -64,10 +68,13 @@ const RobotProfile = ({
|
||||
};
|
||||
|
||||
const handleChangeSlot = (e: SelectChangeEvent<number | 'loading'>): void => {
|
||||
garage.currentSlot = Number(e.target.value);
|
||||
garage.currentSlot = e.target.value;
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot();
|
||||
|
||||
return (
|
||||
<Grid container direction='column' alignItems='center' spacing={1} padding={1} paddingTop={2}>
|
||||
<Grid
|
||||
@ -79,7 +86,7 @@ const RobotProfile = ({
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
<Grid item sx={{ height: '2.3em', position: 'relative' }}>
|
||||
{garage.getSlot().robot.avatarLoaded && garage.getSlot().robot.nickname != null ? (
|
||||
{slot?.avatarLoaded && robot?.nickname != null ? (
|
||||
<Typography align='center' component='h5' variant='h5'>
|
||||
<div
|
||||
style={{
|
||||
@ -98,7 +105,7 @@ const RobotProfile = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<b>{garage.getSlot().robot.nickname}</b>
|
||||
<b>{robot?.nickname}</b>
|
||||
{width < 19 ? null : (
|
||||
<Bolt
|
||||
sx={{
|
||||
@ -120,7 +127,7 @@ const RobotProfile = ({
|
||||
|
||||
<Grid item sx={{ width: `13.5em` }}>
|
||||
<RobotAvatar
|
||||
nickname={garage.getSlot().robot.nickname}
|
||||
nickname={robot?.nickname}
|
||||
smooth={true}
|
||||
style={{ maxWidth: '12.5em', maxHeight: '12.5em' }}
|
||||
placeholderType='generating'
|
||||
@ -135,7 +142,7 @@ const RobotProfile = ({
|
||||
tooltipPosition='top'
|
||||
baseUrl={hostUrl}
|
||||
/>
|
||||
{garage.getSlot().robot.found && Number(garage.getSlot().lastOrderId) > 0 ? (
|
||||
{robot?.found && slot?.lastShortAlias ? (
|
||||
<Typography align='center' variant='h6'>
|
||||
{t('Welcome back!')}
|
||||
</Typography>
|
||||
@ -144,39 +151,31 @@ const RobotProfile = ({
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{Boolean(garage.getSlot().activeOrderId) &&
|
||||
garage.getSlot().robot.avatarLoaded &&
|
||||
Boolean(garage.getSlot().robot.nickname) ? (
|
||||
{Boolean(robot?.activeOrderId) &&
|
||||
Boolean(slot?.avatarLoaded) &&
|
||||
Boolean(robot?.nickname) ? (
|
||||
<Grid item>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/order/${String(garage.getSlot().activeOrderShortAlias)}/${String(
|
||||
garage.getSlot().activeOrderId,
|
||||
)}`,
|
||||
`/order/${String(slot?.activeShortAlias)}/${String(robot?.activeOrderId)}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t('Active order #{{orderID}}', { orderID: garage.getSlot().activeOrderId })}
|
||||
{t('Active order #{{orderID}}', { orderID: robot?.activeOrderId })}
|
||||
</Button>
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
{Boolean(garage.getSlot().lastOrderId) &&
|
||||
garage.getSlot().robot.avatarLoaded &&
|
||||
Boolean(garage.getSlot().robot.nickname) ? (
|
||||
{Boolean(robot?.lastOrderId) && Boolean(slot?.avatarLoaded) && Boolean(robot?.nickname) ? (
|
||||
<Grid item container direction='column' alignItems='center'>
|
||||
<Grid item>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/order/${String(garage.getSlot().lastOrderShortAlias)}/${String(
|
||||
garage.getSlot().lastOrderId,
|
||||
)}`,
|
||||
);
|
||||
navigate(`/order/${String(slot?.lastShortAlias)}/${String(robot?.lastOrderId)}`);
|
||||
}}
|
||||
>
|
||||
{t('Last order #{{orderID}}', { orderID: garage.getSlot().lastOrderId })}
|
||||
{t('Last order #{{orderID}}', { orderID: robot?.lastOrderId })}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
@ -263,9 +262,9 @@ const RobotProfile = ({
|
||||
<Typography>{t('Building...')}</Typography>
|
||||
</MenuItem>
|
||||
) : (
|
||||
garage.slots.map((slot: Slot, index: number) => {
|
||||
Object.values(garage.slots).map((slot: Slot, index: number) => {
|
||||
return (
|
||||
<MenuItem key={index} value={index}>
|
||||
<MenuItem key={index} value={slot.token}>
|
||||
<Grid
|
||||
container
|
||||
direction='row'
|
||||
@ -276,7 +275,7 @@ const RobotProfile = ({
|
||||
>
|
||||
<Grid item>
|
||||
<RobotAvatar
|
||||
nickname={slot.robot.nickname}
|
||||
nickname={slot?.getRobot()?.nickname}
|
||||
smooth={true}
|
||||
style={{ width: '2.6em', height: '2.6em' }}
|
||||
placeholderType='loading'
|
||||
@ -286,7 +285,7 @@ const RobotProfile = ({
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant={windowSize.width < 26 ? 'caption' : undefined}>
|
||||
{slot.robot.nickname}
|
||||
{slot?.getRobot()?.nickname}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -323,7 +322,6 @@ const RobotProfile = ({
|
||||
color='primary'
|
||||
onClick={() => {
|
||||
garage.delete();
|
||||
garage.currentSlot = 0;
|
||||
logoutRobot();
|
||||
setView('welcome');
|
||||
}}
|
||||
|
@ -38,6 +38,10 @@ const TokenInput = ({
|
||||
setShowCopied(false);
|
||||
}, [inputToken]);
|
||||
|
||||
useEffect(() => {
|
||||
setShowCopied(false);
|
||||
}, [showCopied]);
|
||||
|
||||
if (loading) {
|
||||
return <LinearProgress sx={{ height: '0.8em' }} />;
|
||||
} else {
|
||||
@ -67,14 +71,14 @@ const TokenInput = ({
|
||||
<Tooltip open={showCopied} title={t('Copied!')}>
|
||||
<IconButton
|
||||
autoFocus={autoFocusTarget === 'copyButton'}
|
||||
color={garage.getSlot().robot.copiedToken ? 'inherit' : 'primary'}
|
||||
color={garage.getSlot()?.copiedToken ? 'inherit' : 'primary'}
|
||||
onClick={() => {
|
||||
systemClient.copyToClipboard(inputToken);
|
||||
setShowCopied(true);
|
||||
setTimeout(() => {
|
||||
setShowCopied(false);
|
||||
}, 1000);
|
||||
garage.updateRobot({ copiedToken: true });
|
||||
garage.updateSlot({ copiedToken: true }, inputToken);
|
||||
}}
|
||||
>
|
||||
<ContentCopy sx={{ width: '1em', height: '1em' }} />
|
||||
|
@ -30,7 +30,7 @@ interface RobotPageProps {
|
||||
const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => {
|
||||
const { torStatus, windowSize, settings, page } = useContext<UseAppStoreType>(AppContext);
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const urlToken = settings.selfhostedClient ? params.token : null;
|
||||
@ -41,15 +41,15 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => {
|
||||
const [badToken, setBadToken] = useState<string>('');
|
||||
const [inputToken, setInputToken] = useState<string>('');
|
||||
const [view, setView] = useState<'welcome' | 'onboarding' | 'recovery' | 'profile'>(
|
||||
garage.getSlot().robot.token !== undefined ? 'profile' : 'welcome',
|
||||
garage.currentSlot !== null ? 'profile' : 'welcome',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const token = urlToken ?? garage.getSlot().robot.token;
|
||||
if (token !== undefined) {
|
||||
const token = urlToken ?? garage.currentSlot;
|
||||
if (token !== undefined && token !== null) {
|
||||
setInputToken(token);
|
||||
if (window.NativeRobosats === undefined || torStatus === '"Done"') {
|
||||
getRecoverRobot(token);
|
||||
getGenerateRobot(token);
|
||||
setView('profile');
|
||||
}
|
||||
}
|
||||
@ -65,32 +65,18 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => {
|
||||
}
|
||||
}, [inputToken]);
|
||||
|
||||
const getRecoverRobot = (token: string): void => {
|
||||
setInputToken(token);
|
||||
genKey(token)
|
||||
.then((key) => {
|
||||
garage.updateRobot({
|
||||
token,
|
||||
pubKey: key.publicKeyArmored,
|
||||
encPrivKey: key.encryptedPrivateKeyArmored,
|
||||
});
|
||||
void federation.fetchRobot(garage, garage.currentSlot);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
};
|
||||
|
||||
const getGenerateRobot = (token: string): void => {
|
||||
setInputToken(token);
|
||||
genKey(token)
|
||||
.then((key) => {
|
||||
garage.createRobot({
|
||||
garage.upsertRobot(token, sortedCoordinators[0], {
|
||||
token,
|
||||
pubKey: key.publicKeyArmored,
|
||||
encPrivKey: key.encryptedPrivateKeyArmored,
|
||||
shortAlias: sortedCoordinators[0],
|
||||
});
|
||||
void federation.fetchRobot(garage, garage.currentSlot);
|
||||
void federation.fetchRobot(garage, token);
|
||||
garage.currentSlot = token;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
@ -99,7 +85,7 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => {
|
||||
|
||||
const logoutRobot = (): void => {
|
||||
setInputToken('');
|
||||
garage.deleteSlot(garage.currentSlot);
|
||||
garage.deleteSlot();
|
||||
};
|
||||
|
||||
if (!(window.NativeRobosats === undefined) && !(torStatus === 'DONE' || torStatus === '"Done"')) {
|
||||
@ -194,7 +180,7 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => {
|
||||
badToken={badToken}
|
||||
inputToken={inputToken}
|
||||
setInputToken={setInputToken}
|
||||
getRecoverRobot={getRecoverRobot}
|
||||
getRecoverRobot={getGenerateRobot}
|
||||
/>
|
||||
) : null}
|
||||
</Paper>
|
||||
|
@ -33,7 +33,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element =
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(garage.getSlot().robot.loading);
|
||||
setLoading(!garage.getSlot()?.avatarLoaded);
|
||||
}, [robotUpdatedAt]);
|
||||
|
||||
return (
|
||||
@ -57,7 +57,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element =
|
||||
<ListItem className='profileNickname'>
|
||||
<ListItemText secondary={t('Your robot')}>
|
||||
<Typography component='h6' variant='h6'>
|
||||
{garage.getSlot().robot.nickname !== undefined && (
|
||||
{garage.getSlot()?.getRobot()?.nickname !== undefined && (
|
||||
<div style={{ position: 'relative', left: '-7px' }}>
|
||||
<div
|
||||
style={{
|
||||
@ -70,7 +70,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element =
|
||||
>
|
||||
<BoltIcon sx={{ color: '#fcba03', height: '28px', width: '24px' }} />
|
||||
|
||||
<a>{garage.getSlot().robot.nickname}</a>
|
||||
<a>{garage.getSlot()?.getRobot()?.nickname}</a>
|
||||
|
||||
<BoltIcon sx={{ color: '#fcba03', height: '28px', width: '24px' }} />
|
||||
</div>
|
||||
@ -83,7 +83,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element =
|
||||
<RobotAvatar
|
||||
avatarClass='profileAvatar'
|
||||
style={{ width: 65, height: 65 }}
|
||||
nickname={garage.getSlot().robot.nickname}
|
||||
nickname={garage.getSlot()?.getRobot()?.nickname}
|
||||
baseUrl={baseUrl}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
@ -97,15 +97,10 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element =
|
||||
</Typography>
|
||||
|
||||
{Object.values(federation.coordinators).map((coordinator: Coordinator): JSX.Element => {
|
||||
if (!garage.getSlot().robot?.loading) {
|
||||
if (garage.getSlot()?.avatarLoaded) {
|
||||
return (
|
||||
<div key={coordinator.shortAlias}>
|
||||
<RobotInfo
|
||||
coordinator={coordinator}
|
||||
robot={garage.getSlot().robot}
|
||||
slotIndex={garage.currentSlot}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<RobotInfo coordinator={coordinator} onClose={onClose} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
|
@ -43,7 +43,7 @@ const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): J
|
||||
sx={{ width: '100%', maxWidth: '550px' }}
|
||||
disabled
|
||||
label={t('Back it up!')}
|
||||
value={garage.getSlot().robot.token}
|
||||
value={garage.getSlot()?.token}
|
||||
variant='filled'
|
||||
size='small'
|
||||
InputProps={{
|
||||
@ -51,7 +51,7 @@ const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): J
|
||||
<Tooltip disableHoverListener enterTouchDelay={0} title={t('Copied!')}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
systemClient.copyToClipboard(garage.getSlot().robot.token);
|
||||
systemClient.copyToClipboard(garage.getSlot()?.token ?? '');
|
||||
}}
|
||||
>
|
||||
<ContentCopy color='primary' />
|
||||
|
@ -91,6 +91,11 @@ const MakerForm = ({
|
||||
const minRangeAmountMultiple = 1.6;
|
||||
const amountSafeThresholds = [1.03, 0.98];
|
||||
|
||||
useEffect(() => {
|
||||
const slot = garage.getSlot();
|
||||
if (slot?.token) federation.fetchRobot(garage, slot?.token);
|
||||
}, [garage.currentSlot]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrencyCode(currencyDict[fav.currency === 0 ? 1 : fav.currency]);
|
||||
if (maker.coordinator != null) {
|
||||
@ -280,12 +285,19 @@ const MakerForm = ({
|
||||
};
|
||||
|
||||
const handleCreateOrder = function (): void {
|
||||
const slot = garage.getSlot();
|
||||
|
||||
if (slot?.activeShortAlias) {
|
||||
setBadRequest(t('You are already maker of an active order'));
|
||||
return;
|
||||
}
|
||||
|
||||
const { url, basePath } =
|
||||
federation
|
||||
.getCoordinator(maker.coordinator)
|
||||
?.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl) ?? {};
|
||||
|
||||
const auth = garage.getSlot().robot.getAuthHeaders();
|
||||
const auth = slot?.getRobot()?.getAuthHeaders();
|
||||
|
||||
if (!disableRequest && maker.coordinator != null && auth !== null) {
|
||||
setSubmittingRequest(true);
|
||||
@ -565,7 +577,7 @@ const MakerForm = ({
|
||||
setOpenDialogs(false);
|
||||
}}
|
||||
onClickDone={handleCreateOrder}
|
||||
hasRobot={garage.getSlot().robot.avatarLoaded}
|
||||
hasRobot={garage.getSlot()?.avatarLoaded ?? false}
|
||||
onClickGenerateRobot={onClickGenerateRobot}
|
||||
/>
|
||||
<F2fMapDialog
|
||||
|
@ -85,7 +85,7 @@ const Notifications = ({
|
||||
const basePageTitle = t('RoboSats - Simple and Private Bitcoin Exchange');
|
||||
|
||||
const moveToOrderPage = function (): void {
|
||||
navigate(`/order/${String(garage.getOrder()?.id)}`);
|
||||
navigate(`/order/${String(garage.getSlot()?.order?.id)}`);
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
@ -106,7 +106,7 @@ const Notifications = ({
|
||||
|
||||
const Messages: MessagesProps = {
|
||||
bondLocked: {
|
||||
title: t(`${garage.getOrder()?.is_maker === true ? 'Maker' : 'Taker'} bond locked`),
|
||||
title: t(`${garage.getSlot()?.order?.is_maker === true ? 'Maker' : 'Taker'} bond locked`),
|
||||
severity: 'info',
|
||||
onClick: moveToOrderPage,
|
||||
sound: audio.ding,
|
||||
@ -228,9 +228,9 @@ const Notifications = ({
|
||||
};
|
||||
|
||||
const handleStatusChange = function (oldStatus: number | undefined, status: number): void {
|
||||
const order = garage.getOrder();
|
||||
const order = garage.getSlot()?.order;
|
||||
|
||||
if (order === null) return;
|
||||
if (order === undefined || order === null) return;
|
||||
|
||||
let message = emptyNotificationMessage;
|
||||
|
||||
@ -293,8 +293,8 @@ const Notifications = ({
|
||||
|
||||
// Notify on order status change
|
||||
useEffect(() => {
|
||||
const order = garage.getOrder();
|
||||
if (order !== null) {
|
||||
const order = garage.getSlot()?.order;
|
||||
if (order !== undefined && order !== null) {
|
||||
if (order.status !== oldOrderStatus) {
|
||||
handleStatusChange(oldOrderStatus, order.status);
|
||||
setOldOrderStatus(order.status);
|
||||
|
@ -314,7 +314,9 @@ const TakeButton = ({
|
||||
};
|
||||
|
||||
const takeOrder = function (): void {
|
||||
if (currentOrder === null) return;
|
||||
const robot = garage.getSlot()?.getRobot() ?? null;
|
||||
|
||||
if (currentOrder === null || robot === null) return;
|
||||
|
||||
setLoadingTake(true);
|
||||
const { url, basePath } = federation
|
||||
@ -328,7 +330,7 @@ const TakeButton = ({
|
||||
action: 'take',
|
||||
amount: currentOrder?.currency === 1000 ? takeAmount / 100000000 : takeAmount,
|
||||
},
|
||||
{ tokenSHA256: garage.getSlot().robot.tokenSHA256 },
|
||||
{ tokenSHA256: robot?.tokenSHA256 },
|
||||
)
|
||||
.then((data) => {
|
||||
setLoadingTake(false);
|
||||
@ -370,7 +372,7 @@ const TakeButton = ({
|
||||
setLoadingTake(true);
|
||||
setOpen(closeAll);
|
||||
}}
|
||||
hasRobot={garage.getSlot().robot.avatarLoaded}
|
||||
hasRobot={garage.getSlot()?.avatarLoaded ?? false}
|
||||
onClickGenerateRobot={onClickGenerateRobot}
|
||||
/>
|
||||
<InactiveMakerDialog />
|
||||
|
@ -23,7 +23,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import { Numbers, Send, EmojiEvents, ExpandMore } from '@mui/icons-material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { type Coordinator, type Robot } from '../../models';
|
||||
import { type Coordinator } from '../../models';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { EnableTelegramDialog } from '../Dialogs';
|
||||
import { UserNinjaIcon } from '../Icons';
|
||||
@ -33,13 +33,11 @@ import { signCleartextMessage } from '../../pgp';
|
||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
|
||||
interface Props {
|
||||
robot: Robot;
|
||||
slotIndex: number;
|
||||
coordinator: Coordinator;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }: Props) => {
|
||||
const RobotInfo: React.FC<Props> = ({ coordinator, onClose }: Props) => {
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
@ -71,7 +69,8 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
|
||||
const handleWeblnInvoiceClicked = async (e: MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||
e.preventDefault();
|
||||
if (robot.earnedRewards > 0) {
|
||||
const robot = garage.getSlot()?.getRobot(coordinator.shortAlias);
|
||||
if (robot && robot.earnedRewards > 0) {
|
||||
const webln = await getWebln();
|
||||
const invoice = webln.makeInvoice(robot.earnedRewards).then(() => {
|
||||
if (invoice != null) {
|
||||
@ -85,12 +84,13 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
setBadInvoice('');
|
||||
setShowRewardsSpinner(true);
|
||||
|
||||
const robot = garage.getSlot(slotIndex).robot;
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot(coordinator.shortAlias);
|
||||
|
||||
if (robot.encPrivKey != null && robot.token != null) {
|
||||
if (robot && slot?.token && robot.encPrivKey != null && robot.token != null) {
|
||||
void signCleartextMessage(rewardInvoice, robot.encPrivKey, robot.token).then(
|
||||
(signedInvoice) => {
|
||||
void coordinator.fetchReward(signedInvoice, garage, slotIndex).then((data) => {
|
||||
void coordinator.fetchReward(signedInvoice, garage, slot?.token).then((data) => {
|
||||
setBadInvoice(data.bad_invoice ?? '');
|
||||
setShowRewardsSpinner(false);
|
||||
setWithdrawn(data.successful_withdrawal);
|
||||
@ -103,32 +103,42 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
};
|
||||
|
||||
const setStealthInvoice = (wantsStealth: boolean): void => {
|
||||
void coordinator.fetchStealth(wantsStealth, garage, slotIndex);
|
||||
const slot = garage.getSlot();
|
||||
if (slot?.token) {
|
||||
void coordinator.fetchStealth(wantsStealth, garage, slot?.token);
|
||||
}
|
||||
};
|
||||
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot(coordinator.shortAlias);
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMore />}>
|
||||
{`${coordinator.longAlias}:`}
|
||||
{garage.getSlot(slotIndex).robot.earnedRewards > 0 && (
|
||||
{(robot?.earnedRewards ?? 0) > 0 && (
|
||||
<Typography color='success'> {t('Claim Sats!')} </Typography>
|
||||
)}
|
||||
{(garage.getSlot(slotIndex).robot.activeOrderId ?? 0) > 0 && (
|
||||
{slot?.activeShortAlias === coordinator.shortAlias && (
|
||||
<Typography color='success'>
|
||||
<b>{t('Active order!')}</b>
|
||||
</Typography>
|
||||
)}
|
||||
{(garage.getSlot(slotIndex).robot.lastOrderId ?? 0) > 0 &&
|
||||
robot.activeOrderId === undefined && (
|
||||
<Typography color='warning'> {t('finished order')}</Typography>
|
||||
)}
|
||||
{slot?.lastShortAlias === coordinator.shortAlias && (
|
||||
<Typography color='warning'> {t('finished order')}</Typography>
|
||||
)}
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<List dense disablePadding={true}>
|
||||
{(garage.getSlot(slotIndex).robot.activeOrderId ?? 0) > 0 ? (
|
||||
{}
|
||||
{slot?.activeShortAlias && slot?.activeShortAlias === coordinator.shortAlias ? (
|
||||
<ListItemButton
|
||||
onClick={() => {
|
||||
navigate(`/order/${coordinator.shortAlias}/${String(robot.activeOrderId)}`);
|
||||
navigate(
|
||||
`/order/${slot?.activeShortAlias}/${String(
|
||||
slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId,
|
||||
)}`,
|
||||
);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
@ -138,14 +148,20 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
</Badge>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t('One active order #{{orderID}}', { orderID: robot.activeOrderId })}
|
||||
primary={t('One active order #{{orderID}}', {
|
||||
orderID: slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId,
|
||||
})}
|
||||
secondary={t('Your current order')}
|
||||
/>
|
||||
</ListItemButton>
|
||||
) : (garage.getSlot(slotIndex).robot.lastOrderId ?? 0) > 0 ? (
|
||||
) : (robot?.lastOrderId ?? 0) > 0 && slot?.lastShortAlias === coordinator.shortAlias ? (
|
||||
<ListItemButton
|
||||
onClick={() => {
|
||||
navigate(`/order/${coordinator.shortAlias}/${String(robot.lastOrderId)}`);
|
||||
navigate(
|
||||
`/order/${slot?.lastShortAlias}/${String(
|
||||
slot?.getRobot(slot?.lastShortAlias ?? '')?.lastOrderId,
|
||||
)}`,
|
||||
);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
@ -153,7 +169,9 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
<Numbers color='primary' />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t('Your last order #{{orderID}}', { orderID: robot.lastOrderId })}
|
||||
primary={t('Your last order #{{orderID}}', {
|
||||
orderID: slot?.getRobot(slot?.lastShortAlias ?? '')?.lastOrderId,
|
||||
})}
|
||||
secondary={t('Inactive order')}
|
||||
/>
|
||||
</ListItemButton>
|
||||
@ -176,8 +194,8 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
onClose={() => {
|
||||
setOpenEnableTelegram(false);
|
||||
}}
|
||||
tgBotName={robot.tgBotName}
|
||||
tgToken={robot.tgToken}
|
||||
tgBotName={robot?.tgBotName ?? ''}
|
||||
tgToken={robot?.tgToken ?? ''}
|
||||
/>
|
||||
|
||||
<ListItem>
|
||||
@ -186,7 +204,7 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
</ListItemIcon>
|
||||
|
||||
<ListItemText>
|
||||
{robot.tgEnabled ? (
|
||||
{robot?.tgEnabled ? (
|
||||
<Typography color={theme.palette.success.main}>
|
||||
<b>{t('Telegram enabled')}</b>
|
||||
</Typography>
|
||||
@ -222,9 +240,9 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
label={t('Use stealth invoices')}
|
||||
control={
|
||||
<Switch
|
||||
checked={robot.stealthInvoices}
|
||||
checked={robot?.stealthInvoices}
|
||||
onChange={() => {
|
||||
setStealthInvoice(!robot.stealthInvoices);
|
||||
setStealthInvoice(!robot?.stealthInvoices);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
@ -243,12 +261,12 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
<ListItemText secondary={t('Your compensations')}>
|
||||
<Grid container>
|
||||
<Grid item xs={9}>
|
||||
<Typography>{`${robot.earnedRewards} Sats`}</Typography>
|
||||
<Typography>{`${robot?.earnedRewards} Sats`}</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3}>
|
||||
<Button
|
||||
disabled={robot.earnedRewards === 0}
|
||||
disabled={robot?.earnedRewards === 0}
|
||||
onClick={() => {
|
||||
setOpenClaimRewards(true);
|
||||
}}
|
||||
@ -268,7 +286,7 @@ const RobotInfo: React.FC<Props> = ({ robot, slotIndex, coordinator, onClose }:
|
||||
error={Boolean(badInvoice)}
|
||||
helperText={badInvoice ?? ''}
|
||||
label={t('Invoice for {{amountSats}} Sats', {
|
||||
amountSats: robot.earnedRewards,
|
||||
amountSats: robot?.earnedRewards,
|
||||
})}
|
||||
size='small'
|
||||
value={rewardInvoice}
|
||||
|
@ -47,7 +47,7 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
}: Props): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { garage, robotUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
||||
|
||||
const [audio] = useState(() => new Audio(`${audioPath}/chat-open.mp3`));
|
||||
const [connected, setConnected] = useState<boolean>(false);
|
||||
@ -64,10 +64,10 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!connected && garage.getSlot().robot.avatarLoaded) {
|
||||
if (!connected && garage.getSlot()?.avatarLoaded) {
|
||||
connectWebsocket();
|
||||
}
|
||||
}, [connected, robot]);
|
||||
}, [connected, robotUpdatedAt]);
|
||||
|
||||
// Make sure to not keep reconnecting once status is not Chat
|
||||
useEffect(() => {
|
||||
@ -108,7 +108,7 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
setConnected(true);
|
||||
|
||||
connection.send({
|
||||
message: garage.getSlot().robot.pubKey,
|
||||
message: garage.getSlot()?.getRobot()?.pubKey,
|
||||
nick: userNick,
|
||||
});
|
||||
|
||||
@ -130,10 +130,10 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
const createJsonFile: () => object = () => {
|
||||
return {
|
||||
credentials: {
|
||||
own_public_key: garage.getSlot().robot.pubKey,
|
||||
own_public_key: garage.getSlot()?.getRobot()?.pubKey,
|
||||
peer_public_key: peerPubKey,
|
||||
encrypted_private_key: garage.getSlot().robot.encPrivKey,
|
||||
passphrase: garage.getSlot().robot.token,
|
||||
encrypted_private_key: garage.getSlot()?.getRobot()?.encPrivKey,
|
||||
passphrase: garage.getSlot()?.token,
|
||||
},
|
||||
messages,
|
||||
};
|
||||
@ -141,7 +141,7 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
|
||||
const onMessage: (message: any) => void = (message) => {
|
||||
const dataFromServer = JSON.parse(message.data);
|
||||
const robot = garage.getSlot().robot;
|
||||
const robot = garage.getSlot()?.getRobot();
|
||||
if (dataFromServer != null && !receivedIndexes.includes(dataFromServer.index)) {
|
||||
setReceivedIndexes((prev) => [...prev, dataFromServer.index]);
|
||||
setPeerConnected(dataFromServer.peer_connected);
|
||||
@ -211,8 +211,8 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const onButtonClicked = (e: React.FormEvent<HTMLFormElement>): void => {
|
||||
const robot = garage.getSlot().robot;
|
||||
if (robot.token !== undefined && value.includes(robot.token)) {
|
||||
const robot = garage.getSlot()?.getRobot();
|
||||
if (robot?.token !== undefined && value.includes(robot.token)) {
|
||||
alert(
|
||||
`Aye! You just sent your own robot robot.token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`,
|
||||
);
|
||||
@ -263,10 +263,10 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
}}
|
||||
orderId={Number(orderId)}
|
||||
messages={messages}
|
||||
ownPubKey={garage.getSlot().robot.pubKey ?? ''}
|
||||
ownEncPrivKey={garage.getSlot().robot.encPrivKey ?? ''}
|
||||
ownPubKey={garage.getSlot()?.getRobot()?.pubKey ?? ''}
|
||||
ownEncPrivKey={garage.getSlot()?.getRobot()?.encPrivKey ?? ''}
|
||||
peerPubKey={peerPubKey ?? 'Not received yet'}
|
||||
passphrase={garage.getSlot().robot.token ?? ''}
|
||||
passphrase={garage.getSlot()?.getRobot()?.token ?? ''}
|
||||
onClickBack={() => {
|
||||
setAudit(false);
|
||||
}}
|
||||
|
@ -84,12 +84,16 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
}, [chatOffset]);
|
||||
|
||||
const loadMessages: () => void = () => {
|
||||
const shortAlias = garage.getSlot()?.activeShortAlias;
|
||||
|
||||
if (!shortAlias) return;
|
||||
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(garage.getSlot().activeOrderShortAlias)
|
||||
.getCoordinator(shortAlias)
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.get(url + basePath, `/api/chat/?order_id=${orderId}&offset=${lastIndex}`, {
|
||||
tokenSHA256: garage.getSlot().robot.tokenSHA256,
|
||||
tokenSHA256: garage.getSlot()?.getRobot()?.tokenSHA256 ?? '',
|
||||
})
|
||||
.then((results: any) => {
|
||||
if (results != null) {
|
||||
@ -106,18 +110,18 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
const createJsonFile = (): object => {
|
||||
return {
|
||||
credentials: {
|
||||
own_public_key: garage.getSlot().robot.pubKey,
|
||||
own_public_key: garage.getSlot()?.getRobot()?.pubKey,
|
||||
peer_public_key: peerPubKey,
|
||||
encrypted_private_key: garage.getSlot().robot.encPrivKey,
|
||||
passphrase: garage.getSlot().robot.token,
|
||||
encrypted_private_key: garage.getSlot()?.getRobot()?.encPrivKey,
|
||||
passphrase: garage.getSlot()?.token,
|
||||
},
|
||||
messages,
|
||||
};
|
||||
};
|
||||
|
||||
const onMessage = (dataFromServer: ServerMessage): void => {
|
||||
const robot = garage.getSlot().robot;
|
||||
if (dataFromServer != null) {
|
||||
const robot = garage.getSlot();
|
||||
if (robot && dataFromServer != null) {
|
||||
// If we receive an encrypted message
|
||||
if (dataFromServer.message.substring(0, 27) === `-----BEGIN PGP MESSAGE-----`) {
|
||||
void decryptMessage(
|
||||
@ -172,8 +176,8 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const onButtonClicked = (e: React.FormEvent<HTMLFormElement>): void => {
|
||||
const robot = garage.getSlot().robot;
|
||||
if (robot.token !== undefined && value.includes(robot.token)) {
|
||||
const robot = garage.getSlot();
|
||||
if (robot?.token !== undefined && value.includes(robot.token)) {
|
||||
alert(
|
||||
`Aye! You just sent your own robot robot.token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`,
|
||||
);
|
||||
@ -182,7 +186,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
// If input string contains '#' send unencrypted and unlogged message
|
||||
else if (value.substring(0, 1) === '#') {
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(garage.getSlot().activeOrderShortAlias ?? '')
|
||||
.getCoordinator(garage.getSlot()?.activeShortAlias ?? '')
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(
|
||||
@ -193,7 +197,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
order_id: orderId,
|
||||
offset: lastIndex,
|
||||
},
|
||||
{ tokenSHA256: robot.tokenSHA256 },
|
||||
{ tokenSHA256: robot?.tokenSHA256 },
|
||||
)
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
@ -209,13 +213,13 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
});
|
||||
}
|
||||
// Else if message is not empty send message
|
||||
else if (value !== '') {
|
||||
else if (value !== '' && robot?.pubKey) {
|
||||
setWaitingEcho(true);
|
||||
setLastSent(value);
|
||||
encryptMessage(value, robot.pubKey, peerPubKey, robot.encPrivKey, robot.token)
|
||||
encryptMessage(value, robot?.pubKey, peerPubKey ?? '', robot?.encPrivKey, robot?.token)
|
||||
.then((encryptedMessage) => {
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(garage.getSlot().activeOrderShortAlias ?? '')
|
||||
.getCoordinator(garage.getSlot()?.activeShortAlias ?? '')
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(
|
||||
@ -226,7 +230,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
order_id: orderId,
|
||||
offset: lastIndex,
|
||||
},
|
||||
{ tokenSHA256: robot.tokenSHA256 },
|
||||
{ tokenSHA256: robot?.tokenSHA256 },
|
||||
)
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
@ -263,10 +267,10 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
}}
|
||||
orderId={Number(orderId)}
|
||||
messages={messages}
|
||||
ownPubKey={garage.getSlot().robot.pubKey ?? ''}
|
||||
ownEncPrivKey={garage.getSlot().robot.encPrivKey ?? ''}
|
||||
ownPubKey={garage.getSlot()?.getRobot()?.pubKey ?? ''}
|
||||
ownEncPrivKey={garage.getSlot()?.getRobot()?.encPrivKey ?? ''}
|
||||
peerPubKey={peerPubKey ?? 'Not received yet'}
|
||||
passphrase={garage.getSlot().robot.token ?? ''}
|
||||
passphrase={garage.getSlot()?.token ?? ''}
|
||||
onClickBack={() => {
|
||||
setAudit(false);
|
||||
}}
|
||||
|
@ -40,7 +40,7 @@ export const ChatPrompt = ({
|
||||
setMessages,
|
||||
}: ChatPromptProps): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { garage, orderUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
||||
|
||||
const [sentButton, setSentButton] = useState<boolean>(false);
|
||||
const [receivedButton, setReceivedButton] = useState<boolean>(false);
|
||||
@ -49,9 +49,9 @@ export const ChatPrompt = ({
|
||||
const [enableDisputeTime, setEnableDisputeTime] = useState<Date>(new Date(order.expires_at));
|
||||
const [text, setText] = useState<string>('');
|
||||
|
||||
const currencyCode: string = currencies[`${garage.getSlot().order.currency}`];
|
||||
const currencyCode: string = currencies[`${garage.getSlot()?.order?.currency}`];
|
||||
const amount: string = pn(
|
||||
parseFloat(parseFloat(garage.getSlot().order.amount).toFixed(order.currency === 1000 ? 8 : 4)),
|
||||
parseFloat(garage.getSlot()?.order?.amount ?? 0).toFixed(order.currency === 1000 ? 8 : 4),
|
||||
);
|
||||
|
||||
const disputeCountdownRenderer = function ({
|
||||
@ -113,7 +113,7 @@ export const ChatPrompt = ({
|
||||
setText(t("The buyer has sent the fiat. Click 'Confirm Received' once you receive it."));
|
||||
}
|
||||
}
|
||||
}, [order]);
|
||||
}, [orderUpdatedAt]);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
|
@ -155,8 +155,8 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
}
|
||||
|
||||
const renewOrder = function (): void {
|
||||
const currentOrder = garage.getSlot().order;
|
||||
if (currentOrder !== null) {
|
||||
const currentOrder = garage.getSlot()?.order;
|
||||
if (currentOrder) {
|
||||
const body = {
|
||||
type: currentOrder.type,
|
||||
currency: currentOrder.currency,
|
||||
@ -179,7 +179,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(url + basePath, '/api/make/', body, {
|
||||
tokenSHA256: garage.getSlot().robot.tokenSHA256,
|
||||
tokenSHA256: garage.getSlot()?.getRobot()?.tokenSHA256,
|
||||
})
|
||||
.then((data: any) => {
|
||||
if (data.bad_request !== undefined) {
|
||||
@ -203,8 +203,8 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
statement,
|
||||
rating,
|
||||
}: SubmitActionProps): void {
|
||||
const robot = garage.getSlot().robot;
|
||||
const currentOrder = garage.getSlot().order;
|
||||
const robot = garage.getSlot()?.getRobot();
|
||||
const currentOrder = garage.getSlot()?.order;
|
||||
|
||||
void apiClient
|
||||
.post(
|
||||
@ -219,7 +219,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
statement,
|
||||
rating,
|
||||
},
|
||||
{ tokenSHA256: robot.tokenSHA256 },
|
||||
{ tokenSHA256: robot?.tokenSHA256 },
|
||||
)
|
||||
.then((data: Order) => {
|
||||
setOpen(closeAll);
|
||||
@ -269,7 +269,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
};
|
||||
|
||||
const updateInvoice = function (invoice: string): void {
|
||||
const robot = garage.getSlot().robot;
|
||||
const robot = garage.getSlot()?.getRobot();
|
||||
|
||||
if (robot?.encPrivKey != null && robot?.token != null) {
|
||||
setLoadingButtons({ ...noLoadingButtons, submitInvoice: true });
|
||||
@ -284,7 +284,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
};
|
||||
|
||||
const updateAddress = function (): void {
|
||||
const robot = garage.getSlot().robot;
|
||||
const robot = garage.getSlot()?.getRobot();
|
||||
|
||||
if (robot?.encPrivKey != null && robot?.token != null) {
|
||||
setLoadingButtons({ ...noLoadingButtons, submitAddress: true });
|
||||
@ -306,10 +306,10 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
};
|
||||
|
||||
const submitStatement = function (): void {
|
||||
const robot = garage.getSlot().robot;
|
||||
const robot = garage.getSlot()?.getRobot();
|
||||
let statement = dispute.statement;
|
||||
if (dispute.attachLogs) {
|
||||
const payload = { statement, messages, token: robot.token };
|
||||
const payload = { statement, messages, token: robot?.token };
|
||||
statement = JSON.stringify(payload, null, 2);
|
||||
}
|
||||
setLoadingButtons({ ...noLoadingButtons, submitStatement: true });
|
||||
@ -361,15 +361,15 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
|
||||
// Effect on Order Status change (used for WebLN)
|
||||
useEffect(() => {
|
||||
const currentOrder = garage.getSlot().order;
|
||||
if (currentOrder !== null && currentOrder.status !== lastOrderStatus) {
|
||||
const currentOrder = garage.getSlot()?.order;
|
||||
if (currentOrder && currentOrder?.status !== lastOrderStatus) {
|
||||
setLastOrderStatus(currentOrder.status);
|
||||
void handleWebln(currentOrder);
|
||||
}
|
||||
}, [orderUpdatedAt]);
|
||||
|
||||
const statusToContract = function (): Contract {
|
||||
const order = garage.getSlot().order;
|
||||
const order = garage.getSlot()?.order;
|
||||
|
||||
const baseContract: Contract = {
|
||||
title: 'Unknown Order Status',
|
||||
@ -380,7 +380,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
titleIcon: () => <></>,
|
||||
};
|
||||
|
||||
if (order === null) return baseContract;
|
||||
if (!order) return baseContract;
|
||||
|
||||
const status = order.status;
|
||||
const isBuyer = order.is_buyer;
|
||||
@ -747,7 +747,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
setOpen(closeAll);
|
||||
}}
|
||||
waitingWebln={waitingWebln}
|
||||
isBuyer={garage.getSlot().order?.is_buyer ?? false}
|
||||
isBuyer={garage.getSlot()?.order?.is_buyer ?? false}
|
||||
/>
|
||||
<ConfirmDisputeDialog
|
||||
open={open.confirmDispute}
|
||||
@ -770,11 +770,11 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
}}
|
||||
onCollabCancelClick={cancel}
|
||||
loading={loadingButtons.cancel}
|
||||
peerAskedCancel={garage.getSlot().order?.pending_cancel ?? false}
|
||||
peerAskedCancel={garage.getSlot()?.order?.pending_cancel ?? false}
|
||||
/>
|
||||
<ConfirmFiatSentDialog
|
||||
open={open.confirmFiatSent}
|
||||
order={garage.getSlot().order}
|
||||
order={garage.getSlot()?.order ?? null}
|
||||
loadingButton={loadingButtons.fiatSent}
|
||||
onClose={() => {
|
||||
setOpen(closeAll);
|
||||
@ -791,14 +791,14 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
/>
|
||||
<ConfirmFiatReceivedDialog
|
||||
open={open.confirmFiatReceived}
|
||||
order={garage.getSlot().order}
|
||||
order={garage.getSlot()?.order ?? null}
|
||||
loadingButton={loadingButtons.fiatReceived}
|
||||
onClose={() => {
|
||||
setOpen(closeAll);
|
||||
}}
|
||||
onConfirmClick={confirmFiatReceived}
|
||||
/>
|
||||
<CollabCancelAlert order={garage.getSlot().order} />
|
||||
<CollabCancelAlert order={garage.getSlot()?.order ?? null} />
|
||||
<Grid
|
||||
container
|
||||
padding={1}
|
||||
@ -809,7 +809,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
>
|
||||
<Grid item>
|
||||
<Title
|
||||
order={garage.getSlot().order}
|
||||
order={garage.getSlot()?.order ?? null}
|
||||
text={contract?.title}
|
||||
color={contract?.titleColor}
|
||||
icon={contract?.titleIcon}
|
||||
@ -825,7 +825,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
<Divider />
|
||||
<BondStatus
|
||||
status={contract?.bondStatus}
|
||||
isMaker={garage.getSlot().order?.is_maker ?? false}
|
||||
isMaker={garage.getSlot()?.order?.is_maker ?? false}
|
||||
/>
|
||||
</Grid>
|
||||
) : (
|
||||
@ -834,7 +834,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
|
||||
<Grid item>
|
||||
<CancelButton
|
||||
order={garage.getSlot().order}
|
||||
order={garage.getSlot()?.order ?? null}
|
||||
onClickCancel={cancel}
|
||||
openCancelDialog={() => {
|
||||
setOpen({ ...closeAll, confirmCancel: true });
|
||||
|
@ -67,9 +67,8 @@ export const initialFederationContext: UseFederationStoreType = {
|
||||
export const FederationContext = createContext<UseFederationStoreType>(initialFederationContext);
|
||||
|
||||
export const useFederationStore = (): UseFederationStoreType => {
|
||||
const { settings, page, origin, hostUrl, open, torStatus } =
|
||||
useContext<UseAppStoreType>(AppContext);
|
||||
const { setMaker, garage, setBadOrder, robotUpdatedAt, badOrder, orderUpdatedAt } =
|
||||
const { settings, origin, hostUrl, open, torStatus } = useContext<UseAppStoreType>(AppContext);
|
||||
const { setMaker, garage, setBadOrder, robotUpdatedAt } =
|
||||
useContext<UseGarageStoreType>(GarageContext);
|
||||
const [federation, setFederation] = useState(initialFederationContext.federation);
|
||||
const sortedCoordinators = useMemo(() => {
|
||||
@ -106,10 +105,11 @@ export const useFederationStore = (): UseFederationStoreType => {
|
||||
|
||||
const fetchCurrentOrder = (): void => {
|
||||
const activeSlot = garage.getSlot();
|
||||
if (activeSlot.activeOrderShortAlias !== null && activeSlot.activeOrderId !== null) {
|
||||
const coordinator = federation.getCoordinator(activeSlot.activeOrderShortAlias);
|
||||
const robot = activeSlot?.getRobot(activeSlot?.activeShortAlias ?? '');
|
||||
if (robot && robot?.activeOrderId && activeSlot?.activeShortAlias) {
|
||||
const coordinator = federation.getCoordinator(activeSlot?.activeShortAlias);
|
||||
coordinator
|
||||
.fetchOrder(activeSlot.activeOrderId, activeSlot.robot)
|
||||
.fetchOrder(robot.activeOrderId, robot)
|
||||
.then((order) => {
|
||||
if (order?.bad_request !== undefined) {
|
||||
setBadOrder(order.bad_request);
|
||||
@ -135,18 +135,19 @@ export const useFederationStore = (): UseFederationStoreType => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const robot = garage.getSlot().robot;
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot();
|
||||
|
||||
if (robot !== null) {
|
||||
if (open.profile && robot?.avatarLoaded) {
|
||||
void federation.fetchRobot(garage, garage.currentSlot); // refresh/update existing robot
|
||||
if (robot && garage.currentSlot) {
|
||||
if (open.profile && slot?.avatarLoaded && slot.token) {
|
||||
void federation.fetchRobot(garage, slot.token); // refresh/update existing robot
|
||||
} else if (
|
||||
!robot.avatarLoaded &&
|
||||
!Boolean(slot?.avatarLoaded) &&
|
||||
robot.token !== undefined &&
|
||||
robot.encPrivKey !== undefined &&
|
||||
robot.pubKey !== undefined
|
||||
) {
|
||||
void federation.fetchRobot(garage, garage.currentSlot); // create new robot with existing token and keys (on network and coordinator change)
|
||||
void federation.fetchRobot(garage, robot.token); // create new robot with existing token and keys (on network and coordinator change)
|
||||
}
|
||||
}
|
||||
}, [open.profile, hostUrl, robotUpdatedAt]);
|
||||
|
@ -157,6 +157,7 @@ export class Coordinator {
|
||||
};
|
||||
|
||||
loadBook = (onDataLoad: () => void = () => {}): void => {
|
||||
if (!this.enabled) return;
|
||||
if (this.loadingBook) return;
|
||||
|
||||
this.loadingBook = true;
|
||||
@ -181,6 +182,7 @@ export class Coordinator {
|
||||
};
|
||||
|
||||
loadLimits = (onDataLoad: () => void = () => {}): void => {
|
||||
if (!this.enabled) return;
|
||||
if (this.loadingLimits) return;
|
||||
|
||||
this.loadingLimits = true;
|
||||
@ -208,6 +210,7 @@ export class Coordinator {
|
||||
};
|
||||
|
||||
loadInfo = (onDataLoad: () => void = () => {}): void => {
|
||||
if (!this.enabled) return;
|
||||
if (this.loadingInfo) return;
|
||||
|
||||
this.loadingInfo = true;
|
||||
@ -263,10 +266,12 @@ export class Coordinator {
|
||||
}
|
||||
};
|
||||
|
||||
fecthRobot = async (garage: Garage, index: number): Promise<Robot | null> => {
|
||||
const robot = garage?.getSlot(index).robot;
|
||||
fecthRobot = async (garage: Garage, token: string): Promise<Robot | null> => {
|
||||
if (!this.enabled) return null;
|
||||
|
||||
if (robot?.token == null) return null;
|
||||
const robot = garage?.getSlot(token)?.getRobot() ?? null;
|
||||
|
||||
if (robot?.token !== token) return null;
|
||||
|
||||
const authHeaders = robot.getAuthHeaders();
|
||||
|
||||
@ -292,29 +297,26 @@ export class Coordinator {
|
||||
last_login: data.last_login,
|
||||
pubKey: data.public_key,
|
||||
encPrivKey: data.encrypted_private_key,
|
||||
copiedToken: Boolean(data.found),
|
||||
};
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
garage.updateRobot(
|
||||
{
|
||||
...newAttributes,
|
||||
tokenSHA256: authHeaders.tokenSHA256,
|
||||
loading: false,
|
||||
bitsEntropy,
|
||||
shannonEntropy,
|
||||
shortAlias: this.shortAlias,
|
||||
},
|
||||
index,
|
||||
);
|
||||
garage.upsertRobot(token, this.shortAlias, {
|
||||
...newAttributes,
|
||||
tokenSHA256: authHeaders.tokenSHA256,
|
||||
loading: false,
|
||||
bitsEntropy,
|
||||
shannonEntropy,
|
||||
shortAlias: this.shortAlias,
|
||||
});
|
||||
|
||||
return garage.getSlot(index).robot;
|
||||
return garage.getSlot(this.shortAlias)?.getRobot() ?? null;
|
||||
};
|
||||
|
||||
fetchOrder = async (orderId: number, robot: Robot): Promise<Order | null> => {
|
||||
if (!this.enabled) return null;
|
||||
if (!(robot.token != null)) return null;
|
||||
|
||||
const authHeaders = robot.getAuthHeaders();
|
||||
@ -340,12 +342,14 @@ export class Coordinator {
|
||||
fetchReward = async (
|
||||
signedInvoice: string,
|
||||
garage: Garage,
|
||||
index: number,
|
||||
index: string,
|
||||
): Promise<null | {
|
||||
bad_invoice?: string;
|
||||
successful_withdrawal?: boolean;
|
||||
}> => {
|
||||
const robot = garage.getSlot(index).robot;
|
||||
if (!this.enabled) return null;
|
||||
|
||||
const robot = garage.getSlot(index)?.getRobot();
|
||||
|
||||
if (!(robot?.token != null) || !(robot.encPrivKey != null)) return null;
|
||||
|
||||
@ -357,17 +361,17 @@ export class Coordinator {
|
||||
},
|
||||
{ tokenSHA256: robot.tokenSHA256 },
|
||||
);
|
||||
const newRobot = {
|
||||
...robot,
|
||||
garage.upsertRobot(robot?.token, this.shortAlias, {
|
||||
earnedRewards: data?.successful_withdrawal === true ? 0 : robot.earnedRewards,
|
||||
};
|
||||
garage.updateRobot(newRobot, index);
|
||||
});
|
||||
|
||||
return data ?? {};
|
||||
};
|
||||
|
||||
fetchStealth = async (wantsStealth: boolean, garage: Garage, index: number): Promise<null> => {
|
||||
const robot = garage?.getSlot(index).robot;
|
||||
fetchStealth = async (wantsStealth: boolean, garage: Garage, index: string): Promise<null> => {
|
||||
if (!this.enabled) return null;
|
||||
|
||||
const robot = garage?.getSlot(index)?.getRobot();
|
||||
|
||||
if (!(robot?.token != null) || !(robot.encPrivKey != null)) return null;
|
||||
|
||||
@ -378,12 +382,9 @@ export class Coordinator {
|
||||
{ tokenSHA256: robot.tokenSHA256 },
|
||||
);
|
||||
|
||||
garage.updateRobot(
|
||||
{
|
||||
stealthInvoices: wantsStealth,
|
||||
},
|
||||
index,
|
||||
);
|
||||
garage.upsertRobot(robot?.token, this.shortAlias, {
|
||||
stealthInvoices: wantsStealth,
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
@ -90,9 +90,9 @@ export class Federation {
|
||||
};
|
||||
|
||||
// Fetchs
|
||||
fetchRobot = async (garage: Garage, slot: number): Promise<void> => {
|
||||
fetchRobot = async (garage: Garage, token: string): Promise<void> => {
|
||||
Object.values(this.coordinators).forEach((coor) => {
|
||||
void coor.fecthRobot(garage, slot);
|
||||
void coor.fecthRobot(garage, token);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,67 +1,45 @@
|
||||
import { Robot, type Order } from '.';
|
||||
import { type Order } from '.';
|
||||
import { systemClient } from '../services/System';
|
||||
import { saveAsJson } from '../utils';
|
||||
export interface Slot {
|
||||
robot: Robot;
|
||||
lastOrderId: number | null;
|
||||
lastOrderShortAlias: string | null;
|
||||
activeOrderId: number | null;
|
||||
activeOrderShortAlias: string | null;
|
||||
order: Order | null;
|
||||
}
|
||||
|
||||
const defaultSlot = {
|
||||
robot: new Robot(),
|
||||
lastOrderId: null,
|
||||
lastOrderShortAlias: null,
|
||||
activeOrderId: null,
|
||||
activeOrderShortAlias: null,
|
||||
order: null,
|
||||
};
|
||||
import Slot from './Slot.model';
|
||||
|
||||
type GarageHooks = 'onRobotUpdate' | 'onOrderUpdate';
|
||||
|
||||
class Garage {
|
||||
constructor() {
|
||||
this.slots = [];
|
||||
const slotsDump: string = systemClient.getItem('garage') ?? '';
|
||||
if (slotsDump !== '') {
|
||||
const rawSlots = JSON.parse(slotsDump);
|
||||
this.slots = rawSlots
|
||||
.filter((raw: any) => raw !== null)
|
||||
.map((raw: any) => {
|
||||
const robot = new Robot(raw.robot);
|
||||
robot.update(raw.robot);
|
||||
return {
|
||||
...defaultSlot,
|
||||
...raw,
|
||||
robot,
|
||||
order: null,
|
||||
};
|
||||
});
|
||||
console.log('Robot Garage was loaded from local storage');
|
||||
}
|
||||
|
||||
if (this.slots.length < 1) {
|
||||
this.slots = [defaultSlot];
|
||||
}
|
||||
|
||||
this.currentSlot = 0;
|
||||
this.slots = {};
|
||||
this.currentSlot = null;
|
||||
|
||||
this.hooks = {
|
||||
onRobotUpdate: [],
|
||||
onOrderUpdate: [],
|
||||
};
|
||||
|
||||
const slotsDump: string = systemClient.getItem('garageSlots') ?? '';
|
||||
if (slotsDump !== '') {
|
||||
const rawSlots = JSON.parse(slotsDump);
|
||||
Object.values(rawSlots).forEach((rawSlot: Record<any, any>) => {
|
||||
if (rawSlot?.token) {
|
||||
this.createSlot(rawSlot?.token);
|
||||
Object.keys(rawSlot.robots).forEach((shortAlias) => {
|
||||
const rawRobot = rawSlot.robots[shortAlias];
|
||||
this.upsertRobot(rawRobot.token, shortAlias, rawRobot);
|
||||
});
|
||||
this.currentSlot = rawSlot?.token;
|
||||
}
|
||||
});
|
||||
console.log('Robot Garage was loaded from local storage');
|
||||
}
|
||||
}
|
||||
|
||||
slots: Slot[];
|
||||
currentSlot: number;
|
||||
slots: { [token: string]: Slot };
|
||||
currentSlot: string | null;
|
||||
|
||||
hooks: Record<GarageHooks, Array<() => void>>;
|
||||
|
||||
// Hooks
|
||||
registerHook = (hookName: GarageHooks, fn: () => void): void => {
|
||||
this.hooks[hookName].push(fn);
|
||||
this.hooks[hookName]?.push(fn);
|
||||
};
|
||||
|
||||
triggerHook = (hookName: GarageHooks): void => {
|
||||
@ -72,103 +50,97 @@ class Garage {
|
||||
|
||||
// Storage
|
||||
download = (): void => {
|
||||
saveAsJson(`robotGarage_${new Date().toISOString()}.json`, this.slots);
|
||||
saveAsJson(`garageSlots_${new Date().toISOString()}.json`, this.slots);
|
||||
};
|
||||
|
||||
save = (): void => {
|
||||
const saveSlots = this.slots.filter((slot: Slot) => slot !== null);
|
||||
systemClient.setItem('garage', JSON.stringify(saveSlots));
|
||||
systemClient.setItem('garageSlots', JSON.stringify(this.slots));
|
||||
};
|
||||
|
||||
delete = (): void => {
|
||||
this.slots = {};
|
||||
this.currentSlot = null;
|
||||
systemClient.deleteItem('garageSlots');
|
||||
this.triggerHook('onRobotUpdate');
|
||||
this.triggerHook('onOrderUpdate');
|
||||
};
|
||||
|
||||
// Slots
|
||||
delete = (): void => {
|
||||
this.slots = [defaultSlot];
|
||||
systemClient.deleteItem('garage');
|
||||
this.triggerHook('onRobotUpdate');
|
||||
this.triggerHook('onOrderUpdate');
|
||||
getSlot: (token?: string) => Slot | null = (token) => {
|
||||
const currentToken = token ?? this.currentSlot;
|
||||
return currentToken ? this.slots[currentToken] ?? null : null;
|
||||
};
|
||||
|
||||
deleteSlot: (index?: number) => void = (index) => {
|
||||
const targetSlot = index ?? this.slots.length - 1;
|
||||
this.slots.splice(targetSlot, 1);
|
||||
this.currentSlot = 0;
|
||||
this.triggerHook('onRobotUpdate');
|
||||
this.triggerHook('onOrderUpdate');
|
||||
this.save();
|
||||
};
|
||||
|
||||
getSlot: (index?: number) => Slot = (index = this.currentSlot) => {
|
||||
if (this.slots[index] === undefined) {
|
||||
this.slots[index] = defaultSlot;
|
||||
createSlot: (token: string) => Slot | null = (token) => {
|
||||
if (token !== null) {
|
||||
this.slots[token] = new Slot(token);
|
||||
return this.slots[token];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return this.slots[index];
|
||||
deleteSlot: (token?: string) => void = (token) => {
|
||||
const tagetIndex = token ?? this.currentSlot;
|
||||
if (tagetIndex) {
|
||||
delete this.slots[tagetIndex];
|
||||
this.currentSlot = null;
|
||||
this.triggerHook('onRobotUpdate');
|
||||
this.triggerHook('onOrderUpdate');
|
||||
this.save();
|
||||
}
|
||||
};
|
||||
|
||||
updateSlot: (
|
||||
attributes: { avatarLoaded?: boolean; copiedToken?: boolean },
|
||||
token?: string,
|
||||
) => Slot | null = (attributes, token) => {
|
||||
const slot = this.getSlot(token);
|
||||
if (attributes) {
|
||||
if (attributes.avatarLoaded !== undefined) slot?.setAvatarLoaded(attributes.avatarLoaded);
|
||||
if (attributes.copiedToken !== undefined) slot?.setCopiedToken(attributes.copiedToken);
|
||||
this.triggerHook('onRobotUpdate');
|
||||
}
|
||||
return slot;
|
||||
};
|
||||
|
||||
// Robots
|
||||
updateRobot: (attributes: Record<any, any>, index?: number) => void = (
|
||||
upsertRobot: (token: string, shortAlias: string, attributes: Record<any, any>) => void = (
|
||||
token,
|
||||
shortAlias,
|
||||
attributes,
|
||||
index = this.currentSlot,
|
||||
) => {
|
||||
const robot = this.getSlot(index).robot;
|
||||
if (robot != null) {
|
||||
robot.update(attributes);
|
||||
if (attributes.lastOrderId !== undefined && attributes.lastOrderId != null) {
|
||||
this.slots[index].lastOrderId = attributes.lastOrderId;
|
||||
this.slots[index].lastOrderShortAlias = attributes.shortAlias;
|
||||
if (attributes.lastOrderId === this.slots[index].activeOrderId) {
|
||||
this.slots[index].activeOrderId = null;
|
||||
this.slots[index].activeOrderShortAlias = null;
|
||||
}
|
||||
}
|
||||
if (attributes.activeOrderId !== undefined && attributes.activeOrderId != null) {
|
||||
this.slots[index].activeOrderId = attributes.activeOrderId;
|
||||
this.slots[index].activeOrderShortAlias = attributes.shortAlias;
|
||||
this.slots[index].lastOrderId = null;
|
||||
this.slots[index].lastOrderShortAlias = null;
|
||||
}
|
||||
if (token === null || shortAlias === null) return;
|
||||
|
||||
let slot = this.getSlot(token);
|
||||
|
||||
if (slot === null && attributes.token) {
|
||||
slot = this.createSlot(attributes.token);
|
||||
}
|
||||
|
||||
if (slot) {
|
||||
slot.upsertRobot(shortAlias, attributes);
|
||||
this.triggerHook('onRobotUpdate');
|
||||
this.save();
|
||||
}
|
||||
};
|
||||
|
||||
createRobot = (attributes: Record<any, any>): void => {
|
||||
const newSlot = defaultSlot;
|
||||
newSlot.robot.update(attributes);
|
||||
this.slots.push(newSlot);
|
||||
this.currentSlot = this.slots.length - 1;
|
||||
this.save();
|
||||
};
|
||||
|
||||
getActiveOrderId: (index?: number) => number | null = (index = this.currentSlot) => {
|
||||
return this.getSlot(index)?.robot?.activeOrderId ?? null;
|
||||
};
|
||||
|
||||
// Orders
|
||||
updateOrder: (order: Order, index?: number) => void = (order, index = this.currentSlot) => {
|
||||
const updatedOrder = this.slots[index].order;
|
||||
if (updatedOrder !== null && updatedOrder.id === order.id) {
|
||||
Object.assign(updatedOrder, order);
|
||||
this.slots[index].order = updatedOrder;
|
||||
} else {
|
||||
this.slots[index].order = order;
|
||||
updateOrder: (order: Order) => void = (order) => {
|
||||
const slot = this.getSlot();
|
||||
if (slot) {
|
||||
const updatedOrder = slot.order ?? null;
|
||||
if (updatedOrder !== null && updatedOrder.id === order.id) {
|
||||
Object.assign(updatedOrder, order);
|
||||
slot.order = updatedOrder;
|
||||
} else {
|
||||
slot.order = order;
|
||||
}
|
||||
if (slot.order?.is_participant) {
|
||||
slot.activeShortAlias = order.shortAlias;
|
||||
}
|
||||
this.triggerHook('onOrderUpdate');
|
||||
this.save();
|
||||
}
|
||||
if (this.slots[index].order?.is_participant) {
|
||||
this.slots[index].activeOrderId = this.slots[index].order?.id ?? null;
|
||||
this.slots[index].activeOrderShortAlias = this.slots[index].order?.shortAlias ?? null;
|
||||
}
|
||||
this.triggerHook('onOrderUpdate');
|
||||
this.save();
|
||||
};
|
||||
|
||||
deleteOrder: (index?: number) => void = (index = this.currentSlot) => {
|
||||
this.slots[index].order = null;
|
||||
this.triggerHook('onOrderUpdate');
|
||||
this.save();
|
||||
};
|
||||
|
||||
getOrder: (index?: number) => Order | null = (index = this.currentSlot) => {
|
||||
return this.getSlot(index)?.order;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,7 @@ class Robot {
|
||||
public loading: boolean = true;
|
||||
public found: boolean = false;
|
||||
public last_login: string = '';
|
||||
public copiedToken: boolean = false;
|
||||
public avatarLoaded: boolean = false;
|
||||
public shortAlias: string = '';
|
||||
|
||||
update = (attributes: Record<string, any>): void => {
|
||||
Object.assign(this, attributes);
|
||||
|
62
frontend/src/models/Slot.model.ts
Normal file
62
frontend/src/models/Slot.model.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { Robot, type Order } from '.';
|
||||
|
||||
class Slot {
|
||||
constructor(token: string) {
|
||||
this.token = token;
|
||||
this.robots = {};
|
||||
this.order = null;
|
||||
this.activeShortAlias = null;
|
||||
this.lastShortAlias = null;
|
||||
this.copiedToken = false;
|
||||
this.avatarLoaded = false;
|
||||
}
|
||||
|
||||
token: string | null;
|
||||
robots: { [shortAlias: string]: Robot };
|
||||
order: Order | null;
|
||||
activeShortAlias: string | null;
|
||||
lastShortAlias: string | null;
|
||||
copiedToken: boolean;
|
||||
avatarLoaded: boolean;
|
||||
|
||||
setAvatarLoaded = (avatarLoaded: boolean): void => {
|
||||
this.avatarLoaded = avatarLoaded;
|
||||
};
|
||||
|
||||
setCopiedToken = (copied: boolean): void => {
|
||||
this.copiedToken = copied;
|
||||
};
|
||||
|
||||
getRobot = (shortAlias?: string): Robot | null => {
|
||||
if (shortAlias) {
|
||||
return this.robots[shortAlias];
|
||||
} else if (this.activeShortAlias !== null && this.robots[this.activeShortAlias]) {
|
||||
return this.robots[this.activeShortAlias];
|
||||
} else if (this.lastShortAlias !== null && this.robots[this.lastShortAlias]) {
|
||||
return this.robots[this.lastShortAlias];
|
||||
} else if (Object.values(this.robots).length > 0) {
|
||||
return Object.values(this.robots)[0];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
upsertRobot = (shortAlias: string, attributes: Record<any, any>): Robot | null => {
|
||||
if (this.robots[shortAlias] === undefined) this.robots[shortAlias] = new Robot();
|
||||
|
||||
this.robots[shortAlias].update(attributes);
|
||||
|
||||
if (attributes.lastOrderId !== undefined && attributes.lastOrderId != null) {
|
||||
this.lastShortAlias = shortAlias;
|
||||
if (this.activeShortAlias === shortAlias) {
|
||||
this.activeShortAlias = null;
|
||||
}
|
||||
}
|
||||
if (attributes.activeOrderId !== undefined && attributes.activeOrderId != null) {
|
||||
this.activeShortAlias = attributes.shortAlias;
|
||||
}
|
||||
|
||||
return this.robots[shortAlias];
|
||||
};
|
||||
}
|
||||
|
||||
export default Slot;
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Intercanviar?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "Pots afegir nous mètodes",
|
||||
"You must fill the form correctly": "Has d'omplir el formulari correctament",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "Reps aprox. {{swapSats}} LN Sats (les taxes poden variar)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "Du kannst neue Methoden hinzufügen",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap fuera de LN ",
|
||||
"Swap?": "¿Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "Puedes añadir nuevos métodos",
|
||||
"You must fill the form correctly": "Rellene correctamente el formulario",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "Recibes aproximadamente {{swapSats}} LN Sats (la comisión puede variar)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Échanger hors de LN ",
|
||||
"Swap?": "Échange?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "Vous pouvez ajouter de nouvelles méthodes",
|
||||
"You must fill the form correctly": "Vous devez remplir le formulaire correctement",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "Vous recevez environ {{swapSats}} LN Sats (les frais peuvent varier).",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap da LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "Puoi aggiungere nuovi metodi",
|
||||
"You must fill the form correctly": "Devi compilare il form correttamente",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "Riceverai circa {{swapSats}} Sats su LN (le commissioni possono variare)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "スワップ?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "新しい支払い方法を追加できます。",
|
||||
"You must fill the form correctly": "フォームに正しく入力する必要があります。",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "約{{swapSats}} ライトニングSatsを受け取ります(手数料は異なる場合があります)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "У Вас уже есть активный ордер",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Badilisha kutoka LN ",
|
||||
"Swap?": "Badilisha?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "Unaweza kuongeza njia mpya",
|
||||
"You must fill the form correctly": "Lazima ujaze fomu kwa usahihi",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "Unapokea takribani {{swapSats}} LN Sats (ada inaweza kutofautiana)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "Swap?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "You can add new methods",
|
||||
"You must fill the form correctly": "You must fill the form correctly",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "You receive approx {{swapSats}} LN Sats (fees might vary)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "交换?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "你可以添加新方式",
|
||||
"You must fill the form correctly": "你必须正确填写表格",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "你将接收大约{{swapSats}}闪电聪(费用会造成有所差异)",
|
||||
|
@ -387,6 +387,7 @@
|
||||
"Swap out of LN ": "Swap out of LN ",
|
||||
"Swap?": "交換?",
|
||||
"To protect your privacy, the exact location you pin will be slightly randomized.": "To protect your privacy, the exact location you pin will be slightly randomized.",
|
||||
"You are already maker of an active order": "You are already maker of an active order",
|
||||
"You can add new methods": "你可以添加新方式",
|
||||
"You must fill the form correctly": "你必須正確填寫表格",
|
||||
"You receive approx {{swapSats}} LN Sats (fees might vary)": "你將接收大約{{swapSats}}閃電聰(費用會造成有所差異)",
|
||||
|
Loading…
Reference in New Issue
Block a user