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