mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 20:21:35 +00:00
parent
937ac62c5d
commit
eb840c5b14
@ -1,4 +1,4 @@
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
CommunityDialog,
|
||||
ExchangeDialog,
|
||||
@ -9,16 +9,16 @@ import {
|
||||
ClientDialog,
|
||||
UpdateDialog,
|
||||
} from '../../components/Dialogs';
|
||||
import { pn } from '../../utils';
|
||||
import { AppContext, type UseAppStoreType, closeAll } from '../../contexts/AppContext';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { UseGarageStoreType, GarageContext } from '../../contexts/GarageContext';
|
||||
|
||||
export interface OpenDialogs {
|
||||
more: boolean;
|
||||
learn: boolean;
|
||||
community: boolean;
|
||||
info: boolean;
|
||||
coordinator: boolean;
|
||||
coordinator: string;
|
||||
exchange: boolean;
|
||||
client: boolean;
|
||||
update: boolean;
|
||||
@ -29,19 +29,8 @@ export interface OpenDialogs {
|
||||
const MainDialogs = (): JSX.Element => {
|
||||
const { open, setOpen, settings, clientVersion, hostUrl } =
|
||||
useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, focusedCoordinator, coordinatorUpdatedAt } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
|
||||
const [maxAmount, setMaxAmount] = useState<string>('...loading...');
|
||||
|
||||
useEffect(() => {
|
||||
if (focusedCoordinator !== null && focusedCoordinator !== '') {
|
||||
const limits = federation.getCoordinator(focusedCoordinator).limits;
|
||||
if (limits[1000] !== undefined) {
|
||||
setMaxAmount(pn(limits[1000].max_amount * 100000000));
|
||||
}
|
||||
}
|
||||
}, [coordinatorUpdatedAt]);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -56,7 +45,7 @@ const MainDialogs = (): JSX.Element => {
|
||||
/>
|
||||
<AboutDialog
|
||||
open={open.info}
|
||||
maxAmount={maxAmount}
|
||||
maxAmount={'100000'} //FIXME About dialog shoul allow to change coordinator
|
||||
onClose={() => {
|
||||
setOpen((open) => {
|
||||
return { ...open, info: false };
|
||||
@ -103,16 +92,12 @@ const MainDialogs = (): JSX.Element => {
|
||||
}}
|
||||
/>
|
||||
<CoordinatorDialog
|
||||
open={open.coordinator}
|
||||
open={Boolean(open.coordinator)}
|
||||
network={settings.network}
|
||||
onClose={() => {
|
||||
setOpen(closeAll);
|
||||
}}
|
||||
coordinator={
|
||||
focusedCoordinator !== null && focusedCoordinator !== ''
|
||||
? federation.getCoordinator(focusedCoordinator)
|
||||
: null
|
||||
}
|
||||
shortAlias={open.coordinator}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -6,7 +6,6 @@ import { useNavigate, useParams } from 'react-router-dom';
|
||||
import TradeBox from '../../components/TradeBox';
|
||||
import OrderDetails from '../../components/OrderDetails';
|
||||
|
||||
import { apiClient } from '../../services/api';
|
||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
@ -15,8 +14,7 @@ import { type Order } from '../../models';
|
||||
const OrderPage = (): JSX.Element => {
|
||||
const { windowSize, setOpen, settings, navbarHeight, hostUrl, origin } =
|
||||
useContext<UseAppStoreType>(AppContext);
|
||||
const { setFocusedCoordinator, federation, focusedCoordinator } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { garage, badOrder, setBadOrder } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@ -40,32 +38,18 @@ const OrderPage = (): JSX.Element => {
|
||||
|
||||
setBaseUrl(`${url}${basePath}`);
|
||||
|
||||
if (garage.getSlot().activeOrderId === Number(params.orderId)) {
|
||||
if (garage.getSlot().order != null) {
|
||||
setCurrentOrder(garage.getSlot().order);
|
||||
} else {
|
||||
coordinator
|
||||
.fetchOrder(Number(params.orderId) ?? null, garage.getSlot().robot)
|
||||
.then((order) => {
|
||||
if (order?.bad_request !== undefined) {
|
||||
setBadOrder(order.bad_request);
|
||||
} else {
|
||||
setCurrentOrder(order);
|
||||
garage.updateOrder(order as Order);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
} else if (order !== null && order?.id !== null) {
|
||||
setCurrentOrder(order);
|
||||
if (order.is_participant) {
|
||||
garage.updateOrder(order as Order);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
@ -76,50 +60,9 @@ const OrderPage = (): JSX.Element => {
|
||||
|
||||
const onClickCoordinator = function (): void {
|
||||
if (currentOrder?.shortAlias != null) {
|
||||
setFocusedCoordinator(currentOrder.shortAlias);
|
||||
}
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: true };
|
||||
});
|
||||
};
|
||||
|
||||
const renewOrder = function (): void {
|
||||
const order = currentOrder;
|
||||
if (order !== null && focusedCoordinator != null) {
|
||||
const body = {
|
||||
type: order.type,
|
||||
currency: order.currency,
|
||||
amount: order.has_range ? null : order.amount,
|
||||
has_range: order.has_range,
|
||||
min_amount: order.min_amount,
|
||||
max_amount: order.max_amount,
|
||||
payment_method: order.payment_method,
|
||||
is_explicit: order.is_explicit,
|
||||
premium: order.is_explicit ? null : order.premium,
|
||||
satoshis: order.is_explicit ? order.satoshis : null,
|
||||
public_duration: order.public_duration,
|
||||
escrow_duration: order.escrow_duration,
|
||||
bond_size: order.bond_size,
|
||||
latitude: order.latitude,
|
||||
longitude: order.longitude,
|
||||
};
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(order.shortAlias)
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(url + basePath, '/api/make/', body, {
|
||||
tokenSHA256: garage.getSlot().robot.tokenSHA256,
|
||||
})
|
||||
.then((data: any) => {
|
||||
if (data.bad_request !== undefined) {
|
||||
setBadOrder(data.bad_request);
|
||||
} else if (data.id !== undefined) {
|
||||
navigate(`/order/${String(currentOrder?.shortAlias)}/${String(data.id)}`);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setBadOrder('Request error');
|
||||
});
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: shortAlias };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -127,6 +70,26 @@ const OrderPage = (): JSX.Element => {
|
||||
navigate('/robot');
|
||||
};
|
||||
|
||||
const orderDetailsSpace = currentOrder ? (
|
||||
<OrderDetails
|
||||
shortAlias={String(currentOrder.shortAlias)}
|
||||
currentOrder={currentOrder}
|
||||
onClickCoordinator={onClickCoordinator}
|
||||
baseUrl={baseUrl}
|
||||
onClickGenerateRobot={() => {
|
||||
navigate('/robot');
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
const tradeBoxSpace = currentOrder ? (
|
||||
<TradeBox baseUrl={baseUrl} onStartAgain={startAgain} />
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{currentOrder === null && badOrder === undefined && <CircularProgress />}
|
||||
@ -156,15 +119,7 @@ const OrderPage = (): JSX.Element => {
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<OrderDetails
|
||||
shortAlias={String(currentOrder.shortAlias)}
|
||||
currentOrder={currentOrder}
|
||||
onClickCoordinator={onClickCoordinator}
|
||||
baseUrl={baseUrl}
|
||||
onClickGenerateRobot={() => {
|
||||
navigate('/robot');
|
||||
}}
|
||||
/>
|
||||
{orderDetailsSpace}
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={6} style={{ width: '21em' }}>
|
||||
@ -176,15 +131,7 @@ const OrderPage = (): JSX.Element => {
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<TradeBox
|
||||
robot={garage.getSlot().robot}
|
||||
currentOrder={currentOrder}
|
||||
settings={settings}
|
||||
setBadOrder={setBadOrder}
|
||||
baseUrl={baseUrl}
|
||||
onRenewOrder={renewOrder}
|
||||
onStartAgain={startAgain}
|
||||
/>
|
||||
{tradeBoxSpace}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -194,7 +141,7 @@ const OrderPage = (): JSX.Element => {
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', width: '21em' }}>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={(mouseEvent, value) => {
|
||||
onChange={(_mouseEvent, value) => {
|
||||
setTab(value);
|
||||
}}
|
||||
variant='fullWidth'
|
||||
@ -211,28 +158,8 @@ const OrderPage = (): JSX.Element => {
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: tab === 'order' ? '' : 'none' }}>
|
||||
<OrderDetails
|
||||
shortAlias={String(currentOrder.shortAlias)}
|
||||
currentOrder={currentOrder}
|
||||
onClickCoordinator={onClickCoordinator}
|
||||
baseUrl={baseUrl}
|
||||
onClickGenerateRobot={() => {
|
||||
navigate('/robot');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: tab === 'contract' ? '' : 'none' }}>
|
||||
<TradeBox
|
||||
robot={garage.getSlot().robot}
|
||||
currentOrder={currentOrder}
|
||||
settings={settings}
|
||||
setBadOrder={setBadOrder}
|
||||
baseUrl={baseUrl}
|
||||
onRenewOrder={renewOrder}
|
||||
onStartAgain={startAgain}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: tab === 'order' ? '' : 'none' }}>{orderDetailsSpace}</div>
|
||||
<div style={{ display: tab === 'contract' ? '' : 'none' }}>{tradeBoxSpace}</div>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
@ -245,15 +172,7 @@ const OrderPage = (): JSX.Element => {
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<OrderDetails
|
||||
shortAlias={String(currentOrder.shortAlias)}
|
||||
currentOrder={currentOrder}
|
||||
onClickCoordinator={onClickCoordinator}
|
||||
baseUrl={hostUrl}
|
||||
onClickGenerateRobot={() => {
|
||||
navigate('/robot');
|
||||
}}
|
||||
/>
|
||||
{orderDetailsSpace}
|
||||
</Paper>
|
||||
)
|
||||
) : (
|
||||
|
@ -25,14 +25,7 @@ const SettingsPage = (): JSX.Element => {
|
||||
<SettingsForm showNetwork={!(window.NativeRobosats === undefined)} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<FederationTable
|
||||
openCoordinator={() => {
|
||||
setOpen({ ...open, coordinator: true });
|
||||
}}
|
||||
baseUrl={hostUrl}
|
||||
maxHeight={14}
|
||||
network={settings.network}
|
||||
/>
|
||||
<FederationTable baseUrl={hostUrl} maxHeight={14} network={settings.network} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
@ -108,7 +108,7 @@ const BookTable = ({
|
||||
}: BookTableProps): JSX.Element => {
|
||||
const { fav, setFav, settings, setOpen, hostUrl, origin } =
|
||||
useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, setFocusedCoordinator, coordinatorUpdatedAt } =
|
||||
const { federation, coordinatorUpdatedAt } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
|
||||
const { t } = useTranslation();
|
||||
@ -273,9 +273,8 @@ const BookTable = ({
|
||||
}, []);
|
||||
|
||||
const onClickCoordinator = function (shortAlias: string): void {
|
||||
setFocusedCoordinator(shortAlias);
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: true };
|
||||
return { ...open, coordinator: shortAlias };
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -62,11 +62,12 @@ import {
|
||||
import { AppContext } from '../../contexts/AppContext';
|
||||
import { systemClient } from '../../services/System';
|
||||
import { type Badges } from '../../models/Coordinator.model';
|
||||
import { UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
coordinator: Coordinator | null;
|
||||
shortAlias: string | null;
|
||||
network: 'mainnet' | 'testnet' | undefined;
|
||||
}
|
||||
|
||||
@ -335,9 +336,11 @@ const BadgesHall = ({ badges }: BadgesProps): JSX.Element => {
|
||||
);
|
||||
};
|
||||
|
||||
const CoordinatorDialog = ({ open = false, onClose, coordinator, network }: Props): JSX.Element => {
|
||||
const CoordinatorDialog = ({ open = false, onClose, network, shortAlias }: Props): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const { clientVersion, page, hostUrl } = useContext(AppContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const coordinator = federation.getCoordinator(shortAlias);
|
||||
|
||||
const [expanded, setExpanded] = useState<'summary' | 'stats' | 'policies' | undefined>(undefined);
|
||||
|
||||
|
@ -9,22 +9,20 @@ import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||
|
||||
interface FederationTableProps {
|
||||
openCoordinator: () => void;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
fillContainer?: boolean;
|
||||
}
|
||||
|
||||
const FederationTable = ({
|
||||
openCoordinator,
|
||||
maxWidth = 90,
|
||||
maxHeight = 50,
|
||||
fillContainer = false,
|
||||
}: FederationTableProps): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const { federation, sortedCoordinators, setFocusedCoordinator, coordinatorUpdatedAt } =
|
||||
const { federation, sortedCoordinators, coordinatorUpdatedAt } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
const { hostUrl } = useContext<UseAppStoreType>(AppContext);
|
||||
const { hostUrl, setOpen } = useContext<UseAppStoreType>(AppContext);
|
||||
const theme = useTheme();
|
||||
const [pageSize, setPageSize] = useState<number>(0);
|
||||
|
||||
@ -52,8 +50,9 @@ const FederationTable = ({
|
||||
};
|
||||
|
||||
const onClickCoordinator = function (shortAlias: string): void {
|
||||
setFocusedCoordinator(shortAlias);
|
||||
openCoordinator();
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: shortAlias };
|
||||
});
|
||||
};
|
||||
|
||||
const aliasObj = useCallback((width: number) => {
|
||||
|
@ -68,7 +68,7 @@ const MakerForm = ({
|
||||
onClickGenerateRobot = () => null,
|
||||
}: MakerFormProps): JSX.Element => {
|
||||
const { fav, setFav, settings, hostUrl, origin } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, focusedCoordinator, coordinatorUpdatedAt, federationUpdatedAt } =
|
||||
const { federation, coordinatorUpdatedAt, federationUpdatedAt } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
const { maker, setMaker, garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
|
||||
@ -93,8 +93,8 @@ const MakerForm = ({
|
||||
|
||||
useEffect(() => {
|
||||
setCurrencyCode(currencyDict[fav.currency === 0 ? 1 : fav.currency]);
|
||||
if (focusedCoordinator != null) {
|
||||
const newLimits = federation.getCoordinator(focusedCoordinator).limits;
|
||||
if (maker.coordinator != null) {
|
||||
const newLimits = federation.getCoordinator(maker.coordinator).limits;
|
||||
if (Object.keys(newLimits).length !== 0) {
|
||||
updateAmountLimits(newLimits, fav.currency, maker.premium);
|
||||
updateCurrentPrice(newLimits, fav.currency, maker.premium);
|
||||
@ -285,15 +285,9 @@ const MakerForm = ({
|
||||
.getCoordinator(maker.coordinator)
|
||||
?.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl) ?? {};
|
||||
|
||||
const auth = {
|
||||
tokenSHA256: garage.getSlot().robot.tokenSHA256,
|
||||
keys: {
|
||||
pubKey: garage.getSlot().robot.pubKey?.split('\n').join('\\'),
|
||||
encPrivKey: garage.getSlot().robot.encPrivKey?.split('\n').join('\\'),
|
||||
},
|
||||
};
|
||||
const auth = garage.getSlot().robot.getAuthHeaders();
|
||||
|
||||
if (!disableRequest && focusedCoordinator != null) {
|
||||
if (!disableRequest && maker.coordinator != null && auth !== null) {
|
||||
setSubmittingRequest(true);
|
||||
const body = {
|
||||
type: fav.type === 0 ? 1 : 0,
|
||||
@ -320,6 +314,7 @@ const MakerForm = ({
|
||||
setBadRequest(data.bad_request);
|
||||
if (data.id !== undefined) {
|
||||
onOrderCreated(maker.coordinator, data.id);
|
||||
garage.updateOrder(data);
|
||||
}
|
||||
setSubmittingRequest(false);
|
||||
})
|
||||
@ -446,9 +441,9 @@ const MakerForm = ({
|
||||
};
|
||||
|
||||
const amountLabel = useMemo(() => {
|
||||
if (!(focusedCoordinator != null)) return;
|
||||
if (!(maker.coordinator != null)) return;
|
||||
|
||||
const info = federation.getCoordinator(focusedCoordinator)?.info;
|
||||
const info = federation.getCoordinator(maker.coordinator)?.info;
|
||||
const defaultRoutingBudget = 0.001;
|
||||
let label = t('Amount');
|
||||
let helper = '';
|
||||
|
@ -22,15 +22,13 @@ interface SelectCoordinatorProps {
|
||||
|
||||
const SelectCoordinator: React.FC<SelectCoordinatorProps> = ({ coordinator, setCoordinator }) => {
|
||||
const { setOpen, hostUrl } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, setFocusedCoordinator, sortedCoordinators } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onClickCurrentCoordinator = function (shortAlias: string): void {
|
||||
setFocusedCoordinator(shortAlias);
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: true };
|
||||
return { ...open, coordinator: shortAlias };
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -47,9 +47,6 @@ const RobotAvatar: React.FC<Props> = ({
|
||||
coordinator = false,
|
||||
baseUrl,
|
||||
}) => {
|
||||
const { settings, origin, hostUrl } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, focusedCoordinator } = useContext<UseFederationStoreType>(FederationContext);
|
||||
|
||||
const [avatarSrc, setAvatarSrc] = useState<string>();
|
||||
const [nicknameReady, setNicknameReady] = useState<boolean>(false);
|
||||
const [activeBackground, setActiveBackground] = useState<boolean>(true);
|
||||
@ -66,13 +63,10 @@ const RobotAvatar: React.FC<Props> = ({
|
||||
if (window.NativeRobosats === undefined) {
|
||||
setAvatarSrc(`${baseUrl}${path}${nickname}${small ? '.small' : ''}.webp`);
|
||||
setNicknameReady(true);
|
||||
} else if (focusedCoordinator != null) {
|
||||
} else if (baseUrl != null && apiClient.fileImageUrl !== undefined) {
|
||||
setNicknameReady(true);
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(focusedCoordinator)
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
void apiClient
|
||||
.fileImageUrl(url + basePath, `${path}${nickname}${small ? '.small' : ''}.webp`)
|
||||
.fileImageUrl(baseUrl, `${path}${nickname}${small ? '.small' : ''}.webp`)
|
||||
.then(setAvatarSrc);
|
||||
}
|
||||
} else {
|
||||
|
@ -25,7 +25,6 @@ const audioPath =
|
||||
interface Props {
|
||||
orderId: number;
|
||||
status: number;
|
||||
robot: Robot;
|
||||
userNick: string;
|
||||
takerNick: string;
|
||||
messages: EncryptedChatMessage[];
|
||||
@ -38,7 +37,6 @@ interface Props {
|
||||
const EncryptedSocketChat: React.FC<Props> = ({
|
||||
orderId,
|
||||
status,
|
||||
robot,
|
||||
userNick,
|
||||
takerNick,
|
||||
messages,
|
||||
@ -110,7 +108,7 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
setConnected(true);
|
||||
|
||||
connection.send({
|
||||
message: robot.pubKey,
|
||||
message: garage.getSlot().robot.pubKey,
|
||||
nick: userNick,
|
||||
});
|
||||
|
||||
@ -132,10 +130,10 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
const createJsonFile: () => object = () => {
|
||||
return {
|
||||
credentials: {
|
||||
own_public_key: robot.pubKey,
|
||||
own_public_key: garage.getSlot().robot.pubKey,
|
||||
peer_public_key: peerPubKey,
|
||||
encrypted_private_key: robot.encPrivKey,
|
||||
passphrase: robot.token,
|
||||
encrypted_private_key: garage.getSlot().robot.encPrivKey,
|
||||
passphrase: garage.getSlot().robot.token,
|
||||
},
|
||||
messages,
|
||||
};
|
||||
@ -143,7 +141,7 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
|
||||
const onMessage: (message: any) => void = (message) => {
|
||||
const dataFromServer = JSON.parse(message.data);
|
||||
|
||||
const robot = garage.getSlot().robot;
|
||||
if (dataFromServer != null && !receivedIndexes.includes(dataFromServer.index)) {
|
||||
setReceivedIndexes((prev) => [...prev, dataFromServer.index]);
|
||||
setPeerConnected(dataFromServer.peer_connected);
|
||||
@ -213,6 +211,7 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const onButtonClicked = (e: React.FormEvent<HTMLFormElement>): void => {
|
||||
const robot = garage.getSlot().robot;
|
||||
if (robot.token !== undefined && value.includes(robot.token)) {
|
||||
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.`,
|
||||
@ -264,10 +263,10 @@ const EncryptedSocketChat: React.FC<Props> = ({
|
||||
}}
|
||||
orderId={Number(orderId)}
|
||||
messages={messages}
|
||||
ownPubKey={robot.pubKey ?? ''}
|
||||
ownEncPrivKey={robot.encPrivKey ?? ''}
|
||||
ownPubKey={garage.getSlot().robot.pubKey ?? ''}
|
||||
ownEncPrivKey={garage.getSlot().robot.encPrivKey ?? ''}
|
||||
peerPubKey={peerPubKey ?? 'Not received yet'}
|
||||
passphrase={robot.token ?? ''}
|
||||
passphrase={garage.getSlot().robot.token ?? ''}
|
||||
onClickBack={() => {
|
||||
setAudit(false);
|
||||
}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, TextField, Grid, Paper, Typography } from '@mui/material';
|
||||
import { encryptMessage, decryptMessage } from '../../../../pgp';
|
||||
@ -14,10 +14,12 @@ import ChatHeader from '../ChatHeader';
|
||||
import { type EncryptedChatMessage, type ServerMessage } from '..';
|
||||
import { apiClient } from '../../../../services/api';
|
||||
import ChatBottom from '../ChatBottom';
|
||||
import { UseAppStoreType, AppContext } from '../../../../contexts/AppContext';
|
||||
import { UseFederationStoreType, FederationContext } from '../../../../contexts/FederationContext';
|
||||
import { UseGarageStoreType, GarageContext } from '../../../../contexts/GarageContext';
|
||||
|
||||
interface Props {
|
||||
orderId: number;
|
||||
robot: Robot;
|
||||
userNick: string;
|
||||
takerNick: string;
|
||||
chatOffset: number;
|
||||
@ -35,7 +37,6 @@ const audioPath =
|
||||
|
||||
const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
orderId,
|
||||
robot,
|
||||
userNick,
|
||||
takerNick,
|
||||
chatOffset,
|
||||
@ -48,7 +49,8 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { origin, hostUrl, settings } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, focusedCoordinator } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
|
||||
const [audio] = useState(() => new Audio(`${audioPath}/chat-open.mp3`));
|
||||
const [peerConnected, setPeerConnected] = useState<boolean>(false);
|
||||
@ -83,11 +85,11 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
|
||||
const loadMessages: () => void = () => {
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(focusedCoordinator)
|
||||
.getCoordinator(garage.getSlot().activeOrderShortAlias)
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.get(url + basePath, `/api/chat/?order_id=${orderId}&offset=${lastIndex}`, {
|
||||
tokenSHA256: robot.tokenSHA256,
|
||||
tokenSHA256: garage.getSlot().robot.tokenSHA256,
|
||||
})
|
||||
.then((results: any) => {
|
||||
if (results != null) {
|
||||
@ -104,16 +106,17 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
const createJsonFile = (): object => {
|
||||
return {
|
||||
credentials: {
|
||||
own_public_key: robot.pubKey,
|
||||
own_public_key: garage.getSlot().robot.pubKey,
|
||||
peer_public_key: peerPubKey,
|
||||
encrypted_private_key: robot.encPrivKey,
|
||||
passphrase: robot.token,
|
||||
encrypted_private_key: garage.getSlot().robot.encPrivKey,
|
||||
passphrase: garage.getSlot().robot.token,
|
||||
},
|
||||
messages,
|
||||
};
|
||||
};
|
||||
|
||||
const onMessage = (dataFromServer: ServerMessage): void => {
|
||||
const robot = garage.getSlot().robot;
|
||||
if (dataFromServer != null) {
|
||||
// If we receive an encrypted message
|
||||
if (dataFromServer.message.substring(0, 27) === `-----BEGIN PGP MESSAGE-----`) {
|
||||
@ -169,6 +172,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const onButtonClicked = (e: React.FormEvent<HTMLFormElement>): void => {
|
||||
const robot = garage.getSlot().robot;
|
||||
if (robot.token !== undefined && value.includes(robot.token)) {
|
||||
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.`,
|
||||
@ -178,7 +182,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
// If input string contains '#' send unencrypted and unlogged message
|
||||
else if (value.substring(0, 1) === '#') {
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(focusedCoordinator)
|
||||
.getCoordinator(garage.getSlot().activeOrderShortAlias ?? '')
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(
|
||||
@ -211,7 +215,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
encryptMessage(value, robot.pubKey, peerPubKey, robot.encPrivKey, robot.token)
|
||||
.then((encryptedMessage) => {
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(focusedCoordinator)
|
||||
.getCoordinator(garage.getSlot().activeOrderShortAlias ?? '')
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(
|
||||
@ -259,10 +263,10 @@ const EncryptedTurtleChat: React.FC<Props> = ({
|
||||
}}
|
||||
orderId={Number(orderId)}
|
||||
messages={messages}
|
||||
ownPubKey={robot.pubKey ?? ''}
|
||||
ownEncPrivKey={robot.encPrivKey ?? ''}
|
||||
ownPubKey={garage.getSlot().robot.pubKey ?? ''}
|
||||
ownEncPrivKey={garage.getSlot().robot.encPrivKey ?? ''}
|
||||
peerPubKey={peerPubKey ?? 'Not received yet'}
|
||||
passphrase={robot.token ?? ''}
|
||||
passphrase={garage.getSlot().robot.token ?? ''}
|
||||
onClickBack={() => {
|
||||
setAudit(false);
|
||||
}}
|
||||
|
@ -35,7 +35,6 @@ export interface ServerMessage {
|
||||
const EncryptedChat: React.FC<Props> = ({
|
||||
orderId,
|
||||
takerNick,
|
||||
robot,
|
||||
userNick,
|
||||
chatOffset,
|
||||
baseUrl,
|
||||
@ -48,7 +47,6 @@ const EncryptedChat: React.FC<Props> = ({
|
||||
return turtleMode ? (
|
||||
<EncryptedTurtleChat
|
||||
messages={messages}
|
||||
robot={robot}
|
||||
setMessages={setMessages}
|
||||
orderId={orderId}
|
||||
takerNick={takerNick}
|
||||
@ -62,7 +60,6 @@ const EncryptedChat: React.FC<Props> = ({
|
||||
<EncryptedSocketChat
|
||||
status={status}
|
||||
messages={messages}
|
||||
robot={robot}
|
||||
setMessages={setMessages}
|
||||
orderId={orderId}
|
||||
takerNick={takerNick}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Grid, Typography, Tooltip, Collapse } from '@mui/material';
|
||||
import currencies from '../../../../static/assets/currencies.json';
|
||||
@ -8,10 +8,10 @@ import { pn } from '../../../utils';
|
||||
import EncryptedChat, { type EncryptedChatMessage } from '../EncryptedChat';
|
||||
import Countdown, { zeroPad } from 'react-countdown';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { UseGarageStoreType, GarageContext } from '../../../contexts/GarageContext';
|
||||
|
||||
interface ChatPromptProps {
|
||||
order: Order;
|
||||
robot: Robot;
|
||||
onClickConfirmSent: () => void;
|
||||
onClickUndoConfirmSent: () => void;
|
||||
loadingSent: boolean;
|
||||
@ -27,7 +27,6 @@ interface ChatPromptProps {
|
||||
|
||||
export const ChatPrompt = ({
|
||||
order,
|
||||
robot,
|
||||
onClickConfirmSent,
|
||||
onClickUndoConfirmSent,
|
||||
onClickConfirmReceived,
|
||||
@ -41,6 +40,7 @@ export const ChatPrompt = ({
|
||||
setMessages,
|
||||
}: ChatPromptProps): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
|
||||
const [sentButton, setSentButton] = useState<boolean>(false);
|
||||
const [receivedButton, setReceivedButton] = useState<boolean>(false);
|
||||
@ -49,9 +49,9 @@ export const ChatPrompt = ({
|
||||
const [enableDisputeTime, setEnableDisputeTime] = useState<Date>(new Date(order.expires_at));
|
||||
const [text, setText] = useState<string>('');
|
||||
|
||||
const currencyCode: string = currencies[`${order.currency}`];
|
||||
const currencyCode: string = currencies[`${garage.getSlot().order.currency}`];
|
||||
const amount: string = pn(
|
||||
parseFloat(parseFloat(order.amount).toFixed(order.currency === 1000 ? 8 : 4)),
|
||||
parseFloat(parseFloat(garage.getSlot().order.amount).toFixed(order.currency === 1000 ? 8 : 4)),
|
||||
);
|
||||
|
||||
const disputeCountdownRenderer = function ({
|
||||
@ -133,7 +133,6 @@ export const ChatPrompt = ({
|
||||
<Grid item>
|
||||
<EncryptedChat
|
||||
status={order.status}
|
||||
robot={robot}
|
||||
chatOffset={order.chat_last_index}
|
||||
orderId={order.id}
|
||||
takerNick={order.taker_nick}
|
||||
|
@ -45,14 +45,15 @@ import {
|
||||
defaultDispute,
|
||||
} from './Forms';
|
||||
|
||||
import { type Order, type Robot, type Settings } from '../../models';
|
||||
import { type Order } from '../../models';
|
||||
import { type EncryptedChatMessage } from './EncryptedChat';
|
||||
import CollabCancelAlert from './CollabCancelAlert';
|
||||
import { Bolt } from '@mui/icons-material';
|
||||
import { signCleartextMessage } from '../../pgp';
|
||||
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||
import { type UseGarageStoreType, GarageContext } from '../../contexts/GarageContext';
|
||||
import { type UseAppStoreType, AppContext } from '../../contexts/AppContext';
|
||||
import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface loadingButtonsProps {
|
||||
cancel: boolean;
|
||||
@ -101,27 +102,24 @@ const closeAll: OpenDialogProps = {
|
||||
};
|
||||
|
||||
interface TradeBoxProps {
|
||||
robot: Robot;
|
||||
currentOrder: Order;
|
||||
setBadOrder: (state: string | undefined) => void;
|
||||
onRenewOrder: () => void;
|
||||
onStartAgain: () => void;
|
||||
settings: Settings;
|
||||
baseUrl: string;
|
||||
onStartAgain: () => void;
|
||||
}
|
||||
|
||||
const TradeBox = ({
|
||||
robot,
|
||||
currentOrder,
|
||||
settings,
|
||||
baseUrl,
|
||||
setBadOrder,
|
||||
onRenewOrder,
|
||||
onStartAgain,
|
||||
}: TradeBoxProps): JSX.Element => {
|
||||
interface Contract {
|
||||
title: string;
|
||||
titleVariables: object;
|
||||
titleColor: string;
|
||||
prompt: () => JSX.Element;
|
||||
bondStatus: 'hide' | 'locked' | 'unlocked' | 'settled';
|
||||
titleIcon: () => JSX.Element;
|
||||
}
|
||||
|
||||
const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
const { garage, orderUpdatedAt, setBadOrder } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { settings, hostUrl, origin } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { garage, orderUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const { origin, hostUrl } = useContext<UseAppStoreType>(AppContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Buttons and Dialogs
|
||||
const [loadingButtons, setLoadingButtons] = useState<loadingButtonsProps>(noLoadingButtons);
|
||||
@ -156,6 +154,46 @@ const TradeBox = ({
|
||||
rating?: number;
|
||||
}
|
||||
|
||||
const renewOrder = function (): void {
|
||||
const currentOrder = garage.getSlot().order;
|
||||
if (currentOrder !== null) {
|
||||
const body = {
|
||||
type: currentOrder.type,
|
||||
currency: currentOrder.currency,
|
||||
amount: currentOrder.has_range ? null : currentOrder.amount,
|
||||
has_range: currentOrder.has_range,
|
||||
min_amount: currentOrder.min_amount,
|
||||
max_amount: currentOrder.max_amount,
|
||||
payment_method: currentOrder.payment_method,
|
||||
is_explicit: currentOrder.is_explicit,
|
||||
premium: currentOrder.is_explicit ? null : currentOrder.premium,
|
||||
satoshis: currentOrder.is_explicit ? currentOrder.satoshis : null,
|
||||
public_duration: currentOrder.public_duration,
|
||||
escrow_duration: currentOrder.escrow_duration,
|
||||
bond_size: currentOrder.bond_size,
|
||||
latitude: currentOrder.latitude,
|
||||
longitude: currentOrder.longitude,
|
||||
};
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(currentOrder.shortAlias)
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
apiClient
|
||||
.post(url + basePath, '/api/make/', body, {
|
||||
tokenSHA256: garage.getSlot().robot.tokenSHA256,
|
||||
})
|
||||
.then((data: any) => {
|
||||
if (data.bad_request !== undefined) {
|
||||
setBadOrder(data.bad_request);
|
||||
} else if (data.id !== undefined) {
|
||||
navigate(`/order/${String(currentOrder?.shortAlias)}/${String(data.id)}`);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setBadOrder('Request error');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const submitAction = function ({
|
||||
action,
|
||||
invoice,
|
||||
@ -165,13 +203,13 @@ const TradeBox = ({
|
||||
statement,
|
||||
rating,
|
||||
}: SubmitActionProps): void {
|
||||
const { url, basePath } = federation
|
||||
.getCoordinator(currentOrder.shortAlias)
|
||||
.getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl);
|
||||
const robot = garage.getSlot().robot;
|
||||
const currentOrder = garage.getSlot().order;
|
||||
|
||||
void apiClient
|
||||
.post(
|
||||
url + basePath,
|
||||
`/api/order/?order_id=${Number(currentOrder.id)}`,
|
||||
baseUrl,
|
||||
`/api/order/?order_id=${Number(currentOrder?.id)}`,
|
||||
{
|
||||
action,
|
||||
invoice,
|
||||
@ -268,6 +306,7 @@ const TradeBox = ({
|
||||
};
|
||||
|
||||
const submitStatement = function (): void {
|
||||
const robot = garage.getSlot().robot;
|
||||
let statement = dispute.statement;
|
||||
if (dispute.attachLogs) {
|
||||
const payload = { statement, messages, token: robot.token };
|
||||
@ -322,44 +361,48 @@ const TradeBox = ({
|
||||
|
||||
// Effect on Order Status change (used for WebLN)
|
||||
useEffect(() => {
|
||||
const currentOrder = garage.getSlot().order;
|
||||
if (currentOrder !== null && currentOrder.status !== lastOrderStatus) {
|
||||
setLastOrderStatus(currentOrder.status);
|
||||
void handleWebln(currentOrder);
|
||||
}
|
||||
// FIXME this should trigger with current order, not garage order
|
||||
}, [orderUpdatedAt]);
|
||||
|
||||
const statusToContract = function (order: Order): JSX.Element {
|
||||
const statusToContract = function (): Contract {
|
||||
const order = garage.getSlot().order;
|
||||
|
||||
const baseContract: Contract = {
|
||||
title: 'Unknown Order Status',
|
||||
titleVariables: {},
|
||||
titleColor: 'primary',
|
||||
prompt: () => <span>Wops!</span>,
|
||||
bondStatus: 'hide',
|
||||
titleIcon: () => <></>,
|
||||
};
|
||||
|
||||
if (order === null) return baseContract;
|
||||
|
||||
const status = order.status;
|
||||
const isBuyer = order.is_buyer;
|
||||
const isMaker = order.is_maker;
|
||||
|
||||
let title: string = 'Unknown Order Status';
|
||||
let titleVariables: object = {};
|
||||
let titleColor: string = 'primary';
|
||||
let titleIcon: () => JSX.Element = function () {
|
||||
return <></>;
|
||||
};
|
||||
let prompt = (): JSX.Element => <span>Wops!</span>;
|
||||
let bondStatus: 'hide' | 'locked' | 'unlocked' | 'settled' = 'hide';
|
||||
|
||||
switch (status) {
|
||||
// 0: 'Waiting for maker bond'
|
||||
case 0:
|
||||
if (isMaker) {
|
||||
title = 'Lock {{amountSats}} Sats to PUBLISH order';
|
||||
titleVariables = { amountSats: pn(order.bond_satoshis) };
|
||||
prompt = () => {
|
||||
baseContract.title = 'Lock {{amountSats}} Sats to PUBLISH order';
|
||||
baseContract.titleVariables = { amountSats: pn(order.bond_satoshis) };
|
||||
baseContract.prompt = () => {
|
||||
return <LockInvoicePrompt order={order} concept={'bond'} />;
|
||||
};
|
||||
bondStatus = 'hide';
|
||||
baseContract.bondStatus = 'hide';
|
||||
}
|
||||
break;
|
||||
// 1: 'Public'
|
||||
case 1:
|
||||
if (isMaker) {
|
||||
title = 'Your order is public';
|
||||
prompt = () => {
|
||||
baseContract.title = 'Your order is public';
|
||||
baseContract.prompt = () => {
|
||||
return (
|
||||
<PublicWaitPrompt
|
||||
order={order}
|
||||
@ -368,14 +411,14 @@ const TradeBox = ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
}
|
||||
break;
|
||||
// 2: 'Paused'
|
||||
case 2:
|
||||
if (isMaker) {
|
||||
title = 'Your order is paused';
|
||||
prompt = () => {
|
||||
baseContract.title = 'Your order is paused';
|
||||
baseContract.prompt = () => {
|
||||
return (
|
||||
<PausedPrompt
|
||||
pauseLoading={loadingButtons.pauseOrder}
|
||||
@ -383,53 +426,53 @@ const TradeBox = ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
}
|
||||
break;
|
||||
|
||||
// 3: 'Waiting for taker bond'
|
||||
case 3:
|
||||
if (isMaker) {
|
||||
title = 'A taker has been found!';
|
||||
prompt = () => {
|
||||
baseContract.title = 'A taker has been found!';
|
||||
baseContract.prompt = () => {
|
||||
return <TakerFoundPrompt />;
|
||||
};
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
} else {
|
||||
title = 'Lock {{amountSats}} Sats to TAKE order';
|
||||
titleVariables = { amountSats: pn(order.bond_satoshis) };
|
||||
prompt = () => {
|
||||
baseContract.title = 'Lock {{amountSats}} Sats to TAKE order';
|
||||
baseContract.titleVariables = { amountSats: pn(order.bond_satoshis) };
|
||||
baseContract.prompt = () => {
|
||||
return <LockInvoicePrompt order={order} concept={'bond'} />;
|
||||
};
|
||||
bondStatus = 'hide';
|
||||
baseContract.bondStatus = 'hide';
|
||||
}
|
||||
break;
|
||||
|
||||
// 5: 'Expired'
|
||||
case 5:
|
||||
title = 'The order has expired';
|
||||
prompt = () => {
|
||||
baseContract.title = 'The order has expired';
|
||||
baseContract.prompt = () => {
|
||||
return (
|
||||
<ExpiredPrompt
|
||||
loadingRenew={loadingButtons.renewOrder}
|
||||
order={order}
|
||||
onClickRenew={() => {
|
||||
onRenewOrder();
|
||||
renewOrder();
|
||||
setLoadingButtons({ ...noLoadingButtons, renewOrder: true });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
bondStatus = 'hide'; // To do: show bond status according to expiry message.
|
||||
baseContract.bondStatus = 'hide'; // To do: show bond status according to expiry message.
|
||||
break;
|
||||
|
||||
// 6: 'Waiting for trade collateral and buyer invoice'
|
||||
case 6:
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
if (isBuyer) {
|
||||
title = 'Submit payout info';
|
||||
titleVariables = { amountSats: pn(order.invoice_amount) };
|
||||
prompt = function () {
|
||||
baseContract.title = 'Submit payout info';
|
||||
baseContract.titleVariables = { amountSats: pn(order.invoice_amount) };
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<PayoutPrompt
|
||||
order={order}
|
||||
@ -446,10 +489,10 @@ const TradeBox = ({
|
||||
);
|
||||
};
|
||||
} else {
|
||||
title = 'Lock {{amountSats}} Sats as collateral';
|
||||
titleVariables = { amountSats: pn(order.escrow_satoshis) };
|
||||
titleColor = 'warning';
|
||||
prompt = () => {
|
||||
baseContract.title = 'Lock {{amountSats}} Sats as collateral';
|
||||
baseContract.titleVariables = { amountSats: pn(order.escrow_satoshis) };
|
||||
baseContract.titleColor = 'warning';
|
||||
baseContract.prompt = () => {
|
||||
return <LockInvoicePrompt order={order} concept={'escrow'} />;
|
||||
};
|
||||
}
|
||||
@ -457,17 +500,17 @@ const TradeBox = ({
|
||||
|
||||
// 7: 'Waiting only for seller trade collateral'
|
||||
case 7:
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
if (isBuyer) {
|
||||
title = 'Your info looks good!';
|
||||
prompt = () => {
|
||||
baseContract.title = 'Your info looks good!';
|
||||
baseContract.prompt = () => {
|
||||
return <EscrowWaitPrompt />;
|
||||
};
|
||||
} else {
|
||||
title = 'Lock {{amountSats}} Sats as collateral';
|
||||
titleVariables = { amountSats: pn(order.escrow_satoshis) };
|
||||
titleColor = 'warning';
|
||||
prompt = () => {
|
||||
baseContract.title = 'Lock {{amountSats}} Sats as collateral';
|
||||
baseContract.titleVariables = { amountSats: pn(order.escrow_satoshis) };
|
||||
baseContract.titleColor = 'warning';
|
||||
baseContract.prompt = () => {
|
||||
return <LockInvoicePrompt order={order} concept={'escrow'} />;
|
||||
};
|
||||
}
|
||||
@ -475,11 +518,11 @@ const TradeBox = ({
|
||||
|
||||
// 8: 'Waiting only for buyer invoice'
|
||||
case 8:
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
if (isBuyer) {
|
||||
title = 'Submit payout info';
|
||||
titleVariables = { amountSats: pn(order.invoice_amount) };
|
||||
prompt = () => {
|
||||
baseContract.title = 'Submit payout info';
|
||||
baseContract.titleVariables = { amountSats: pn(order.invoice_amount) };
|
||||
baseContract.prompt = () => {
|
||||
return (
|
||||
<PayoutPrompt
|
||||
order={order}
|
||||
@ -496,8 +539,8 @@ const TradeBox = ({
|
||||
);
|
||||
};
|
||||
} else {
|
||||
title = 'The trade collateral is locked!';
|
||||
prompt = () => {
|
||||
baseContract.title = 'The trade collateral is locked!';
|
||||
baseContract.prompt = () => {
|
||||
return <PayoutWaitPrompt />;
|
||||
};
|
||||
}
|
||||
@ -507,12 +550,11 @@ const TradeBox = ({
|
||||
// 10: 'Fiat sent - In chatroom'
|
||||
case 9:
|
||||
case 10:
|
||||
title = isBuyer ? 'Chat with the seller' : 'Chat with the buyer';
|
||||
prompt = function () {
|
||||
baseContract.title = isBuyer ? 'Chat with the seller' : 'Chat with the buyer';
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<ChatPrompt
|
||||
order={order}
|
||||
robot={robot}
|
||||
onClickConfirmSent={() => {
|
||||
setOpen({ ...open, confirmFiatSent: true });
|
||||
}}
|
||||
@ -535,20 +577,20 @@ const TradeBox = ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
bondStatus = 'locked';
|
||||
baseContract.bondStatus = 'locked';
|
||||
break;
|
||||
|
||||
// 11: 'In dispute'
|
||||
case 11:
|
||||
bondStatus = 'settled';
|
||||
baseContract.bondStatus = 'settled';
|
||||
if (order.statement_submitted) {
|
||||
title = 'We have received your statement';
|
||||
prompt = function () {
|
||||
baseContract.title = 'We have received your statement';
|
||||
baseContract.prompt = function () {
|
||||
return <DisputeWaitPeerPrompt />;
|
||||
};
|
||||
} else {
|
||||
title = 'A dispute has been opened';
|
||||
prompt = function () {
|
||||
baseContract.title = 'A dispute has been opened';
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<DisputePrompt
|
||||
loading={loadingButtons.submitStatement}
|
||||
@ -567,18 +609,18 @@ const TradeBox = ({
|
||||
// 13: 'Sending satoshis to buyer'
|
||||
case 13:
|
||||
if (isBuyer) {
|
||||
bondStatus = 'unlocked';
|
||||
title = 'Attempting Lightning Payment';
|
||||
prompt = function () {
|
||||
baseContract.bondStatus = 'unlocked';
|
||||
baseContract.title = 'Attempting Lightning Payment';
|
||||
baseContract.prompt = function () {
|
||||
return <SendingSatsPrompt />;
|
||||
};
|
||||
} else {
|
||||
title = 'Trade finished!';
|
||||
titleColor = 'success';
|
||||
titleIcon = function () {
|
||||
baseContract.title = 'Trade finished!';
|
||||
baseContract.titleColor = 'success';
|
||||
baseContract.titleIcon = function () {
|
||||
return <Bolt xs={{ width: '1em', height: '1em' }} color='warning' />;
|
||||
};
|
||||
prompt = function () {
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<SuccessfulPrompt
|
||||
baseUrl={baseUrl}
|
||||
@ -587,7 +629,7 @@ const TradeBox = ({
|
||||
onClickStartAgain={onStartAgain}
|
||||
loadingRenew={loadingButtons.renewOrder}
|
||||
onClickRenew={() => {
|
||||
onRenewOrder();
|
||||
renewOrder();
|
||||
setLoadingButtons({ ...noLoadingButtons, renewOrder: true });
|
||||
}}
|
||||
/>
|
||||
@ -598,12 +640,12 @@ const TradeBox = ({
|
||||
|
||||
// 14: 'Sucessful trade'
|
||||
case 14:
|
||||
title = 'Trade finished!';
|
||||
titleColor = 'success';
|
||||
titleIcon = function () {
|
||||
baseContract.title = 'Trade finished!';
|
||||
baseContract.titleColor = 'success';
|
||||
baseContract.titleIcon = function () {
|
||||
return <Bolt xs={{ width: '1em', height: '1em' }} color='warning' />;
|
||||
};
|
||||
prompt = function () {
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<SuccessfulPrompt
|
||||
baseUrl={baseUrl}
|
||||
@ -612,7 +654,7 @@ const TradeBox = ({
|
||||
onClickStartAgain={onStartAgain}
|
||||
loadingRenew={loadingButtons.renewOrder}
|
||||
onClickRenew={() => {
|
||||
onRenewOrder();
|
||||
renewOrder();
|
||||
setLoadingButtons({ ...noLoadingButtons, renewOrder: true });
|
||||
}}
|
||||
/>
|
||||
@ -623,9 +665,9 @@ const TradeBox = ({
|
||||
// 15: 'Failed lightning network routing'
|
||||
case 15:
|
||||
if (isBuyer) {
|
||||
bondStatus = 'unlocked';
|
||||
title = 'Lightning Routing Failed';
|
||||
prompt = function () {
|
||||
baseContract.bondStatus = 'unlocked';
|
||||
baseContract.title = 'Lightning Routing Failed';
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<RoutingFailedPrompt
|
||||
order={order}
|
||||
@ -638,12 +680,12 @@ const TradeBox = ({
|
||||
);
|
||||
};
|
||||
} else {
|
||||
title = 'Trade finished!';
|
||||
titleColor = 'success';
|
||||
titleIcon = function () {
|
||||
baseContract.title = 'Trade finished!';
|
||||
baseContract.titleColor = 'success';
|
||||
baseContract.titleIcon = function () {
|
||||
return <Bolt xs={{ width: '1em', height: '1em' }} color='warning' />;
|
||||
};
|
||||
prompt = function () {
|
||||
baseContract.prompt = function () {
|
||||
return (
|
||||
<SuccessfulPrompt
|
||||
baseUrl={baseUrl}
|
||||
@ -652,7 +694,7 @@ const TradeBox = ({
|
||||
onClickStartAgain={onStartAgain}
|
||||
loadingRenew={loadingButtons.renewOrder}
|
||||
onClickRenew={() => {
|
||||
onRenewOrder();
|
||||
renewOrder();
|
||||
setLoadingButtons({ ...noLoadingButtons, renewOrder: true });
|
||||
}}
|
||||
/>
|
||||
@ -663,9 +705,9 @@ const TradeBox = ({
|
||||
|
||||
// 16: 'Wait for dispute resolution'
|
||||
case 16:
|
||||
bondStatus = 'settled';
|
||||
title = 'We have the statements';
|
||||
prompt = function () {
|
||||
baseContract.bondStatus = 'settled';
|
||||
baseContract.title = 'We have the statements';
|
||||
baseContract.prompt = function () {
|
||||
return <DisputeWaitResolutionPrompt />;
|
||||
};
|
||||
break;
|
||||
@ -675,14 +717,14 @@ const TradeBox = ({
|
||||
case 17:
|
||||
case 18:
|
||||
if ((status === 17 && isMaker) || (status === 18 && !isMaker)) {
|
||||
title = 'You have lost the dispute';
|
||||
prompt = function () {
|
||||
baseContract.title = 'You have lost the dispute';
|
||||
baseContract.prompt = function () {
|
||||
return <DisputeLoserPrompt />;
|
||||
};
|
||||
bondStatus = 'settled';
|
||||
baseContract.bondStatus = 'settled';
|
||||
} else if ((status === 17 && !isMaker) || (status === 18 && isMaker)) {
|
||||
title = 'You have won the dispute';
|
||||
prompt = function () {
|
||||
baseContract.title = 'You have won the dispute';
|
||||
baseContract.prompt = function () {
|
||||
return <DisputeWinnerPrompt />;
|
||||
};
|
||||
}
|
||||
@ -692,10 +734,10 @@ const TradeBox = ({
|
||||
break;
|
||||
}
|
||||
|
||||
return { title, titleVariables, titleColor, prompt, bondStatus, titleIcon };
|
||||
return baseContract;
|
||||
};
|
||||
|
||||
const contract = currentOrder != null ? statusToContract(currentOrder) : null;
|
||||
const contract = statusToContract();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@ -705,7 +747,7 @@ const TradeBox = ({
|
||||
setOpen(closeAll);
|
||||
}}
|
||||
waitingWebln={waitingWebln}
|
||||
isBuyer={currentOrder?.is_buyer ?? false}
|
||||
isBuyer={garage.getSlot().order?.is_buyer ?? false}
|
||||
/>
|
||||
<ConfirmDisputeDialog
|
||||
open={open.confirmDispute}
|
||||
@ -728,11 +770,11 @@ const TradeBox = ({
|
||||
}}
|
||||
onCollabCancelClick={cancel}
|
||||
loading={loadingButtons.cancel}
|
||||
peerAskedCancel={currentOrder?.pending_cancel ?? false}
|
||||
peerAskedCancel={garage.getSlot().order?.pending_cancel ?? false}
|
||||
/>
|
||||
<ConfirmFiatSentDialog
|
||||
open={open.confirmFiatSent}
|
||||
order={currentOrder}
|
||||
order={garage.getSlot().order}
|
||||
loadingButton={loadingButtons.fiatSent}
|
||||
onClose={() => {
|
||||
setOpen(closeAll);
|
||||
@ -749,14 +791,14 @@ const TradeBox = ({
|
||||
/>
|
||||
<ConfirmFiatReceivedDialog
|
||||
open={open.confirmFiatReceived}
|
||||
order={currentOrder}
|
||||
order={garage.getSlot().order}
|
||||
loadingButton={loadingButtons.fiatReceived}
|
||||
onClose={() => {
|
||||
setOpen(closeAll);
|
||||
}}
|
||||
onConfirmClick={confirmFiatReceived}
|
||||
/>
|
||||
<CollabCancelAlert order={currentOrder} />
|
||||
<CollabCancelAlert order={garage.getSlot().order} />
|
||||
<Grid
|
||||
container
|
||||
padding={1}
|
||||
@ -767,7 +809,7 @@ const TradeBox = ({
|
||||
>
|
||||
<Grid item>
|
||||
<Title
|
||||
order={currentOrder}
|
||||
order={garage.getSlot().order}
|
||||
text={contract?.title}
|
||||
color={contract?.titleColor}
|
||||
icon={contract?.titleIcon}
|
||||
@ -781,7 +823,10 @@ const TradeBox = ({
|
||||
{contract?.bondStatus !== 'hide' ? (
|
||||
<Grid item sx={{ width: '100%' }}>
|
||||
<Divider />
|
||||
<BondStatus status={contract?.bondStatus} isMaker={currentOrder?.is_maker ?? false} />
|
||||
<BondStatus
|
||||
status={contract?.bondStatus}
|
||||
isMaker={garage.getSlot().order?.is_maker ?? false}
|
||||
/>
|
||||
</Grid>
|
||||
) : (
|
||||
<></>
|
||||
@ -789,7 +834,7 @@ const TradeBox = ({
|
||||
|
||||
<Grid item>
|
||||
<CancelButton
|
||||
order={currentOrder}
|
||||
order={garage.getSlot().order}
|
||||
onClickCancel={cancel}
|
||||
openCancelDialog={() => {
|
||||
setOpen({ ...closeAll, confirmCancel: true });
|
||||
|
@ -51,8 +51,6 @@ export interface fetchRobotProps {
|
||||
export interface UseFederationStoreType {
|
||||
federation: Federation;
|
||||
sortedCoordinators: string[];
|
||||
focusedCoordinator: string | null;
|
||||
setFocusedCoordinator: Dispatch<SetStateAction<string>>;
|
||||
setDelay: Dispatch<SetStateAction<number>>;
|
||||
coordinatorUpdatedAt: string;
|
||||
federationUpdatedAt: string;
|
||||
@ -61,8 +59,6 @@ export interface UseFederationStoreType {
|
||||
export const initialFederationContext: UseFederationStoreType = {
|
||||
federation: new Federation(),
|
||||
sortedCoordinators: [],
|
||||
focusedCoordinator: '',
|
||||
setFocusedCoordinator: () => {},
|
||||
setDelay: () => {},
|
||||
coordinatorUpdatedAt: '',
|
||||
federationUpdatedAt: '',
|
||||
@ -73,7 +69,7 @@ export const FederationContext = createContext<UseFederationStoreType>(initialFe
|
||||
export const useFederationStore = (): UseFederationStoreType => {
|
||||
const { settings, page, origin, hostUrl, open, torStatus } =
|
||||
useContext<UseAppStoreType>(AppContext);
|
||||
const { setMaker, garage, orderUpdatedAt, robotUpdatedAt, badOrder } =
|
||||
const { setMaker, garage, setBadOrder, robotUpdatedAt, badOrder, orderUpdatedAt } =
|
||||
useContext<UseGarageStoreType>(GarageContext);
|
||||
const [federation, setFederation] = useState(initialFederationContext.federation);
|
||||
const sortedCoordinators = useMemo(() => {
|
||||
@ -88,13 +84,10 @@ export const useFederationStore = (): UseFederationStoreType => {
|
||||
);
|
||||
const [federationUpdatedAt, setFederationUpdatedAt] = useState<string>(new Date().toISOString());
|
||||
|
||||
const [focusedCoordinator, setFocusedCoordinator] = useState<string>(sortedCoordinators[0]);
|
||||
|
||||
const [delay, setDelay] = useState<number>(60000);
|
||||
const [delay, setDelay] = useState<number>(5000);
|
||||
const [timer, setTimer] = useState<NodeJS.Timer | undefined>(() =>
|
||||
setInterval(() => null, delay),
|
||||
);
|
||||
const [currentOrder, setCurrentOrder] = useState<Order | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// On bitcoin network change we reset book, limits and federation info and fetch everything again
|
||||
@ -112,27 +105,34 @@ export const useFederationStore = (): UseFederationStoreType => {
|
||||
}, [settings.network, torStatus]);
|
||||
|
||||
const fetchCurrentOrder = (): void => {
|
||||
if (currentOrder?.id != null && (page === 'order' || badOrder === undefined)) {
|
||||
void federation.fetchOrder(currentOrder, garage);
|
||||
const activeSlot = garage.getSlot();
|
||||
if (activeSlot.activeOrderShortAlias !== null && activeSlot.activeOrderId !== null) {
|
||||
const coordinator = federation.getCoordinator(activeSlot.activeOrderShortAlias);
|
||||
coordinator
|
||||
.fetchOrder(activeSlot.activeOrderId, activeSlot.robot)
|
||||
.then((order) => {
|
||||
if (order?.bad_request !== undefined) {
|
||||
setBadOrder(order.bad_request);
|
||||
}
|
||||
if (order?.id !== null) {
|
||||
garage.updateOrder(order as Order);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setTimer(setTimeout(fetchCurrentOrder, delay));
|
||||
});
|
||||
} else {
|
||||
setTimer(setTimeout(fetchCurrentOrder, delay));
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch current order at load and in a loop
|
||||
useEffect(() => {
|
||||
fetchCurrentOrder();
|
||||
}, [currentOrder, page]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentOrder(garage.getOrder());
|
||||
}, [orderUpdatedAt]);
|
||||
|
||||
useEffect(() => {
|
||||
clearInterval(timer);
|
||||
setTimer(setInterval(fetchCurrentOrder, delay));
|
||||
fetchCurrentOrder();
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, [delay, currentOrder, page, badOrder]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const robot = garage.getSlot().robot;
|
||||
@ -154,8 +154,6 @@ export const useFederationStore = (): UseFederationStoreType => {
|
||||
return {
|
||||
federation,
|
||||
sortedCoordinators,
|
||||
focusedCoordinator,
|
||||
setFocusedCoordinator,
|
||||
setDelay,
|
||||
coordinatorUpdatedAt,
|
||||
federationUpdatedAt,
|
||||
|
@ -188,7 +188,7 @@ export class Coordinator {
|
||||
apiClient
|
||||
.get(this.url, `${this.basePath}/api/limits/`)
|
||||
.then((data) => {
|
||||
if (data != null) {
|
||||
if (data !== null) {
|
||||
const newLimits = data as LimitList;
|
||||
|
||||
for (const currency in this.limits) {
|
||||
@ -215,8 +215,10 @@ export class Coordinator {
|
||||
apiClient
|
||||
.get(this.url, `${this.basePath}/api/info/`)
|
||||
.then((data) => {
|
||||
this.info = data as Info;
|
||||
onDataLoad();
|
||||
if (data !== null) {
|
||||
this.info = data as Info;
|
||||
onDataLoad();
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
|
@ -96,20 +96,6 @@ export class Federation {
|
||||
});
|
||||
};
|
||||
|
||||
fetchOrder = async (order: Order, garage: Garage): Promise<Order | null> => {
|
||||
if (order.shortAlias !== null) {
|
||||
const coordinator = this.coordinators[order.shortAlias];
|
||||
if (coordinator != null && order.id !== null) {
|
||||
const newOrder = await coordinator.fetchOrder(order.id, garage.getSlot().robot);
|
||||
if (newOrder != null) {
|
||||
garage.updateOrder(newOrder);
|
||||
return newOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
return order;
|
||||
};
|
||||
|
||||
// Coordinators
|
||||
getCoordinator = (shortAlias: string): Coordinator => {
|
||||
return this.coordinators[shortAlias];
|
||||
|
@ -146,7 +146,17 @@ class Garage {
|
||||
|
||||
// Orders
|
||||
updateOrder: (order: Order, index?: number) => void = (order, index = this.currentSlot) => {
|
||||
this.slots[index].order = order;
|
||||
const updatedOrder = this.slots[index].order;
|
||||
if (updatedOrder !== null && updatedOrder.id === order.id) {
|
||||
Object.assign(updatedOrder, order);
|
||||
this.slots[index].order = updatedOrder;
|
||||
} else {
|
||||
this.slots[index].order = order;
|
||||
}
|
||||
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();
|
||||
};
|
||||
|
@ -106,6 +106,9 @@ export interface Order {
|
||||
network: 'mainnet' | 'testnet';
|
||||
shortAlias: string;
|
||||
bad_request?: string;
|
||||
bad_address?: string;
|
||||
bad_invoice?: string;
|
||||
bad_statement?: string;
|
||||
}
|
||||
|
||||
export const defaultOrder: Order = {
|
||||
|
@ -35,7 +35,6 @@ const FederationWidget = React.forwardRef(function Component(
|
||||
return (
|
||||
<Paper elevation={3} style={{ width: '100%', height: '100%' }}>
|
||||
<FederationTable
|
||||
openCoordinator={() => setOpen({ ...open, coordinator: true })}
|
||||
maxWidth={layout.w * gridCellSize} // EM units
|
||||
maxHeight={layout.h * gridCellSize} // EM units
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user