From e8ec7f989aa55c4c94ab4e4f83d2026fedc76c60 Mon Sep 17 00:00:00 2001 From: KoalaSat Date: Tue, 21 Nov 2023 18:36:59 +0100 Subject: [PATCH] Adapt garage to coordinators --- frontend/src/basic/BookPage/index.tsx | 2 +- frontend/src/basic/Main.tsx | 12 +- frontend/src/basic/MakerPage/index.tsx | 2 +- frontend/src/basic/NavBar/NavBar.tsx | 19 +- frontend/src/basic/OrderPage/index.tsx | 50 +++-- frontend/src/basic/RobotPage/Onboarding.tsx | 20 +- frontend/src/basic/RobotPage/RobotProfile.tsx | 52 +++-- frontend/src/basic/RobotPage/TokenInput.tsx | 8 +- frontend/src/basic/RobotPage/index.tsx | 36 +-- frontend/src/components/Dialogs/Profile.tsx | 17 +- .../src/components/Dialogs/StoreToken.tsx | 4 +- .../src/components/MakerForm/MakerForm.tsx | 16 +- .../src/components/Notifications/index.tsx | 12 +- .../components/OrderDetails/TakeButton.tsx | 8 +- frontend/src/components/RobotInfo/index.tsx | 76 ++++--- .../EncryptedSocketChat/index.tsx | 26 +-- .../EncryptedTurtleChat/index.tsx | 40 ++-- .../src/components/TradeBox/Prompts/Chat.tsx | 8 +- frontend/src/components/TradeBox/index.tsx | 44 ++-- frontend/src/contexts/FederationContext.ts | 25 ++- frontend/src/models/Coordinator.model.ts | 61 ++--- frontend/src/models/Federation.model.ts | 4 +- frontend/src/models/Garage.model.ts | 212 ++++++++---------- frontend/src/models/Robot.model.ts | 3 +- frontend/src/models/Slot.model.ts | 62 +++++ frontend/static/locales/ca.json | 1 + frontend/static/locales/cs.json | 1 + frontend/static/locales/de.json | 1 + frontend/static/locales/en.json | 1 + frontend/static/locales/es.json | 1 + frontend/static/locales/eu.json | 1 + frontend/static/locales/fr.json | 1 + frontend/static/locales/it.json | 1 + frontend/static/locales/ja.json | 1 + frontend/static/locales/pl.json | 1 + frontend/static/locales/pt.json | 1 + frontend/static/locales/ru.json | 1 + frontend/static/locales/sv.json | 1 + frontend/static/locales/sw.json | 1 + frontend/static/locales/th.json | 1 + frontend/static/locales/zh-SI.json | 1 + frontend/static/locales/zh-TR.json | 1 + 42 files changed, 461 insertions(+), 375 deletions(-) create mode 100644 frontend/src/models/Slot.model.ts diff --git a/frontend/src/basic/BookPage/index.tsx b/frontend/src/basic/BookPage/index.tsx index a8397612..bc4dd9b9 100644 --- a/frontend/src/basic/BookPage/index.tsx +++ b/frontend/src/basic/BookPage/index.tsx @@ -31,7 +31,7 @@ const BookPage = (): JSX.Element => { const chartWidthEm = width - maxBookTableWidth; const onOrderClicked = function (id: number, shortAlias: string): void { - if (garage.getSlot().robot.avatarLoaded) { + if (garage.getSlot()?.avatarLoaded) { setDelay(10000); navigate(`/order/${shortAlias}/${id}`); } else { diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index 0ff0290c..7176a041 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -40,22 +40,24 @@ const Main: React.FC = () => { setAvatarBaseUrl(federation.getCoordinator(sortedCoordinators[0]).getBaseUrl()); }, [settings.network, settings.selfhostedClient, federation, sortedCoordinators]); + const onLoad = () => { + garage.updateSlot({ avatarLoaded: true }); + }; + return ( { - garage.updateRobot({ avatarLoaded: true }); - }} + onLoad={onLoad} /> { setOpen({ ...closeAll, profile: true }); }} - rewards={garage.getSlot().robot.earnedRewards} + rewards={garage.getSlot()?.getRobot()?.earnedRewards} windowWidth={windowSize?.width} /> {settings.network === 'testnet' ? ( diff --git a/frontend/src/basic/MakerPage/index.tsx b/frontend/src/basic/MakerPage/index.tsx index b59fdc7e..e77b40e9 100644 --- a/frontend/src/basic/MakerPage/index.tsx +++ b/frontend/src/basic/MakerPage/index.tsx @@ -51,7 +51,7 @@ const MakerPage = (): JSX.Element => { ]); const onOrderClicked = function (id: number): void { - if (garage.getSlot().robot.avatarLoaded) { + if (garage.getSlot()?.avatarLoaded) { navigate(`/order/${id}`); } else { setOpenNoRobot(true); diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index 51c14168..4fbefe1d 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -42,7 +42,7 @@ const NavBar = (): JSX.Element => { const tabSx = smallBar ? { position: 'relative', - bottom: garage.getSlot().robot.avatarLoaded ? '0.9em' : '0.13em', + bottom: garage.getSlot()?.avatarLoaded ? '0.9em' : '0.13em', minWidth: '1em', } : { position: 'relative', bottom: '1em', minWidth: '2em' }; @@ -82,8 +82,9 @@ const NavBar = (): JSX.Element => { setPage(newPage); const param = newPage === 'order' - ? `${String(slot.activeOrderShortAlias)}/${String( - slot.activeOrderId ?? slot.lastOrderId, + ? `${String(slot?.activeShortAlias)}/${String( + slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId ?? + slot?.getRobot(slot?.lastShortAlias ?? '')?.lastOrderId, )}` : ''; setTimeout(() => { @@ -96,6 +97,8 @@ const NavBar = (): JSX.Element => { setOpen(closeAll); }, [page, setOpen]); + const slot = garage.getSlot(); + return ( { { setOpen({ ...closeAll, profile: !open.profile }); }} icon={ - garage.getSlot().robot.nickname != null && garage.getSlot().robot.avatarLoaded ? ( + slot?.getRobot()?.nickname != null && slot?.avatarLoaded ? ( ) : ( @@ -162,7 +165,9 @@ const NavBar = (): JSX.Element => { sx={tabSx} label={smallBar ? undefined : t('Order')} value='order' - disabled={!garage.getSlot().robot.avatarLoaded || !garage.getSlot().activeOrderId} + disabled={ + !slot?.avatarLoaded || !slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId + } icon={} iconPosition='start' /> diff --git a/frontend/src/basic/OrderPage/index.tsx b/frontend/src/basic/OrderPage/index.tsx index 89e7f11d..322186a3 100644 --- a/frontend/src/basic/OrderPage/index.tsx +++ b/frontend/src/basic/OrderPage/index.tsx @@ -26,6 +26,7 @@ const OrderPage = (): JSX.Element => { const [tab, setTab] = useState<'order' | 'contract'>('contract'); const [baseUrl, setBaseUrl] = useState(hostUrl); const [currentOrder, setCurrentOrder] = useState(null); + const [currentOrderId, setCurrentOrderId] = useState(null); useEffect(() => { const coordinator = federation.getCoordinator(params.shortAlias ?? ''); @@ -38,30 +39,41 @@ const OrderPage = (): JSX.Element => { setBaseUrl(`${url}${basePath}`); - if (currentOrder?.id !== Number(params.orderId)) { - const coordinator = federation.getCoordinator(params.shortAlias ?? ''); - coordinator - .fetchOrder(Number(params.orderId) ?? null, garage.getSlot().robot) - .then((order) => { - if (order?.bad_request !== undefined) { - setBadOrder(order.bad_request); - } else if (order !== null && order?.id !== null) { - setCurrentOrder(order); - if (order.is_participant) { - garage.updateOrder(order as Order); - } - } - }) - .catch((e) => { - console.log(e); - }); - } + const orderId = Number(params.orderId); + if (orderId && currentOrderId !== orderId) setCurrentOrderId(orderId); }, [params]); + useEffect(() => { + setCurrentOrder(null); + if (currentOrderId !== null) { + const coordinator = federation.getCoordinator(params.shortAlias ?? ''); + const slot = garage.getSlot(); + const robot = slot?.getRobot(); + if (robot && slot?.token) { + federation.fetchRobot(garage, slot.token); + coordinator + .fetchOrder(currentOrderId, robot) + .then((order) => { + if (order?.bad_request !== undefined) { + setBadOrder(order.bad_request); + } else if (order !== null && order?.id !== null) { + setCurrentOrder(order); + if (order.is_participant) { + garage.updateOrder(order as Order); + } + } + }) + .catch((e) => { + console.log(e); + }); + } + } + }, [currentOrderId]); + const onClickCoordinator = function (): void { if (currentOrder?.shortAlias != null) { setOpen((open) => { - return { ...open, coordinator: shortAlias }; + return { ...open, coordinator: currentOrder?.shortAlias }; }); } }; diff --git a/frontend/src/basic/RobotPage/Onboarding.tsx b/frontend/src/basic/RobotPage/Onboarding.tsx index 10deb8da..4abc0edb 100644 --- a/frontend/src/basic/RobotPage/Onboarding.tsx +++ b/frontend/src/basic/RobotPage/Onboarding.tsx @@ -61,6 +61,9 @@ const Onboarding = ({ }, 1000); }; + const slot = garage.getSlot(); + const robot = slot?.getRobot(); + return ( @@ -122,7 +125,6 @@ const Onboarding = ({ onClick={() => { setStep('2'); getGenerateRobot(inputToken); - garage.updateRobot({ nickname: undefined }); }} variant='contained' size='large' @@ -149,7 +151,7 @@ const Onboarding = ({ - {garage.getSlot().robot.avatarLoaded && Boolean(garage.getSlot().robot.nickname) ? ( + {slot?.avatarLoaded && Boolean(robot?.nickname) ? ( t('This is your trading avatar') ) : ( <> @@ -162,7 +164,7 @@ const Onboarding = ({ - {garage.getSlot().robot.avatarLoaded && Boolean(garage.getSlot().robot.nickname) ? ( + {slot?.avatarLoaded && Boolean(robot?.nickname) ? ( {t('Hi! My name is')} @@ -197,7 +199,7 @@ const Onboarding = ({ width: '1.5em', }} /> - {garage.getSlot().robot.nickname} + {robot.nickname} ) : null} - + ) : null} - {Boolean(garage.getSlot().lastOrderId) && - garage.getSlot().robot.avatarLoaded && - Boolean(garage.getSlot().robot.nickname) ? ( + {Boolean(robot?.lastOrderId) && Boolean(slot?.avatarLoaded) && Boolean(robot?.nickname) ? ( @@ -263,9 +262,9 @@ const RobotProfile = ({ {t('Building...')} ) : ( - garage.slots.map((slot: Slot, index: number) => { + Object.values(garage.slots).map((slot: Slot, index: number) => { return ( - + - {slot.robot.nickname} + {slot?.getRobot()?.nickname} @@ -323,7 +322,6 @@ const RobotProfile = ({ color='primary' onClick={() => { garage.delete(); - garage.currentSlot = 0; logoutRobot(); setView('welcome'); }} diff --git a/frontend/src/basic/RobotPage/TokenInput.tsx b/frontend/src/basic/RobotPage/TokenInput.tsx index 55bb2729..c4c83113 100644 --- a/frontend/src/basic/RobotPage/TokenInput.tsx +++ b/frontend/src/basic/RobotPage/TokenInput.tsx @@ -38,6 +38,10 @@ const TokenInput = ({ setShowCopied(false); }, [inputToken]); + useEffect(() => { + setShowCopied(false); + }, [showCopied]); + if (loading) { return ; } else { @@ -67,14 +71,14 @@ const TokenInput = ({ { systemClient.copyToClipboard(inputToken); setShowCopied(true); setTimeout(() => { setShowCopied(false); }, 1000); - garage.updateRobot({ copiedToken: true }); + garage.updateSlot({ copiedToken: true }, inputToken); }} > diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index 06004a47..793a4abd 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -30,7 +30,7 @@ interface RobotPageProps { const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => { const { torStatus, windowSize, settings, page } = useContext(AppContext); const { garage } = useContext(GarageContext); - const { federation } = useContext(FederationContext); + const { federation, sortedCoordinators } = useContext(FederationContext); const { t } = useTranslation(); const params = useParams(); const urlToken = settings.selfhostedClient ? params.token : null; @@ -41,15 +41,15 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => { const [badToken, setBadToken] = useState(''); const [inputToken, setInputToken] = useState(''); const [view, setView] = useState<'welcome' | 'onboarding' | 'recovery' | 'profile'>( - garage.getSlot().robot.token !== undefined ? 'profile' : 'welcome', + garage.currentSlot !== null ? 'profile' : 'welcome', ); useEffect(() => { - const token = urlToken ?? garage.getSlot().robot.token; - if (token !== undefined) { + const token = urlToken ?? garage.currentSlot; + if (token !== undefined && token !== null) { setInputToken(token); if (window.NativeRobosats === undefined || torStatus === '"Done"') { - getRecoverRobot(token); + getGenerateRobot(token); setView('profile'); } } @@ -65,32 +65,18 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => { } }, [inputToken]); - const getRecoverRobot = (token: string): void => { - setInputToken(token); - genKey(token) - .then((key) => { - garage.updateRobot({ - token, - pubKey: key.publicKeyArmored, - encPrivKey: key.encryptedPrivateKeyArmored, - }); - void federation.fetchRobot(garage, garage.currentSlot); - }) - .catch((error) => { - console.error('Error:', error); - }); - }; - const getGenerateRobot = (token: string): void => { setInputToken(token); genKey(token) .then((key) => { - garage.createRobot({ + garage.upsertRobot(token, sortedCoordinators[0], { token, pubKey: key.publicKeyArmored, encPrivKey: key.encryptedPrivateKeyArmored, + shortAlias: sortedCoordinators[0], }); - void federation.fetchRobot(garage, garage.currentSlot); + void federation.fetchRobot(garage, token); + garage.currentSlot = token; }) .catch((error) => { console.error('Error:', error); @@ -99,7 +85,7 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => { const logoutRobot = (): void => { setInputToken(''); - garage.deleteSlot(garage.currentSlot); + garage.deleteSlot(); }; if (!(window.NativeRobosats === undefined) && !(torStatus === 'DONE' || torStatus === '"Done"')) { @@ -194,7 +180,7 @@ const RobotPage = ({ avatarBaseUrl }: RobotPageProps): JSX.Element => { badToken={badToken} inputToken={inputToken} setInputToken={setInputToken} - getRecoverRobot={getRecoverRobot} + getRecoverRobot={getGenerateRobot} /> ) : null} diff --git a/frontend/src/components/Dialogs/Profile.tsx b/frontend/src/components/Dialogs/Profile.tsx index 97ee91d1..e8b3d427 100644 --- a/frontend/src/components/Dialogs/Profile.tsx +++ b/frontend/src/components/Dialogs/Profile.tsx @@ -33,7 +33,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element = const [loading, setLoading] = useState(true); useEffect(() => { - setLoading(garage.getSlot().robot.loading); + setLoading(!garage.getSlot()?.avatarLoaded); }, [robotUpdatedAt]); return ( @@ -57,7 +57,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element = - {garage.getSlot().robot.nickname !== undefined && ( + {garage.getSlot()?.getRobot()?.nickname !== undefined && (
@@ -83,7 +83,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element = @@ -97,15 +97,10 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element = {Object.values(federation.coordinators).map((coordinator: Coordinator): JSX.Element => { - if (!garage.getSlot().robot?.loading) { + if (garage.getSlot()?.avatarLoaded) { return (
- +
); } else { diff --git a/frontend/src/components/Dialogs/StoreToken.tsx b/frontend/src/components/Dialogs/StoreToken.tsx index 4c5d545f..c48d2ff7 100644 --- a/frontend/src/components/Dialogs/StoreToken.tsx +++ b/frontend/src/components/Dialogs/StoreToken.tsx @@ -43,7 +43,7 @@ const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): J sx={{ width: '100%', maxWidth: '550px' }} disabled label={t('Back it up!')} - value={garage.getSlot().robot.token} + value={garage.getSlot()?.token} variant='filled' size='small' InputProps={{ @@ -51,7 +51,7 @@ const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): J { - systemClient.copyToClipboard(garage.getSlot().robot.token); + systemClient.copyToClipboard(garage.getSlot()?.token ?? ''); }} > diff --git a/frontend/src/components/MakerForm/MakerForm.tsx b/frontend/src/components/MakerForm/MakerForm.tsx index 1f49b361..6a592da9 100644 --- a/frontend/src/components/MakerForm/MakerForm.tsx +++ b/frontend/src/components/MakerForm/MakerForm.tsx @@ -91,6 +91,11 @@ const MakerForm = ({ const minRangeAmountMultiple = 1.6; const amountSafeThresholds = [1.03, 0.98]; + useEffect(() => { + const slot = garage.getSlot(); + if (slot?.token) federation.fetchRobot(garage, slot?.token); + }, [garage.currentSlot]); + useEffect(() => { setCurrencyCode(currencyDict[fav.currency === 0 ? 1 : fav.currency]); if (maker.coordinator != null) { @@ -280,12 +285,19 @@ const MakerForm = ({ }; const handleCreateOrder = function (): void { + const slot = garage.getSlot(); + + if (slot?.activeShortAlias) { + setBadRequest(t('You are already maker of an active order')); + return; + } + const { url, basePath } = federation .getCoordinator(maker.coordinator) ?.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl) ?? {}; - const auth = garage.getSlot().robot.getAuthHeaders(); + const auth = slot?.getRobot()?.getAuthHeaders(); if (!disableRequest && maker.coordinator != null && auth !== null) { setSubmittingRequest(true); @@ -565,7 +577,7 @@ const MakerForm = ({ setOpenDialogs(false); }} onClickDone={handleCreateOrder} - hasRobot={garage.getSlot().robot.avatarLoaded} + hasRobot={garage.getSlot()?.avatarLoaded ?? false} onClickGenerateRobot={onClickGenerateRobot} /> { - const order = garage.getOrder(); - if (order !== null) { + const order = garage.getSlot()?.order; + if (order !== undefined && order !== null) { if (order.status !== oldOrderStatus) { handleStatusChange(oldOrderStatus, order.status); setOldOrderStatus(order.status); diff --git a/frontend/src/components/OrderDetails/TakeButton.tsx b/frontend/src/components/OrderDetails/TakeButton.tsx index b60c77e3..0bc3004d 100644 --- a/frontend/src/components/OrderDetails/TakeButton.tsx +++ b/frontend/src/components/OrderDetails/TakeButton.tsx @@ -314,7 +314,9 @@ const TakeButton = ({ }; const takeOrder = function (): void { - if (currentOrder === null) return; + const robot = garage.getSlot()?.getRobot() ?? null; + + if (currentOrder === null || robot === null) return; setLoadingTake(true); const { url, basePath } = federation @@ -328,7 +330,7 @@ const TakeButton = ({ action: 'take', amount: currentOrder?.currency === 1000 ? takeAmount / 100000000 : takeAmount, }, - { tokenSHA256: garage.getSlot().robot.tokenSHA256 }, + { tokenSHA256: robot?.tokenSHA256 }, ) .then((data) => { setLoadingTake(false); @@ -370,7 +372,7 @@ const TakeButton = ({ setLoadingTake(true); setOpen(closeAll); }} - hasRobot={garage.getSlot().robot.avatarLoaded} + hasRobot={garage.getSlot()?.avatarLoaded ?? false} onClickGenerateRobot={onClickGenerateRobot} /> diff --git a/frontend/src/components/RobotInfo/index.tsx b/frontend/src/components/RobotInfo/index.tsx index 3463974b..b3acd7b6 100644 --- a/frontend/src/components/RobotInfo/index.tsx +++ b/frontend/src/components/RobotInfo/index.tsx @@ -23,7 +23,7 @@ import { } from '@mui/material'; import { Numbers, Send, EmojiEvents, ExpandMore } from '@mui/icons-material'; import { useNavigate } from 'react-router-dom'; -import { type Coordinator, type Robot } from '../../models'; +import { type Coordinator } from '../../models'; import { useTranslation } from 'react-i18next'; import { EnableTelegramDialog } from '../Dialogs'; import { UserNinjaIcon } from '../Icons'; @@ -33,13 +33,11 @@ import { signCleartextMessage } from '../../pgp'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; interface Props { - robot: Robot; - slotIndex: number; coordinator: Coordinator; onClose: () => void; } -const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: Props) => { +const RobotInfo: React.FC = ({ coordinator, onClose }: Props) => { const { garage } = useContext(GarageContext); const navigate = useNavigate(); const { t } = useTranslation(); @@ -71,7 +69,8 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: const handleWeblnInvoiceClicked = async (e: MouseEvent): void => { e.preventDefault(); - if (robot.earnedRewards > 0) { + const robot = garage.getSlot()?.getRobot(coordinator.shortAlias); + if (robot && robot.earnedRewards > 0) { const webln = await getWebln(); const invoice = webln.makeInvoice(robot.earnedRewards).then(() => { if (invoice != null) { @@ -85,12 +84,13 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: setBadInvoice(''); setShowRewardsSpinner(true); - const robot = garage.getSlot(slotIndex).robot; + const slot = garage.getSlot(); + const robot = slot?.getRobot(coordinator.shortAlias); - if (robot.encPrivKey != null && robot.token != null) { + if (robot && slot?.token && robot.encPrivKey != null && robot.token != null) { void signCleartextMessage(rewardInvoice, robot.encPrivKey, robot.token).then( (signedInvoice) => { - void coordinator.fetchReward(signedInvoice, garage, slotIndex).then((data) => { + void coordinator.fetchReward(signedInvoice, garage, slot?.token).then((data) => { setBadInvoice(data.bad_invoice ?? ''); setShowRewardsSpinner(false); setWithdrawn(data.successful_withdrawal); @@ -103,32 +103,42 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: }; const setStealthInvoice = (wantsStealth: boolean): void => { - void coordinator.fetchStealth(wantsStealth, garage, slotIndex); + const slot = garage.getSlot(); + if (slot?.token) { + void coordinator.fetchStealth(wantsStealth, garage, slot?.token); + } }; + const slot = garage.getSlot(); + const robot = slot?.getRobot(coordinator.shortAlias); + return ( }> {`${coordinator.longAlias}:`} - {garage.getSlot(slotIndex).robot.earnedRewards > 0 && ( + {(robot?.earnedRewards ?? 0) > 0 && (  {t('Claim Sats!')} )} - {(garage.getSlot(slotIndex).robot.activeOrderId ?? 0) > 0 && ( + {slot?.activeShortAlias === coordinator.shortAlias && (  {t('Active order!')} )} - {(garage.getSlot(slotIndex).robot.lastOrderId ?? 0) > 0 && - robot.activeOrderId === undefined && ( -  {t('finished order')} - )} + {slot?.lastShortAlias === coordinator.shortAlias && ( +  {t('finished order')} + )} - {(garage.getSlot(slotIndex).robot.activeOrderId ?? 0) > 0 ? ( + {} + {slot?.activeShortAlias && slot?.activeShortAlias === coordinator.shortAlias ? ( { - navigate(`/order/${coordinator.shortAlias}/${String(robot.activeOrderId)}`); + navigate( + `/order/${slot?.activeShortAlias}/${String( + slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId, + )}`, + ); onClose(); }} > @@ -138,14 +148,20 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: - ) : (garage.getSlot(slotIndex).robot.lastOrderId ?? 0) > 0 ? ( + ) : (robot?.lastOrderId ?? 0) > 0 && slot?.lastShortAlias === coordinator.shortAlias ? ( { - navigate(`/order/${coordinator.shortAlias}/${String(robot.lastOrderId)}`); + navigate( + `/order/${slot?.lastShortAlias}/${String( + slot?.getRobot(slot?.lastShortAlias ?? '')?.lastOrderId, + )}`, + ); onClose(); }} > @@ -153,7 +169,9 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: @@ -176,8 +194,8 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: onClose={() => { setOpenEnableTelegram(false); }} - tgBotName={robot.tgBotName} - tgToken={robot.tgToken} + tgBotName={robot?.tgBotName ?? ''} + tgToken={robot?.tgToken ?? ''} /> @@ -186,7 +204,7 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: - {robot.tgEnabled ? ( + {robot?.tgEnabled ? ( {t('Telegram enabled')} @@ -222,9 +240,9 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: label={t('Use stealth invoices')} control={ { - setStealthInvoice(!robot.stealthInvoices); + setStealthInvoice(!robot?.stealthInvoices); }} /> } @@ -243,12 +261,12 @@ const RobotInfo: React.FC = ({ robot, slotIndex, coordinator, onClose }: - {`${robot.earnedRewards} Sats`} + {`${robot?.earnedRewards} Sats`}