mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-31 02:21:35 +00:00
commit
ec1b4f2576
@ -478,6 +478,7 @@ class LNDNode:
|
||||
payment_request=lnpayment.invoice,
|
||||
fee_limit_sat=fee_limit_sat,
|
||||
timeout_seconds=timeout_seconds,
|
||||
amp=True,
|
||||
)
|
||||
|
||||
routerstub = router_pb2_grpc.RouterStub(cls.channel)
|
||||
@ -536,6 +537,7 @@ class LNDNode:
|
||||
fee_limit_sat=fee_limit_sat,
|
||||
timeout_seconds=timeout_seconds,
|
||||
allow_self_payment=True,
|
||||
amp=True,
|
||||
)
|
||||
|
||||
order = lnpayment.order_paid_LN
|
||||
|
@ -50,16 +50,16 @@ class Command(BaseCommand):
|
||||
parts = message.split(" ")
|
||||
if len(parts) < 2:
|
||||
self.notifications.send_telegram_message(
|
||||
chat_id=result["message"]["from"]["id"],
|
||||
text='You must enable the notifications bot using the RoboSats client. Click on your "Robot robot" -> "Enable Telegram" and follow the link or scan the QR code.',
|
||||
result["message"]["from"]["id"],
|
||||
'You must enable the notifications bot using the RoboSats client. Click on your "Robot robot" -> "Enable Telegram" and follow the link or scan the QR code.',
|
||||
)
|
||||
continue
|
||||
token = parts[-1]
|
||||
robot = Robot.objects.filter(telegram_token=token).first()
|
||||
if not robot:
|
||||
self.notifications.send_telegram_message(
|
||||
chat_id=result["message"]["from"]["id"],
|
||||
text=f'Wops, invalid token! There is no Robot with telegram chat token "{token}"',
|
||||
result["message"]["from"]["id"],
|
||||
f'Wops, invalid token! There is no Robot with telegram chat token "{token}"',
|
||||
)
|
||||
continue
|
||||
|
||||
|
18
api/migrations/0049_alter_currency_currency.py
Normal file
18
api/migrations/0049_alter_currency_currency.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-15 18:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0048_alter_order_reference'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='currency',
|
||||
name='currency',
|
||||
field=models.PositiveSmallIntegerField(choices=[(1, 'USD'), (2, 'EUR'), (3, 'JPY'), (4, 'GBP'), (5, 'AUD'), (6, 'CAD'), (7, 'CHF'), (8, 'CNY'), (9, 'HKD'), (10, 'NZD'), (11, 'SEK'), (12, 'KRW'), (13, 'SGD'), (14, 'NOK'), (15, 'MXN'), (16, 'BYN'), (17, 'RUB'), (18, 'ZAR'), (19, 'TRY'), (20, 'BRL'), (21, 'CLP'), (22, 'CZK'), (23, 'DKK'), (24, 'HRK'), (25, 'HUF'), (26, 'INR'), (27, 'ISK'), (28, 'PLN'), (29, 'RON'), (30, 'ARS'), (31, 'VES'), (32, 'COP'), (33, 'PEN'), (34, 'UYU'), (35, 'PYG'), (36, 'BOB'), (37, 'IDR'), (38, 'ANG'), (39, 'CRC'), (40, 'CUP'), (41, 'DOP'), (42, 'GHS'), (43, 'GTQ'), (44, 'ILS'), (45, 'JMD'), (46, 'KES'), (47, 'KZT'), (48, 'MYR'), (49, 'NAD'), (50, 'NGN'), (51, 'AZN'), (52, 'PAB'), (53, 'PHP'), (54, 'PKR'), (55, 'QAR'), (56, 'SAR'), (57, 'THB'), (58, 'TTD'), (59, 'VND'), (60, 'XOF'), (61, 'TWD'), (62, 'TZS'), (63, 'XAF'), (64, 'UAH'), (65, 'EGP'), (66, 'LKR'), (67, 'MAD'), (68, 'AED'), (69, 'TND'), (70, 'ETB'), (71, 'GEL'), (72, 'UGX'), (73, 'RSD'), (74, 'IRT'), (75, 'BDT'), (76, 'ALL'), (77, 'DZD'), (300, 'XAU'), (1000, 'BTC')], unique=True),
|
||||
),
|
||||
]
|
18
api/migrations/0050_alter_order_status.py
Normal file
18
api/migrations/0050_alter_order_status.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-22 08:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0049_alter_currency_currency'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='status',
|
||||
field=models.PositiveSmallIntegerField(choices=[(0, 'Waiting for maker bond'), (1, 'Public'), (2, 'Paused'), (3, 'Waiting for taker bond'), (4, 'Cancelled'), (5, 'Expired'), (6, 'Waiting for trade collateral and buyer invoice'), (7, 'Waiting only for seller trade collateral'), (8, 'Waiting only for buyer invoice'), (9, 'Sending fiat - In chatroom'), (10, 'Fiat sent - In chatroom'), (11, 'In dispute'), (12, 'Collaboratively cancelled'), (13, 'Sending satoshis to buyer'), (14, 'Successful trade'), (15, 'Failed lightning network routing'), (16, 'Wait for dispute resolution'), (17, 'Maker lost dispute'), (18, 'Taker lost dispute')], default=0),
|
||||
),
|
||||
]
|
@ -46,7 +46,7 @@ class Order(models.Model):
|
||||
DIS = 11, "In dispute"
|
||||
CCA = 12, "Collaboratively cancelled"
|
||||
PAY = 13, "Sending satoshis to buyer"
|
||||
SUC = 14, "Sucessful trade"
|
||||
SUC = 14, "Successful trade"
|
||||
FAI = 15, "Failed lightning network routing"
|
||||
WFR = 16, "Wait for dispute resolution"
|
||||
MLD = 17, "Maker lost dispute"
|
||||
|
@ -37,13 +37,13 @@ class Notifications:
|
||||
if robot.telegram_enabled:
|
||||
self.send_telegram_message(robot.telegram_chat_id, title, description)
|
||||
|
||||
def save_message(self, order, robot, title, description):
|
||||
def save_message(self, order, robot, title, description=""):
|
||||
"""Save a message for a user"""
|
||||
Notification.objects.create(
|
||||
title=title, description=description, robot=robot, order=order
|
||||
)
|
||||
|
||||
def send_telegram_message(self, chat_id, title, description):
|
||||
def send_telegram_message(self, chat_id, title, description=""):
|
||||
"""sends a message to a user with telegram notifications enabled"""
|
||||
|
||||
bot_token = config("TELEGRAM_TOKEN")
|
||||
|
@ -112,7 +112,7 @@ class OrderViewSchema:
|
||||
- `11` "In dispute"
|
||||
- `12` "Collaboratively cancelled"
|
||||
- `13` "Sending satoshis to buyer"
|
||||
- `14` "Sucessful trade"
|
||||
- `14` "Successful trade"
|
||||
- `15` "Failed lightning network routing"
|
||||
- `16` "Wait for dispute resolution"
|
||||
- `17` "Maker lost dispute"
|
||||
|
@ -141,7 +141,7 @@ def get_devfund_pubkey(network: str) -> str:
|
||||
"""
|
||||
|
||||
session = get_session()
|
||||
url = "https://raw.githubusercontent.com/RoboSats/robosats/main/devfund_pubey.json"
|
||||
url = "https://raw.githubusercontent.com/RoboSats/robosats/main/devfund_pubkey.json"
|
||||
|
||||
try:
|
||||
response = session.get(url)
|
||||
|
@ -365,7 +365,7 @@ paths:
|
||||
- `11` "In dispute"
|
||||
- `12` "Collaboratively cancelled"
|
||||
- `13` "Sending satoshis to buyer"
|
||||
- `14` "Sucessful trade"
|
||||
- `14` "Successful trade"
|
||||
- `15` "Failed lightning network routing"
|
||||
- `16` "Wait for dispute resolution"
|
||||
- `17` "Maker lost dispute"
|
||||
@ -1833,7 +1833,7 @@ components:
|
||||
* `11` - In dispute
|
||||
* `12` - Collaboratively cancelled
|
||||
* `13` - Sending satoshis to buyer
|
||||
* `14` - Sucessful trade
|
||||
* `14` - Successful trade
|
||||
* `15` - Failed lightning network routing
|
||||
* `16` - Wait for dispute resolution
|
||||
* `17` - Maker lost dispute
|
||||
|
24
frontend/package-lock.json
generated
24
frontend/package-lock.json
generated
@ -23,11 +23,11 @@
|
||||
"@nivo/core": "^0.86.0",
|
||||
"@nivo/line": "^0.86.0",
|
||||
"base-ex": "^0.8.1",
|
||||
"country-flag-icons": "^1.5.11",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^2.30.0",
|
||||
"file-replace-loader": "^1.4.0",
|
||||
"i18next": "^23.2.11",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.5.0",
|
||||
"install": "^0.13.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
@ -81,7 +81,7 @@
|
||||
"jest": "^29.6.1",
|
||||
"prettier": "^3.3.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.2",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
@ -6411,9 +6411,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/country-flag-icons": {
|
||||
"version": "1.5.11",
|
||||
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.11.tgz",
|
||||
"integrity": "sha512-B+mvFywunkRJs270k7kCBjhogvIA0uNn6GAXv6m2cPn3rrwqZzZVr2gBWcz+Cz7OGVWlcbERlYRIX0S6OGr8Bw=="
|
||||
"version": "1.5.13",
|
||||
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.13.tgz",
|
||||
"integrity": "sha512-4JwHNqaKZ19doQoNcBjsoYA+I7NqCH/mC/6f5cBWvdKzcK5TMmzLpq3Z/syVHMHJuDGFwJ+rPpGizvrqJybJow=="
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
@ -8823,9 +8823,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-browser-languagedetector": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz",
|
||||
"integrity": "sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
|
||||
"integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
@ -16730,9 +16730,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
|
||||
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
@ -43,7 +43,7 @@
|
||||
"jest": "^29.6.1",
|
||||
"prettier": "^3.3.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.2",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
@ -62,11 +62,11 @@
|
||||
"@nivo/core": "^0.86.0",
|
||||
"@nivo/line": "^0.86.0",
|
||||
"base-ex": "^0.8.1",
|
||||
"country-flag-icons": "^1.5.11",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^2.30.0",
|
||||
"file-replace-loader": "^1.4.0",
|
||||
"i18next": "^23.2.11",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.5.0",
|
||||
"install": "^0.13.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
|
@ -40,14 +40,14 @@ const OrderPage = (): JSX.Element => {
|
||||
const shortAlias = params.shortAlias;
|
||||
const coordinator = federation.getCoordinator(shortAlias ?? '');
|
||||
if (coordinator) {
|
||||
const { url, basePath } = coordinator?.getEndpoint(
|
||||
const endpoint = coordinator?.getEndpoint(
|
||||
settings.network,
|
||||
origin,
|
||||
settings.selfhostedClient,
|
||||
hostUrl,
|
||||
);
|
||||
|
||||
setBaseUrl(`${url}${basePath}`);
|
||||
if (endpoint) setBaseUrl(`${endpoint?.url}${endpoint?.basePath}`);
|
||||
|
||||
const orderId = Number(params.orderId);
|
||||
if (
|
||||
|
@ -2,38 +2,45 @@ import React, { useState, useContext, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
Grid,
|
||||
LinearProgress,
|
||||
Typography,
|
||||
Alert,
|
||||
LinearProgress,
|
||||
Select,
|
||||
MenuItem,
|
||||
Box,
|
||||
TextField,
|
||||
SelectChangeEvent,
|
||||
useTheme,
|
||||
Tooltip,
|
||||
type SelectChangeEvent,
|
||||
useMediaQuery,
|
||||
styled,
|
||||
} from '@mui/material';
|
||||
import { Bolt, Add, DeleteSweep, Logout, Download } from '@mui/icons-material';
|
||||
import { Bolt, Add, DeleteSweep, Logout, Download, FileCopy } from '@mui/icons-material';
|
||||
import RobotAvatar from '../../components/RobotAvatar';
|
||||
import TokenInput from './TokenInput';
|
||||
import { type Slot, type Robot } from '../../models';
|
||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||
import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
|
||||
import { genBase62Token } from '../../utils';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
|
||||
const BUTTON_COLORS = {
|
||||
primary: '#2196f3',
|
||||
secondary: '#9c27b0',
|
||||
text: '#ffffff',
|
||||
hoverPrimary: '#4dabf5',
|
||||
hoverSecondary: '#af52bf',
|
||||
activePrimary: '#1976d2',
|
||||
activeSecondary: '#7b1fa2',
|
||||
deleteHover: '#ff6666',
|
||||
};
|
||||
|
||||
const COLORS = {
|
||||
shadow: '#000000',
|
||||
};
|
||||
|
||||
interface RobotProfileProps {
|
||||
robot: Robot;
|
||||
setRobot: (state: Robot) => void;
|
||||
setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void;
|
||||
getGenerateRobot: (token: string, slot?: number) => void;
|
||||
inputToken: string;
|
||||
getGenerateRobot: (token: string) => void;
|
||||
setInputToken: (token: string) => void;
|
||||
logoutRobot: () => void;
|
||||
setInputToken: (state: string) => void;
|
||||
width: number;
|
||||
baseUrl: string;
|
||||
setView: (view: string) => void;
|
||||
}
|
||||
|
||||
const RobotProfile = ({
|
||||
@ -42,7 +49,6 @@ const RobotProfile = ({
|
||||
setInputToken,
|
||||
logoutRobot,
|
||||
setView,
|
||||
width,
|
||||
}: RobotProfileProps): JSX.Element => {
|
||||
const { windowSize } = useContext<UseAppStoreType>(AppContext);
|
||||
const { garage, robotUpdatedAt, orderUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
|
||||
@ -51,6 +57,7 @@ const RobotProfile = ({
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
@ -82,278 +89,309 @@ const RobotProfile = ({
|
||||
).length;
|
||||
|
||||
return (
|
||||
<Grid container direction='column' alignItems='center' spacing={1} padding={1} paddingTop={2}>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
direction='column'
|
||||
alignItems='center'
|
||||
spacing={1}
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
<Grid item sx={{ height: '2.3em', position: 'relative' }}>
|
||||
{slot?.nickname ? (
|
||||
<Typography align='center' component='h5' variant='h5'>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
{width < 19 ? null : (
|
||||
<Bolt
|
||||
sx={{
|
||||
color: '#fcba03',
|
||||
height: '1.5em',
|
||||
width: '1.5em',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<b>{slot?.nickname}</b>
|
||||
{width < 19 ? null : (
|
||||
<Bolt
|
||||
sx={{
|
||||
color: '#fcba03',
|
||||
height: '1.5em',
|
||||
width: '1.5em',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Typography>
|
||||
) : (
|
||||
<>
|
||||
<b>{t('Building your robot!')}</b>
|
||||
<LinearProgress />
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
<Grid item sx={{ width: `13.5em` }}>
|
||||
<RobotAvatar
|
||||
<ProfileContainer $isMobile={isMobile}>
|
||||
<InfoSection colors={COLORS} $isMobile={isMobile}>
|
||||
<NicknameTypography variant={isMobile ? "h6" : "h5"} align="center" $isMobile={isMobile}>
|
||||
<BoltIcon $isMobile={isMobile} />
|
||||
{slot?.nickname}
|
||||
<BoltIcon $isMobile={isMobile} />
|
||||
</NicknameTypography>
|
||||
<StyledRobotAvatar
|
||||
hashId={slot?.hashId}
|
||||
smooth={true}
|
||||
style={{ maxWidth: '12.5em', maxHeight: '12.5em' }}
|
||||
placeholderType='generating'
|
||||
imageStyle={{
|
||||
transform: '',
|
||||
border: '2px solid #555',
|
||||
filter: 'drop-shadow(1px 1px 1px #000000)',
|
||||
height: `12.4em`,
|
||||
width: `12.4em`,
|
||||
}}
|
||||
tooltip={t('This is your trading avatar')}
|
||||
tooltipPosition='top'
|
||||
style={{ width: isMobile ? '80px' : '120px', height: isMobile ? '80px' : '120px' }}
|
||||
/>
|
||||
{robot?.found && Boolean(slot?.lastShortAlias) ? (
|
||||
<Typography align='center' variant='h6'>
|
||||
{t('Welcome back!')}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{loadingCoordinators > 0 && !Boolean(robot?.activeOrderId) ? (
|
||||
<Grid>
|
||||
<b>{t('Looking for orders!')}</b>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
{Boolean(robot?.activeOrderId) && Boolean(slot?.hashId) ? (
|
||||
<Grid item>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCurrentOrderId({ id: robot?.activeOrderId, shortAlias: slot?.activeShortAlias });
|
||||
navigate(
|
||||
`/order/${String(slot?.activeShortAlias)}/${String(robot?.activeOrderId)}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t('Active order #{{orderID}}', { orderID: robot?.activeOrderId })}
|
||||
</Button>
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
{Boolean(robot?.lastOrderId) && Boolean(slot?.hashId) ? (
|
||||
<Grid item container direction='column' alignItems='center'>
|
||||
<Grid item>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCurrentOrderId({ id: robot?.lastOrderId, shortAlias: slot?.activeShortAlias });
|
||||
navigate(`/order/${String(slot?.lastShortAlias)}/${String(robot?.lastOrderId)}`);
|
||||
}}
|
||||
>
|
||||
{t('Last order #{{orderID}}', { orderID: robot?.lastOrderId })}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Alert severity='warning'>
|
||||
<Grid container direction='column' alignItems='center'>
|
||||
<Grid item>
|
||||
{t(
|
||||
'Reusing trading identity degrades your privacy against other users, coordinators and observers.',
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item sx={{ position: 'relative', right: '1em' }}>
|
||||
<Button color='success' size='small' onClick={handleAddRobot}>
|
||||
<Add />
|
||||
{t('Add a new Robot')}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Alert>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
{!Boolean(robot?.activeOrderId) &&
|
||||
slot?.hashId &&
|
||||
!Boolean(robot?.lastOrderId) &&
|
||||
loadingCoordinators === 0 ? (
|
||||
<Grid item>{t('No existing orders found')}</Grid>
|
||||
) : null}
|
||||
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
direction='row'
|
||||
justifyContent='stretch'
|
||||
alignItems='stretch'
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
xs={2}
|
||||
sx={{ display: 'flex', justifyContent: 'stretch', alignItems: 'stretch' }}
|
||||
>
|
||||
<Tooltip enterTouchDelay={0} enterDelay={300} enterNextDelay={1000} title={t('Logout')}>
|
||||
<Button
|
||||
sx={{ minWidth: '2em', width: '100%' }}
|
||||
color='primary'
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
<StatusTypography variant={isMobile ? "body2" : "body1"} align="center" $isMobile={isMobile}>
|
||||
{loadingCoordinators > 0 && !robot?.activeOrderId ? t('Looking for orders!') : t('Ready to Trade')}
|
||||
</StatusTypography>
|
||||
{loadingCoordinators > 0 && !robot?.activeOrderId && <StyledLinearProgress $isMobile={isMobile} />}
|
||||
<TokenBox $isMobile={isMobile}>
|
||||
<CustomIconButton onClick={() => {
|
||||
logoutRobot();
|
||||
setView('welcome');
|
||||
}}
|
||||
>
|
||||
<Logout />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<TokenInput
|
||||
inputToken={inputToken}
|
||||
editable={false}
|
||||
label={t('Store your token safely')}
|
||||
setInputToken={setInputToken}
|
||||
onPressEnter={() => null}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item sx={{ width: '100%' }}>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: 'background.paper',
|
||||
border: '1px solid',
|
||||
borderRadius: '4px',
|
||||
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
|
||||
}}
|
||||
>
|
||||
<Grid container direction='column' alignItems='center' spacing={2} padding={2}>
|
||||
<Grid item sx={{ width: '100%' }}>
|
||||
<Typography variant='caption'>{t('Robot Garage')}</Typography>
|
||||
<Select
|
||||
}}>
|
||||
<StyledLogoutIcon $isMobile={isMobile} />
|
||||
</CustomIconButton>
|
||||
<StyledTextField
|
||||
fullWidth
|
||||
required={true}
|
||||
inputProps={{
|
||||
style: { textAlign: 'center' },
|
||||
value={inputToken}
|
||||
variant="standard"
|
||||
$isMobile={isMobile}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
disableUnderline: true,
|
||||
endAdornment: (
|
||||
<CustomIconButton onClick={() => navigator.clipboard.writeText(inputToken)}>
|
||||
<StyledFileCopyIcon $isMobile={isMobile} />
|
||||
</CustomIconButton>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</TokenBox>
|
||||
</InfoSection>
|
||||
|
||||
<RightSection $isMobile={isMobile}>
|
||||
<TitleSection>
|
||||
<TitleTypography variant={isMobile ? "subtitle1" : "h6"} align="center">
|
||||
{t('Robot Garage')}
|
||||
</TitleTypography>
|
||||
</TitleSection>
|
||||
<StyledSelect
|
||||
value={loading ? 'loading' : garage.currentSlot}
|
||||
onChange={handleChangeSlot}
|
||||
$isMobile={isMobile}
|
||||
>
|
||||
{loading ? (
|
||||
<MenuItem key={'loading'} value={'loading'}>
|
||||
<Typography>{t('Building...')}</Typography>
|
||||
<Typography variant={isMobile ? "body2" : "body1"}>{t('Building...')}</Typography>
|
||||
</MenuItem>
|
||||
) : (
|
||||
Object.values(garage.slots).map((slot: Slot, index: number) => {
|
||||
return (
|
||||
<MenuItem key={index} value={slot.token}>
|
||||
<Grid
|
||||
container
|
||||
direction='row'
|
||||
justifyContent='flex-start'
|
||||
alignItems='center'
|
||||
style={{ height: '2.8em' }}
|
||||
spacing={1}
|
||||
>
|
||||
<Grid item>
|
||||
<RobotAvatar
|
||||
Object.values(garage.slots).map((slot: Slot, index: number) => (
|
||||
<StyledMenuItem key={index} value={slot.token} $isMobile={isMobile}>
|
||||
<MenuItemContent>
|
||||
<StyledMenuItemAvatar
|
||||
hashId={slot?.hashId}
|
||||
smooth={true}
|
||||
style={{ width: '2.6em', height: '2.6em' }}
|
||||
$isMobile={isMobile}
|
||||
placeholderType='loading'
|
||||
small={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant={windowSize.width < 26 ? 'caption' : undefined}>
|
||||
{slot?.nickname}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MenuItem>
|
||||
);
|
||||
})
|
||||
<Typography variant={isMobile ? "body2" : "body1"}>{slot?.nickname}</Typography>
|
||||
</MenuItemContent>
|
||||
</StyledMenuItem>
|
||||
))
|
||||
)}
|
||||
</Select>
|
||||
</Grid>
|
||||
|
||||
<Grid item container direction='row' alignItems='center' justifyContent='space-evenly'>
|
||||
<Grid item>
|
||||
<LoadingButton loading={loading} color='primary' onClick={handleAddRobot}>
|
||||
<Add /> <div style={{ width: '0.5em' }} />
|
||||
{t('Add Robot')}
|
||||
</LoadingButton>
|
||||
</Grid>
|
||||
|
||||
{window.NativeRobosats === undefined ? (
|
||||
<Grid item>
|
||||
<Button
|
||||
color='primary'
|
||||
onClick={() => {
|
||||
garage.download();
|
||||
}}
|
||||
</StyledSelect>
|
||||
<ButtonContainer>
|
||||
<StyledButton
|
||||
$buttonColor={BUTTON_COLORS.primary}
|
||||
$hoverColor={BUTTON_COLORS.hoverPrimary}
|
||||
$textColor={BUTTON_COLORS.text}
|
||||
$isMobile={isMobile}
|
||||
onClick={handleAddRobot}
|
||||
>
|
||||
<Download />
|
||||
</Button>
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
<Grid item>
|
||||
<Button
|
||||
color='primary'
|
||||
<StyledAddIcon $isMobile={isMobile} /> {t('ADD ROBOT')}
|
||||
</StyledButton>
|
||||
{window.NativeRobosats === undefined && (
|
||||
<StyledButton
|
||||
$buttonColor={BUTTON_COLORS.secondary}
|
||||
$hoverColor={BUTTON_COLORS.hoverSecondary}
|
||||
$textColor={BUTTON_COLORS.text}
|
||||
$isMobile={isMobile}
|
||||
onClick={() => garage.download()}
|
||||
>
|
||||
<StyledDownloadIcon $isMobile={isMobile} /> {t('DOWNLOAD')}
|
||||
</StyledButton>
|
||||
)}
|
||||
<StyledButton
|
||||
$buttonColor="transparent"
|
||||
$hoverColor={BUTTON_COLORS.deleteHover}
|
||||
$textColor="red"
|
||||
$isMobile={isMobile}
|
||||
onClick={() => {
|
||||
garage.delete();
|
||||
logoutRobot();
|
||||
setView('welcome');
|
||||
}}
|
||||
>
|
||||
<DeleteSweep /> <div style={{ width: '0.5em' }} />
|
||||
{t('Delete Garage')}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<StyledDeleteSweepIcon $isMobile={isMobile} /> {t('DELETE GARAGE')}
|
||||
</StyledButton>
|
||||
</ButtonContainer>
|
||||
</RightSection>
|
||||
</ProfileContainer>
|
||||
);
|
||||
};
|
||||
|
||||
// Styled components
|
||||
const ProfileContainer = styled(Box)<{ $isMobile: boolean }>(({ theme, $isMobile }) => ({
|
||||
width: '100%',
|
||||
maxWidth: $isMobile ? '100%' : 1000,
|
||||
margin: '0 auto',
|
||||
display: 'flex',
|
||||
flexDirection: $isMobile ? 'column' : 'row',
|
||||
border: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: $isMobile ? '4px 4px 0px #000000' : '8px 8px 0px #000000',
|
||||
}));
|
||||
|
||||
const InfoSection = styled(Box)<{ colors: typeof COLORS; $isMobile: boolean }>(({ theme, colors, $isMobile }) => ({
|
||||
flexGrow: 1,
|
||||
flexBasis: $isMobile ? 'auto' : 0,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: $isMobile ? theme.spacing(2) : theme.spacing(4),
|
||||
borderBottom: $isMobile ? '1px solid #000' : 'none',
|
||||
borderRight: $isMobile ? 'none' : '2px solid #000',
|
||||
}));
|
||||
|
||||
const NicknameTypography = styled(Typography)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: $isMobile ? '8px' : '16px',
|
||||
}));
|
||||
|
||||
const BoltIcon = styled(Bolt)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
color: '#fcba03',
|
||||
height: $isMobile ? '0.8em' : '1em',
|
||||
width: $isMobile ? '0.8em' : '1em',
|
||||
}));
|
||||
|
||||
const StyledRobotAvatar = styled(RobotAvatar)({
|
||||
'& img': {
|
||||
border: '2px solid #555',
|
||||
borderRadius: '50%',
|
||||
},
|
||||
});
|
||||
|
||||
const StatusTypography = styled(Typography)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
marginBottom: $isMobile ? '8px' : '16px',
|
||||
}));
|
||||
|
||||
const StyledLinearProgress = styled(LinearProgress)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
width: '100%',
|
||||
marginBottom: $isMobile ? '8px' : '16px',
|
||||
}));
|
||||
|
||||
const TokenBox = styled(Box)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
border: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||
borderRadius: '4px',
|
||||
}));
|
||||
|
||||
const StyledTextField = styled(TextField)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
'& .MuiInputBase-root': {
|
||||
height: $isMobile ? '36px' : '48px',
|
||||
padding: $isMobile ? '2px 4px' : '4px 8px',
|
||||
fontSize: $isMobile ? '0.8rem' : '1rem',
|
||||
}
|
||||
}));
|
||||
|
||||
const RightSection = styled(Box)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
flexGrow: 1,
|
||||
flexBasis: $isMobile ? 'auto' : 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
const TitleSection = styled(Box)(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
borderBottom: `2px solid #000`,
|
||||
}));
|
||||
|
||||
const TitleTypography = styled(Typography)({
|
||||
fontWeight: 'bold',
|
||||
});
|
||||
|
||||
const StyledSelect = styled(Select)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
width: '100%',
|
||||
height: $isMobile ? '50px' : '80px',
|
||||
borderBottom: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||
borderRadius: 0,
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
border: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledMenuItem = styled(MenuItem)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
height: $isMobile ? '50px' : '80px',
|
||||
}));
|
||||
|
||||
const MenuItemContent = styled(Box)({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
const StyledMenuItemAvatar = styled(RobotAvatar)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
width: $isMobile ? '24px' : '30px',
|
||||
height: $isMobile ? '24px' : '30px',
|
||||
marginRight: '8px',
|
||||
}));
|
||||
|
||||
const ButtonContainer = styled(Box)({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
const StyledButton = styled('button')<{
|
||||
$buttonColor: string;
|
||||
$hoverColor: string;
|
||||
$textColor: string;
|
||||
$isMobile: boolean;
|
||||
}>(({ theme, $buttonColor, $hoverColor, $textColor, $isMobile }) => ({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
padding: theme.spacing(2),
|
||||
border: 'none',
|
||||
borderRadius: 0,
|
||||
backgroundColor: $buttonColor,
|
||||
color: $textColor,
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
transition: 'background-color 0.3s ease, color 0.3s ease',
|
||||
width: '100%',
|
||||
height: $isMobile ? '40px' : '60px',
|
||||
borderBottom: $isMobile ? '1px solid #000' : '2px solid #000',
|
||||
'&:hover': {
|
||||
backgroundColor: $hoverColor,
|
||||
color: $buttonColor === 'transparent' ? '#fff' : $textColor,
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: $buttonColor === BUTTON_COLORS.primary ? BUTTON_COLORS.activePrimary : BUTTON_COLORS.activeSecondary,
|
||||
},
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const CustomIconButton = styled('button')({
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
color: '#1976d2',
|
||||
padding: '4px',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'color 0.3s ease',
|
||||
'&:hover': {
|
||||
color: '#0d47a1',
|
||||
},
|
||||
'&:active': {
|
||||
color: '#002171',
|
||||
},
|
||||
});
|
||||
|
||||
const StyledLogoutIcon = styled(Logout)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||
}));
|
||||
|
||||
const StyledFileCopyIcon = styled(FileCopy)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||
}));
|
||||
|
||||
const StyledAddIcon = styled(Add)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
marginRight: '8px',
|
||||
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||
}));
|
||||
|
||||
const StyledDownloadIcon = styled(Download)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
marginRight: '8px',
|
||||
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||
}));
|
||||
|
||||
const StyledDeleteSweepIcon = styled(DeleteSweep)<{ $isMobile: boolean }>(({ $isMobile }) => ({
|
||||
marginRight: '8px',
|
||||
fontSize: $isMobile ? '1rem' : '1.5rem',
|
||||
}));
|
||||
|
||||
export default RobotProfile;
|
@ -1,7 +1,6 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Paper,
|
||||
Grid,
|
||||
CircularProgress,
|
||||
Box,
|
||||
@ -129,7 +128,6 @@ const RobotPage = (): JSX.Element => {
|
||||
} else {
|
||||
return (
|
||||
<StyledMainBox>
|
||||
<StyledPaper>
|
||||
{view === 'welcome' && (
|
||||
<Welcome setView={setView} getGenerateRobot={getGenerateRobot} width={1200} />
|
||||
)}
|
||||
@ -164,7 +162,6 @@ const RobotPage = (): JSX.Element => {
|
||||
getRecoverRobot={getGenerateRobot}
|
||||
/>
|
||||
)}
|
||||
</StyledPaper>
|
||||
</StyledMainBox>
|
||||
);
|
||||
}
|
||||
@ -174,11 +171,13 @@ const RobotPage = (): JSX.Element => {
|
||||
const StyledConnectingBox = styled(Box)({
|
||||
width: '100vw',
|
||||
height: 'auto',
|
||||
backgroundColor: 'transparent',
|
||||
});
|
||||
|
||||
const StyledTorIconBox = styled(Box)({
|
||||
position: 'fixed',
|
||||
top: '4.6em',
|
||||
backgroundColor: 'transparent',
|
||||
});
|
||||
|
||||
const StyledMainBox = styled(Box)({
|
||||
@ -188,18 +187,9 @@ const StyledMainBox = styled(Box)({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '2em',
|
||||
});
|
||||
|
||||
const StyledPaper = styled(Paper)(({ theme }) => ({
|
||||
width: '80vw',
|
||||
maxWidth: '1200px',
|
||||
maxHeight: '85vh',
|
||||
overflow: 'auto',
|
||||
overflowX: 'clip',
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
boxShadow: 'none',
|
||||
padding: '1em',
|
||||
}));
|
||||
});
|
||||
|
||||
export default RobotPage;
|
||||
|
@ -1,22 +1,24 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { Button, Grid, List, ListItem, Paper, TextField, Typography } from '@mui/material';
|
||||
import { Button, Grid, Paper, TextField, Typography, Box } from '@mui/material';
|
||||
import SettingsForm from '../../components/SettingsForm';
|
||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||
import FederationTable from '../../components/FederationTable';
|
||||
import { t } from 'i18next';
|
||||
import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
const SettingsPage = (): JSX.Element => {
|
||||
const { windowSize, navbarHeight } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation, addNewCoordinator } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const maxHeight = (windowSize.height * 0.65)
|
||||
|
||||
const maxHeight = (windowSize.height * 0.65);
|
||||
const [newAlias, setNewAlias] = useState<string>('');
|
||||
const [newUrl, setNewUrl] = useState<string>('');
|
||||
const [error, setError] = useState<string>();
|
||||
// Regular expression to match a valid .onion URL
|
||||
|
||||
const onionUrlPattern = /^((http|https):\/\/)?[a-zA-Z2-7]{16,56}\.onion$/;
|
||||
|
||||
const addCoordinator = () => {
|
||||
const addCoordinator: () => void = () => {
|
||||
if (federation.coordinators[newAlias]) {
|
||||
setError(t('Alias already exists'));
|
||||
} else {
|
||||
@ -35,48 +37,42 @@ const SettingsPage = (): JSX.Element => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper
|
||||
elevation={12}
|
||||
sx={{
|
||||
padding: '0.6em',
|
||||
width: '20.5em',
|
||||
maxHeight: `${maxHeight}em`,
|
||||
overflow: 'auto',
|
||||
overflowX: 'clip',
|
||||
}}
|
||||
>
|
||||
<SettingsContainer elevation={12}>
|
||||
<Grid container>
|
||||
<Grid item>
|
||||
<LeftGrid item xs={12} md={6}>
|
||||
<SettingsForm />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
</LeftGrid>
|
||||
<RightGrid item xs={12} md={6}>
|
||||
<FederationTableWrapper>
|
||||
<FederationTable maxHeight={18} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography align='center' component='h2' variant='subtitle2' color='secondary'>
|
||||
</FederationTableWrapper>
|
||||
{error && (
|
||||
<ErrorTypography align='center' component='h2' variant='subtitle2' color='secondary'>
|
||||
{error}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<List>
|
||||
<ListItem>
|
||||
<TextField
|
||||
</ErrorTypography>
|
||||
)}
|
||||
<InputContainer>
|
||||
<StyledTextField
|
||||
id='outlined-basic'
|
||||
label={t('Alias')}
|
||||
variant='outlined'
|
||||
size='small'
|
||||
value={newAlias}
|
||||
onChange={(e) => setNewAlias(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setNewAlias(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
<StyledTextField
|
||||
id='outlined-basic'
|
||||
label={t('URL')}
|
||||
variant='outlined'
|
||||
size='small'
|
||||
value={newUrl}
|
||||
onChange={(e) => setNewUrl(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setNewUrl(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
sx={{ maxHeight: 38 }}
|
||||
<StyledButton
|
||||
disabled={false}
|
||||
onClick={addCoordinator}
|
||||
variant='contained'
|
||||
@ -85,12 +81,83 @@ const SettingsPage = (): JSX.Element => {
|
||||
type='submit'
|
||||
>
|
||||
{t('Add')}
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
</StyledButton>
|
||||
</InputContainer>
|
||||
</RightGrid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</SettingsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
// Styled Components
|
||||
const SettingsContainer = styled(Paper)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
width: '80vw',
|
||||
height: '70vh',
|
||||
margin: '0 auto',
|
||||
border: '2px solid #000',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '8px 8px 0px #000',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
flexDirection: 'column',
|
||||
width: '90vw',
|
||||
height: 'fit-content',
|
||||
marginTop: '30rem',
|
||||
},
|
||||
}));
|
||||
|
||||
const LeftGrid = styled(Grid)(({ theme }) => ({
|
||||
padding: '2rem',
|
||||
borderRight: '2px solid #000',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: '1rem',
|
||||
borderRight: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const RightGrid = styled(Grid)(({ theme }) => ({
|
||||
padding: '2rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: '1rem',
|
||||
},
|
||||
}));
|
||||
|
||||
const FederationTableWrapper = styled(Box)({
|
||||
flexGrow: 1,
|
||||
'& > *': {
|
||||
width: '100% !important',
|
||||
},
|
||||
});
|
||||
|
||||
const ErrorTypography = styled(Typography)({
|
||||
// You can add specific styles for the error message here if needed
|
||||
});
|
||||
|
||||
const InputContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: theme.spacing(1),
|
||||
marginTop: theme.spacing(2),
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
flexDirection: 'column',
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledTextField = styled(TextField)(({ theme }) => ({
|
||||
flexGrow: 1,
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledButton = styled(Button)(({ theme }) => ({
|
||||
maxHeight: 40,
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
width: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
export default SettingsPage;
|
@ -14,7 +14,6 @@ import {
|
||||
LinearProgress,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
type LinearProgressProps,
|
||||
styled,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
@ -24,7 +23,7 @@ import {
|
||||
type GridPaginationModel,
|
||||
type GridColDef,
|
||||
type GridValidRowModel,
|
||||
GridSlotsComponent,
|
||||
type GridSlotsComponent,
|
||||
} from '@mui/x-data-grid';
|
||||
import currencyDict from '../../../static/assets/currencies.json';
|
||||
import { type PublicOrder } from '../../models';
|
||||
@ -831,14 +830,6 @@ const BookTable = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface GridComponentProps {
|
||||
LoadingOverlay: (props: LinearProgressProps) => JSX.Element;
|
||||
NoResultsOverlay?: (props: any) => JSX.Element;
|
||||
NoRowsOverlay?: (props: any) => JSX.Element;
|
||||
Footer?: (props: any) => JSX.Element;
|
||||
Toolbar?: (props: any) => JSX.Element;
|
||||
}
|
||||
|
||||
const NoResultsOverlay = function (): JSX.Element {
|
||||
return (
|
||||
<Grid
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
|
@ -1,78 +1,38 @@
|
||||
import React, { useCallback, useEffect, useState, useContext, useMemo } from 'react';
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Box, useTheme, Checkbox, CircularProgress, Typography, Grid } from '@mui/material';
|
||||
import { Box, Checkbox, CircularProgress, Grid, Typography } from '@mui/material';
|
||||
import { DataGrid, type GridColDef, type GridValidRowModel } from '@mui/x-data-grid';
|
||||
import { type Coordinator } from '../../models';
|
||||
import RobotAvatar from '../RobotAvatar';
|
||||
import { Link, LinkOff } from '@mui/icons-material';
|
||||
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
|
||||
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||
import headerStyleFix from '../DataGrid/HeaderFix';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
interface FederationTableProps {
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
fillContainer?: boolean;
|
||||
}
|
||||
|
||||
const FederationTable = ({
|
||||
maxWidth = 90,
|
||||
maxHeight = 50,
|
||||
fillContainer = false,
|
||||
}: FederationTableProps): JSX.Element => {
|
||||
const FederationTable = ({ maxWidth = 90, fillContainer = false }: FederationTableProps): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const { federation, sortedCoordinators, coordinatorUpdatedAt, federationUpdatedAt } =
|
||||
useContext<UseFederationStoreType>(FederationContext);
|
||||
const { federation, sortedCoordinators, coordinatorUpdatedAt } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { setOpen, settings } = useContext<UseAppStoreType>(AppContext);
|
||||
const theme = useTheme();
|
||||
const [pageSize, setPageSize] = useState<number>(0);
|
||||
|
||||
// all sizes in 'em'
|
||||
const fontSize = theme.typography.fontSize;
|
||||
const verticalHeightFrame = 3.3;
|
||||
const verticalHeightRow = 3.27;
|
||||
const defaultPageSize = Math.max(
|
||||
Math.floor((maxHeight - verticalHeightFrame) / verticalHeightRow),
|
||||
1,
|
||||
);
|
||||
const height = defaultPageSize * verticalHeightRow + verticalHeightFrame;
|
||||
|
||||
const [useDefaultPageSize, setUseDefaultPageSize] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (useDefaultPageSize) {
|
||||
setPageSize(defaultPageSize);
|
||||
}
|
||||
}, [coordinatorUpdatedAt, federationUpdatedAt]);
|
||||
|
||||
const localeText = {
|
||||
MuiTablePagination: { labelRowsPerPage: t('Coordinators per page:') },
|
||||
noResultsOverlayLabel: t('No coordinators found.'),
|
||||
};
|
||||
|
||||
const onClickCoordinator = function (shortAlias: string): void {
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: shortAlias };
|
||||
});
|
||||
};
|
||||
|
||||
const aliasObj = useCallback((width: number) => {
|
||||
return {
|
||||
field: 'longAlias',
|
||||
headerName: t('Coordinator'),
|
||||
width: width * fontSize,
|
||||
width: width,
|
||||
renderCell: (params: any) => {
|
||||
const coordinator = federation.coordinators[params.row.shortAlias];
|
||||
return (
|
||||
<Grid
|
||||
<CoordinatorGrid
|
||||
container
|
||||
direction='row'
|
||||
sx={{ cursor: 'pointer', position: 'relative', left: '-0.3em', width: '50em' }}
|
||||
wrap='nowrap'
|
||||
onClick={() => {
|
||||
onClickCoordinator(params.row.shortAlias);
|
||||
}}
|
||||
alignItems='center'
|
||||
direction="row"
|
||||
wrap="nowrap"
|
||||
onClick={() => onClickCoordinator(params.row.shortAlias)}
|
||||
alignItems="center"
|
||||
spacing={1}
|
||||
>
|
||||
<Grid item>
|
||||
@ -87,31 +47,29 @@ const FederationTable = ({
|
||||
<Grid item>
|
||||
<Typography>{params.row.longAlias}</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CoordinatorGrid>
|
||||
);
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
}, [federation.coordinators]);
|
||||
|
||||
const enabledObj = useCallback(
|
||||
(width: number) => {
|
||||
return {
|
||||
field: 'enabled',
|
||||
headerName: t('Enabled'),
|
||||
width: width * fontSize,
|
||||
width: width,
|
||||
renderCell: (params: any) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={params.row.enabled}
|
||||
onClick={() => {
|
||||
onEnableChange(params.row.shortAlias);
|
||||
}}
|
||||
onClick={() => onEnableChange(params.row.shortAlias)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
[coordinatorUpdatedAt],
|
||||
[coordinatorUpdatedAt]
|
||||
);
|
||||
|
||||
const upObj = useCallback(
|
||||
@ -119,52 +77,41 @@ const FederationTable = ({
|
||||
return {
|
||||
field: 'up',
|
||||
headerName: t('Up'),
|
||||
width: width * fontSize,
|
||||
width: width,
|
||||
renderCell: (params: any) => {
|
||||
return (
|
||||
<div
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
onClickCoordinator(params.row.shortAlias);
|
||||
}}
|
||||
>
|
||||
{Boolean(params.row.loadingInfo) && Boolean(params.row.enabled) ? (
|
||||
<CircularProgress thickness={0.35 * fontSize} size={1.5 * fontSize} />
|
||||
<UpStatusContainer onClick={() => onClickCoordinator(params.row.shortAlias)}>
|
||||
{params.row.loadingInfo && params.row.enabled ? (
|
||||
<CircularProgress thickness={2} size={24} />
|
||||
) : params.row.info !== undefined ? (
|
||||
<Link color='success' />
|
||||
<Link color="success" />
|
||||
) : (
|
||||
<LinkOff color='error' />
|
||||
<LinkOff color="error" />
|
||||
)}
|
||||
</div>
|
||||
</UpStatusContainer>
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
[coordinatorUpdatedAt],
|
||||
[coordinatorUpdatedAt]
|
||||
);
|
||||
|
||||
const columnSpecs = {
|
||||
alias: {
|
||||
priority: 2,
|
||||
order: 1,
|
||||
normal: {
|
||||
width: 12.1,
|
||||
width: 200,
|
||||
object: aliasObj,
|
||||
},
|
||||
},
|
||||
up: {
|
||||
priority: 3,
|
||||
order: 2,
|
||||
normal: {
|
||||
width: 3.5,
|
||||
width: 70,
|
||||
object: upObj,
|
||||
},
|
||||
},
|
||||
enabled: {
|
||||
priority: 1,
|
||||
order: 3,
|
||||
normal: {
|
||||
width: 5,
|
||||
width: 90,
|
||||
object: enabledObj,
|
||||
},
|
||||
},
|
||||
@ -174,28 +121,16 @@ const FederationTable = ({
|
||||
columns: Array<GridColDef<GridValidRowModel>>;
|
||||
width: number;
|
||||
} {
|
||||
const useSmall = maxWidth < 30;
|
||||
const selectedColumns: object[] = [];
|
||||
let width: number = 0;
|
||||
|
||||
for (const value of Object.values(columnSpecs)) {
|
||||
const colWidth = Number(
|
||||
useSmall && Boolean(value.small) ? value.small.width : value.normal.width,
|
||||
);
|
||||
const colObject = useSmall && Boolean(value.small) ? value.small.object : value.normal.object;
|
||||
const colWidth = value.normal.width;
|
||||
const colObject = value.normal.object;
|
||||
|
||||
if (width + colWidth < maxWidth || selectedColumns.length < 2) {
|
||||
width = width + colWidth;
|
||||
selectedColumns.push([colObject(colWidth, false), value.order]);
|
||||
} else {
|
||||
selectedColumns.push([colObject(colWidth, true), value.order]);
|
||||
width += colWidth;
|
||||
selectedColumns.push([colObject(colWidth), value.order]);
|
||||
}
|
||||
}
|
||||
|
||||
// sort columns by column.order value
|
||||
selectedColumns.sort(function (first, second) {
|
||||
return first[1] - second[1];
|
||||
});
|
||||
|
||||
const columns: Array<GridColDef<GridValidRowModel>> = selectedColumns.map(function (item) {
|
||||
return item[0];
|
||||
@ -204,7 +139,7 @@ const FederationTable = ({
|
||||
return { columns, width: width * 0.9 };
|
||||
};
|
||||
|
||||
const { columns, width } = filteredColumns();
|
||||
const { columns } = filteredColumns();
|
||||
|
||||
const onEnableChange = function (shortAlias: string): void {
|
||||
if (federation.getCoordinator(shortAlias).enabled === true) {
|
||||
@ -217,38 +152,65 @@ const FederationTable = ({
|
||||
const reorderedCoordinators = useMemo(() => {
|
||||
return sortedCoordinators.reduce((coordinators, key) => {
|
||||
coordinators[key] = federation.coordinators[key];
|
||||
|
||||
return coordinators;
|
||||
}, {});
|
||||
}, [settings.network, federationUpdatedAt]);
|
||||
}, [settings.network, coordinatorUpdatedAt]);
|
||||
|
||||
const onClickCoordinator = (shortAlias: string): void => {
|
||||
setOpen((open) => {
|
||||
return { ...open, coordinator: shortAlias };
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={
|
||||
fillContainer
|
||||
? { width: '100%', height: '100%' }
|
||||
: { width: `${width}em`, height: `${height}em`, overflow: 'auto' }
|
||||
}
|
||||
>
|
||||
<DataGrid
|
||||
sx={headerStyleFix}
|
||||
localeText={localeText}
|
||||
rowHeight={3.714 * theme.typography.fontSize}
|
||||
headerHeight={3.25 * theme.typography.fontSize}
|
||||
<TableContainer fillContainer={fillContainer} maxWidth={maxWidth}>
|
||||
<StyledDataGrid
|
||||
rows={Object.values(reorderedCoordinators)}
|
||||
getRowId={(params: Coordinator) => params.shortAlias}
|
||||
columns={columns}
|
||||
checkboxSelection={false}
|
||||
pageSize={pageSize}
|
||||
rowsPerPageOptions={width < 22 ? [] : [0, pageSize, defaultPageSize * 2, 50, 100]}
|
||||
onPageSizeChange={(newPageSize) => {
|
||||
setPageSize(newPageSize);
|
||||
setUseDefaultPageSize(false);
|
||||
}}
|
||||
hideFooter={true}
|
||||
autoHeight
|
||||
hideFooter
|
||||
/>
|
||||
</Box>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
// Styled Components
|
||||
const TableContainer = styled(Box, {
|
||||
shouldForwardProp: (prop) => prop !== 'fillContainer' && prop !== 'maxWidth',
|
||||
})<{ fillContainer: boolean; maxWidth: number }>(({ theme, fillContainer, maxWidth }) => ({
|
||||
width: fillContainer ? '100%' : `${maxWidth}em`,
|
||||
height: 'fit-content',
|
||||
border: '2px solid black',
|
||||
overflow: 'hidden',
|
||||
borderRadius: '8px',
|
||||
boxShadow: 'none',
|
||||
}));
|
||||
|
||||
const StyledDataGrid = styled(DataGrid)(({ theme }) => ({
|
||||
'& .MuiDataGrid-root': {
|
||||
fontSize: { xs: '0.8rem', sm: '0.9rem', md: '1rem' },
|
||||
},
|
||||
'& .MuiDataGrid-cell': {
|
||||
padding: { xs: '0.5rem', sm: '1rem' },
|
||||
},
|
||||
'& .MuiDataGrid-columnHeaders': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
borderBottom: '2px solid black',
|
||||
fontSize: { xs: '0.8rem', sm: '0.9rem', md: '1rem' },
|
||||
},
|
||||
}));
|
||||
|
||||
const CoordinatorGrid = styled(Grid)({
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
left: '-0.3em',
|
||||
width: '50em',
|
||||
});
|
||||
|
||||
const UpStatusContainer = styled('div')({
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
export default FederationTable;
|
@ -90,6 +90,7 @@ const FlagWithProps = ({ code, width = '1.428em', height = '1.428em' }: Props):
|
||||
if (code === 'IRT') flag = <Flags.IR {...defaultProps} />;
|
||||
if (code === 'BDT') flag = <Flags.BD {...defaultProps} />;
|
||||
if (code === 'ALL') flag = <Flags.AL {...defaultProps} />;
|
||||
if (code === 'DZD') flag = <Flags.DZ {...defaultProps} />;
|
||||
if (code === 'ANY') flag = <EarthIcon {...defaultProps} />;
|
||||
if (code === 'XAU') flag = <GoldIcon {...defaultProps} />;
|
||||
if (code === 'BTC') flag = <SwapCallsIcon color='primary' />;
|
||||
|
@ -409,9 +409,8 @@ const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => {
|
||||
))}
|
||||
{qttHiddenTags > 0 ? (
|
||||
<StyledChip
|
||||
sx={{ borderRadius: 1 }}
|
||||
label={`+${qttHiddenTags}`}
|
||||
sx={{ height: '1.6rem' }}
|
||||
sx={{ borderRadius: 1, height: '1.6rem' }}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
Grid,
|
||||
Select,
|
||||
|
@ -248,7 +248,7 @@ const Notifications = ({
|
||||
// 11: 'In dispute'
|
||||
// 12: 'Collaboratively cancelled'
|
||||
// 13: 'Sending satoshis to buyer'
|
||||
// 14: 'Sucessful trade'
|
||||
// 14: 'Successful trade'
|
||||
// 15: 'Failed lightning network routing'
|
||||
// 16: 'Wait for dispute resolution'
|
||||
// 17: 'Maker lost dispute'
|
||||
|
@ -174,8 +174,8 @@ const OrderDetails = ({
|
||||
|
||||
const isBuyer = (order.type === 0 && order.is_maker) || (order.type === 1 && !order.is_maker);
|
||||
const tradeFee = order.is_maker
|
||||
? coordinator.info?.maker_fee ?? 0
|
||||
: coordinator.info?.taker_fee ?? 0;
|
||||
? (coordinator.info?.maker_fee ?? 0)
|
||||
: (coordinator.info?.taker_fee ?? 0);
|
||||
const defaultRoutingBudget = 0.001;
|
||||
const btc_now = order.satoshis_now / 100000000;
|
||||
const rate = Number(order.max_amount ?? order.amount) / btc_now;
|
||||
|
@ -1,39 +1,36 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { type UseAppStoreType, AppContext } from '../../contexts/AppContext';
|
||||
import {
|
||||
Grid,
|
||||
Paper,
|
||||
Switch,
|
||||
useTheme,
|
||||
FormControlLabel,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
Slider,
|
||||
Typography,
|
||||
ToggleButtonGroup,
|
||||
ToggleButton,
|
||||
Box,
|
||||
Slider,
|
||||
} from '@mui/material';
|
||||
import SelectLanguage from './SelectLanguage';
|
||||
import {
|
||||
Translate,
|
||||
Palette,
|
||||
LightMode,
|
||||
DarkMode,
|
||||
SettingsOverscan,
|
||||
Link,
|
||||
AccountBalance,
|
||||
AttachMoney,
|
||||
QrCode,
|
||||
ControlPoint,
|
||||
DarkMode,
|
||||
} from '@mui/icons-material';
|
||||
import { systemClient } from '../../services/System';
|
||||
import { TorIcon } from '../Icons';
|
||||
import SwapCalls from '@mui/icons-material/SwapCalls';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
import { apiClient } from '../../services/api';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
interface SettingsFormProps {
|
||||
dense?: boolean;
|
||||
@ -41,10 +38,9 @@ interface SettingsFormProps {
|
||||
|
||||
const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
const { fav, setFav, settings, setSettings } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
const fontSizes = [
|
||||
{ label: 'XS', value: { basic: 12, pro: 10 } },
|
||||
{ label: 'S', value: { basic: 13, pro: 11 } },
|
||||
@ -53,64 +49,59 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
{ label: 'XL', value: { basic: 16, pro: 14 } },
|
||||
];
|
||||
|
||||
const handleToggleChange = (e, newValue) => {
|
||||
if (newValue !== null) {
|
||||
setFav({ ...fav, mode: newValue, currency: newValue === 'fiat' ? 0 : 1000 });
|
||||
}
|
||||
};
|
||||
|
||||
const handleNetworkChange = (e, newValue) => {
|
||||
if (newValue !== null) {
|
||||
setSettings({ ...settings, network: newValue });
|
||||
systemClient.setItem('settings_network', newValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<List dense={dense}>
|
||||
<ListItem>
|
||||
{/* Language Settings */}
|
||||
<StyledListItem>
|
||||
<SettingHeader>
|
||||
<ListItemIcon>
|
||||
<Translate />
|
||||
</ListItemIcon>
|
||||
<SelectLanguage
|
||||
<Typography variant="subtitle1">
|
||||
{t('Language Settings')}
|
||||
</Typography>
|
||||
</SettingHeader>
|
||||
<StyledSelectLanguage
|
||||
language={settings.language}
|
||||
setLanguage={(language) => {
|
||||
setSettings({ ...settings, language });
|
||||
systemClient.setItem('settings_language', language);
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
</StyledListItem>
|
||||
|
||||
<ListItem>
|
||||
{/* Appearance Settings */}
|
||||
<StyledListItem>
|
||||
<SettingHeader>
|
||||
<ListItemIcon>
|
||||
<Palette />
|
||||
</ListItemIcon>
|
||||
<Typography variant="subtitle1">
|
||||
{t('Appearance Settings')}
|
||||
</Typography>
|
||||
</SettingHeader>
|
||||
<AppearanceSettingsBox>
|
||||
<FormControlLabel
|
||||
labelPlacement='end'
|
||||
label={settings.mode === 'dark' ? t('Dark') : t('Light')}
|
||||
labelPlacement="end"
|
||||
label={t('Dark Mode')}
|
||||
control={
|
||||
<Switch
|
||||
checked={settings.mode === 'dark'}
|
||||
checkedIcon={
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
width: '1.2em',
|
||||
height: '1.2em',
|
||||
borderRadius: '0.4em',
|
||||
backgroundColor: 'white',
|
||||
position: 'relative',
|
||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
||||
}}
|
||||
>
|
||||
<DarkMode sx={{ width: '0.8em', height: '0.8em', color: '#666' }} />
|
||||
</Paper>
|
||||
}
|
||||
icon={
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
width: '1.2em',
|
||||
height: '1.2em',
|
||||
borderRadius: '0.4em',
|
||||
backgroundColor: 'white',
|
||||
padding: '0.07em',
|
||||
position: 'relative',
|
||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
||||
}}
|
||||
>
|
||||
<LightMode sx={{ width: '0.67em', height: '0.67em', color: '#666' }} />
|
||||
</Paper>
|
||||
}
|
||||
onChange={(e) => {
|
||||
const mode = e.target.checked ? 'dark' : 'light';
|
||||
setSettings({ ...settings, mode });
|
||||
@ -119,49 +110,13 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{settings.mode === 'dark' ? (
|
||||
<>
|
||||
<ListItemIcon>
|
||||
<QrCode />
|
||||
</ListItemIcon>
|
||||
<FormControlLabel
|
||||
sx={{ position: 'relative', right: '1.5em', width: '3em' }}
|
||||
labelPlacement='end'
|
||||
label={settings.lightQRs ? t('Light') : t('Dark')}
|
||||
{settings.mode === 'dark' && (
|
||||
<QRCodeSwitch
|
||||
labelPlacement="end"
|
||||
label={t('QR Code Color')}
|
||||
control={
|
||||
<Switch
|
||||
checked={!settings.lightQRs}
|
||||
checkedIcon={
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
width: '1.2em',
|
||||
height: '1.2em',
|
||||
borderRadius: '0.4em',
|
||||
backgroundColor: 'white',
|
||||
position: 'relative',
|
||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
||||
}}
|
||||
>
|
||||
<DarkMode sx={{ width: '0.8em', height: '0.8em', color: '#666' }} />
|
||||
</Paper>
|
||||
}
|
||||
icon={
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
width: '1.2em',
|
||||
height: '1.2em',
|
||||
borderRadius: '0.4em',
|
||||
backgroundColor: 'white',
|
||||
padding: '0.07em',
|
||||
position: 'relative',
|
||||
top: `${7 - 0.5 * theme.typography.fontSize}px`,
|
||||
}}
|
||||
>
|
||||
<LightMode sx={{ width: '0.67em', height: '0.67em', color: '#666' }} />
|
||||
</Paper>
|
||||
}
|
||||
onChange={(e) => {
|
||||
const lightQRs = !e.target.checked;
|
||||
setSettings({ ...settings, lightQRs });
|
||||
@ -170,17 +125,21 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</ListItem>
|
||||
</AppearanceSettingsBox>
|
||||
</StyledListItem>
|
||||
|
||||
<ListItem>
|
||||
{/* Font Size Settings */}
|
||||
<StyledListItem>
|
||||
<SettingHeader>
|
||||
<ListItemIcon>
|
||||
<SettingsOverscan />
|
||||
</ListItemIcon>
|
||||
<Slider
|
||||
<Typography variant="subtitle1">
|
||||
{t('Font Size')}
|
||||
</Typography>
|
||||
</SettingHeader>
|
||||
<StyledSlider
|
||||
value={settings.fontSize}
|
||||
min={settings.frontend === 'basic' ? 12 : 10}
|
||||
max={settings.frontend === 'basic' ? 16 : 14}
|
||||
@ -190,80 +149,98 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
setSettings({ ...settings, fontSize });
|
||||
systemClient.setItem(`settings_fontsize_${settings.frontend}`, fontSize.toString());
|
||||
}}
|
||||
valueLabelDisplay='off'
|
||||
valueLabelDisplay="off"
|
||||
track={false}
|
||||
marks={fontSizes.map(({ label, value }) => ({
|
||||
label: <Typography variant='caption'>{t(label)}</Typography>,
|
||||
label: <Typography variant="caption">{t(label)}</Typography>,
|
||||
value: settings.frontend === 'basic' ? value.basic : value.pro,
|
||||
}))}
|
||||
track={false}
|
||||
/>
|
||||
</ListItem>
|
||||
</StyledListItem>
|
||||
|
||||
<ListItem>
|
||||
{/* Currency Settings */}
|
||||
<StyledListItem>
|
||||
<SettingHeader>
|
||||
<ListItemIcon>
|
||||
<AccountBalance />
|
||||
</ListItemIcon>
|
||||
<ToggleButtonGroup
|
||||
<Typography variant="subtitle1">
|
||||
{t('Currency Settings')}
|
||||
</Typography>
|
||||
</SettingHeader>
|
||||
<StyledToggleButtonGroup
|
||||
exclusive={true}
|
||||
value={fav.mode}
|
||||
onChange={(e, mode) => {
|
||||
setFav({ ...fav, mode, currency: mode === 'fiat' ? 0 : 1000 });
|
||||
}}
|
||||
onChange={handleToggleChange}
|
||||
fullWidth
|
||||
>
|
||||
<ToggleButton value='fiat' color='primary'>
|
||||
<StyledToggleButton value="fiat">
|
||||
<AttachMoney />
|
||||
{t('Fiat')}
|
||||
</ToggleButton>
|
||||
<ToggleButton value='swap' color='secondary'>
|
||||
</StyledToggleButton>
|
||||
<StyledToggleButton value="swap">
|
||||
<SwapCalls />
|
||||
{t('Swaps')}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</ListItem>
|
||||
</StyledToggleButton>
|
||||
</StyledToggleButtonGroup>
|
||||
</StyledListItem>
|
||||
|
||||
<ListItem>
|
||||
{/* Network Settings */}
|
||||
<StyledListItem>
|
||||
<SettingHeader>
|
||||
<ListItemIcon>
|
||||
<Link />
|
||||
</ListItemIcon>
|
||||
<ToggleButtonGroup
|
||||
<Typography variant="subtitle1">
|
||||
{t('Network Settings')}
|
||||
</Typography>
|
||||
</SettingHeader>
|
||||
<StyledToggleButtonGroup
|
||||
exclusive={true}
|
||||
value={settings.network}
|
||||
onChange={(e, network) => {
|
||||
setSettings({ ...settings, network });
|
||||
systemClient.setItem('settings_network', network);
|
||||
}}
|
||||
onChange={handleNetworkChange}
|
||||
fullWidth
|
||||
>
|
||||
<ToggleButton value='mainnet' color='primary'>
|
||||
<StyledToggleButton value="mainnet">
|
||||
{t('Mainnet')}
|
||||
</ToggleButton>
|
||||
<ToggleButton value='testnet' color='secondary'>
|
||||
</StyledToggleButton>
|
||||
<StyledToggleButton value="testnet">
|
||||
{t('Testnet')}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</ListItem>
|
||||
</StyledToggleButton>
|
||||
</StyledToggleButtonGroup>
|
||||
</StyledListItem>
|
||||
|
||||
{/* Proxy Settings */}
|
||||
{window.NativeRobosats !== undefined && (
|
||||
<ListItem>
|
||||
<StyledListItem>
|
||||
<SettingHeader>
|
||||
<ListItemIcon>
|
||||
<TorIcon />
|
||||
</ListItemIcon>
|
||||
<ToggleButtonGroup
|
||||
<Typography variant="subtitle1">
|
||||
{t('Proxy Settings')}
|
||||
</Typography>
|
||||
</SettingHeader>
|
||||
<StyledToggleButtonGroup
|
||||
exclusive={true}
|
||||
value={settings.useProxy}
|
||||
onChange={(_e, useProxy) => {
|
||||
if (useProxy !== null) {
|
||||
setSettings({ ...settings, useProxy });
|
||||
systemClient.setItem('settings_use_proxy', String(useProxy));
|
||||
apiClient.useProxy = useProxy;
|
||||
}
|
||||
}}
|
||||
fullWidth
|
||||
>
|
||||
<ToggleButton value={true} color='primary'>
|
||||
{t('Build-in')}
|
||||
</ToggleButton>
|
||||
<ToggleButton value={false} color='secondary'>
|
||||
<StyledToggleButton value={true}>
|
||||
{t('Built-in')}
|
||||
</StyledToggleButton>
|
||||
<StyledToggleButton value={false}>
|
||||
{t('Disabled')}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</ListItem>
|
||||
</StyledToggleButton>
|
||||
</StyledToggleButtonGroup>
|
||||
</StyledListItem>
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
@ -271,4 +248,79 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
);
|
||||
};
|
||||
|
||||
// Styled Components
|
||||
const StyledListItem = styled(ListItem)({
|
||||
display: 'block',
|
||||
});
|
||||
|
||||
const SettingHeader = styled(Box)({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: '0.5em',
|
||||
});
|
||||
|
||||
const StyledSelectLanguage = styled(SelectLanguage)(({ theme }) => ({
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: '8px',
|
||||
border: '2px solid black',
|
||||
boxShadow: '4px 4px 0px rgba(0, 0, 0, 1)',
|
||||
width: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
const AppearanceSettingsBox = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2),
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
flexDirection: 'row',
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
const QRCodeSwitch = styled(FormControlLabel)(({ theme }) => ({
|
||||
marginLeft: 0,
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledSlider = styled(Slider)(({ theme }) => ({
|
||||
'& .MuiSlider-thumb': {
|
||||
borderRadius: '8px',
|
||||
border: '2px solid black',
|
||||
},
|
||||
'& .MuiSlider-track': {
|
||||
borderRadius: '8px',
|
||||
border: '2px solid black',
|
||||
},
|
||||
'& .MuiSlider-rail': {
|
||||
borderRadius: '8px',
|
||||
border: '2px solid black',
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledToggleButtonGroup = styled(ToggleButtonGroup)({
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
const StyledToggleButton = styled(ToggleButton)(({ theme }) => ({
|
||||
borderRadius: '8px',
|
||||
border: '2px solid black',
|
||||
boxShadow: 'none',
|
||||
fontWeight: 'bold',
|
||||
width: '100%',
|
||||
'&.Mui-selected': {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastText,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: 'initial',
|
||||
},
|
||||
}));
|
||||
|
||||
export default SettingsForm;
|
@ -639,7 +639,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => {
|
||||
}
|
||||
break;
|
||||
|
||||
// 14: 'Sucessful trade'
|
||||
// 14: 'Successful trade'
|
||||
case 14:
|
||||
baseContract.title = 'Trade finished!';
|
||||
baseContract.titleColor = 'success';
|
||||
|
@ -4,19 +4,17 @@ import React, {
|
||||
useEffect,
|
||||
useState,
|
||||
type SetStateAction,
|
||||
useMemo,
|
||||
useContext,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
|
||||
import { type Order, Federation, Settings, Coordinator } from '../models';
|
||||
import { type Order, Federation, Settings } from '../models';
|
||||
|
||||
import { federationLottery } from '../utils';
|
||||
|
||||
import { AppContext, type UseAppStoreType } from './AppContext';
|
||||
import { GarageContext, type UseGarageStoreType } from './GarageContext';
|
||||
import NativeRobosats from '../services/Native';
|
||||
import { Origins } from '../models/Coordinator.model';
|
||||
import { type Origin, type Origins } from '../models/Coordinator.model';
|
||||
|
||||
// Refresh delays (ms) according to Order status
|
||||
const defaultDelay = 5000;
|
||||
@ -35,7 +33,7 @@ const statusToDelay = [
|
||||
100000, // 'In dispute'
|
||||
999999, // 'Collaboratively cancelled'
|
||||
10000, // 'Sending satoshis to buyer'
|
||||
60000, // 'Sucessful trade'
|
||||
60000, // 'Successful trade'
|
||||
30000, // 'Failed lightning network routing'
|
||||
300000, // 'Wait for dispute resolution'
|
||||
300000, // 'Maker lost dispute'
|
||||
@ -175,14 +173,15 @@ export const FederationContextProvider = ({
|
||||
federated: false,
|
||||
enabled: true,
|
||||
};
|
||||
const origins: Origins = {
|
||||
clearnet: undefined,
|
||||
onion: url as Origin,
|
||||
i2p: undefined,
|
||||
};
|
||||
if (settings.network === 'mainnet') {
|
||||
attributes.mainnet = {
|
||||
onion: url,
|
||||
} as Origins;
|
||||
attributes.mainnet = origins;
|
||||
} else {
|
||||
attributes.testnet = {
|
||||
onion: url,
|
||||
} as Origins;
|
||||
attributes.testnet = origins;
|
||||
}
|
||||
federation.addCoordinator(origin, settings, hostUrl, attributes);
|
||||
const newCoordinator = federation.coordinators[alias];
|
||||
|
@ -210,7 +210,7 @@ export class Coordinator {
|
||||
|
||||
generateAllMakerAvatars = async (data: [PublicOrder]): Promise<void> => {
|
||||
for (const order of data) {
|
||||
roboidentitiesClient.generateRobohash(order.maker_hash_id, 'small');
|
||||
void roboidentitiesClient.generateRobohash(order.maker_hash_id, 'small');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,7 @@ export class Federation {
|
||||
settings: Settings,
|
||||
hostUrl: string,
|
||||
attributes: Record<any, any>,
|
||||
) => {
|
||||
): void => {
|
||||
const value = {
|
||||
...coordinatorDefaultValues,
|
||||
...attributes,
|
||||
@ -111,7 +111,7 @@ export class Federation {
|
||||
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
||||
this.updateEnabledCoordinators();
|
||||
for (const coor of Object.values(this.coordinators)) {
|
||||
coor.update(() => {
|
||||
void coor.update(() => {
|
||||
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
|
||||
this.onCoordinatorSaved();
|
||||
});
|
||||
@ -124,7 +124,7 @@ export class Federation {
|
||||
this.triggerHook('onCoordinatorUpdate');
|
||||
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
||||
for (const coor of Object.values(this.coordinators)) {
|
||||
coor.updateBook(() => {
|
||||
void coor.updateBook(() => {
|
||||
this.onCoordinatorSaved();
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Coordinator, type Order } from '.';
|
||||
import { type Coordinator, type Order } from '.';
|
||||
import { systemClient } from '../services/System';
|
||||
import { saveAsJson } from '../utils';
|
||||
import Slot from './Slot.model';
|
||||
@ -59,8 +59,13 @@ class Garage {
|
||||
const rawSlots = JSON.parse(slotsDump);
|
||||
Object.values(rawSlots).forEach((rawSlot: Record<any, any>) => {
|
||||
if (rawSlot?.token) {
|
||||
this.slots[rawSlot.token] = new Slot(rawSlot.token, Object.keys(rawSlot.robots), {}, () =>
|
||||
this.triggerHook('onRobotUpdate'),
|
||||
this.slots[rawSlot.token] = new Slot(
|
||||
rawSlot.token,
|
||||
Object.keys(rawSlot.robots),
|
||||
{},
|
||||
() => {
|
||||
this.triggerHook('onRobotUpdate');
|
||||
},
|
||||
);
|
||||
Object.keys(rawSlot.robots).forEach((shortAlias) => {
|
||||
const rawRobot = rawSlot.robots[shortAlias];
|
||||
@ -78,7 +83,7 @@ class Garage {
|
||||
// Slots
|
||||
getSlot: (token?: string) => Slot | null = (token) => {
|
||||
const currentToken = token ?? this.currentSlot;
|
||||
return currentToken ? this.slots[currentToken] ?? null : null;
|
||||
return currentToken ? (this.slots[currentToken] ?? null) : null;
|
||||
};
|
||||
|
||||
deleteSlot: (token?: string) => void = (token) => {
|
||||
@ -118,9 +123,9 @@ class Garage {
|
||||
if (!token || !shortAliases) return;
|
||||
|
||||
if (this.getSlot(token) === null) {
|
||||
this.slots[token] = new Slot(token, shortAliases, attributes, () =>
|
||||
this.triggerHook('onRobotUpdate'),
|
||||
);
|
||||
this.slots[token] = new Slot(token, shortAliases, attributes, () => {
|
||||
this.triggerHook('onRobotUpdate');
|
||||
});
|
||||
this.save();
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { sha256 } from 'js-sha256';
|
||||
import { Coordinator, Garage, Robot, type Order } from '.';
|
||||
import { type Coordinator, type Garage, Robot, type Order } from '.';
|
||||
import { roboidentitiesClient } from '../services/Roboidentities/Web';
|
||||
|
||||
class Slot {
|
||||
@ -13,12 +13,12 @@ class Slot {
|
||||
|
||||
this.hashId = sha256(sha256(this.token));
|
||||
this.nickname = null;
|
||||
roboidentitiesClient.generateRoboname(this.hashId).then((nickname) => {
|
||||
void roboidentitiesClient.generateRoboname(this.hashId).then((nickname) => {
|
||||
this.nickname = nickname;
|
||||
onRobotUpdate();
|
||||
});
|
||||
roboidentitiesClient.generateRobohash(this.hashId, 'small');
|
||||
roboidentitiesClient.generateRobohash(this.hashId, 'large');
|
||||
void roboidentitiesClient.generateRobohash(this.hashId, 'small');
|
||||
void roboidentitiesClient.generateRobohash(this.hashId, 'large');
|
||||
|
||||
this.robots = shortAliases.reduce((acc: Record<string, Robot>, shortAlias: string) => {
|
||||
acc[shortAlias] = new Robot(robotAttributes);
|
||||
@ -82,7 +82,7 @@ class Slot {
|
||||
pubKey: defaultRobot.pubKey,
|
||||
encPrivKey: defaultRobot.encPrivKey,
|
||||
});
|
||||
coordinator.fetchRobot(garage, defaultRobot.token);
|
||||
void coordinator.fetchRobot(garage, defaultRobot.token);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import RoboidentitiesClientNativeClient from './RoboidentitiesNativeClient';
|
||||
import { RoboidentitiesClient } from './type';
|
||||
import { type RoboidentitiesClient } from './type';
|
||||
|
||||
export const roboidentitiesClient: RoboidentitiesClient = new RoboidentitiesClientNativeClient();
|
||||
|
@ -31,8 +31,8 @@ class RoboidentitiesNativeClient implements RoboidentitiesClient {
|
||||
type: 'robohash',
|
||||
detail: key,
|
||||
});
|
||||
const result = response ? Object.values(response)[0] : '';
|
||||
const image = `data:image/png;base64,${result}`;
|
||||
const result: string = response ? Object.values(response)[0] : '';
|
||||
const image: string = `data:image/png;base64,${result}`;
|
||||
this.robohashes[key] = image;
|
||||
return image;
|
||||
}
|
||||
|
@ -4,14 +4,14 @@ import { robohash } from './RobohashGenerator';
|
||||
|
||||
class RoboidentitiesClientWebClient implements RoboidentitiesClient {
|
||||
public generateRoboname: (initialString: string) => Promise<string> = async (initialString) => {
|
||||
return new Promise<string>(async (resolve, _reject) => {
|
||||
return await new Promise<string>((resolve, _reject) => {
|
||||
resolve(generate_roboname(initialString));
|
||||
});
|
||||
};
|
||||
|
||||
public generateRobohash: (initialString: string, size: 'small' | 'large') => Promise<string> =
|
||||
async (initialString, size) => {
|
||||
return robohash.generate(initialString, size);
|
||||
return await robohash.generate(initialString, size);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import RoboidentitiesClientWebClient from './RoboidentitiesWebClient';
|
||||
import { RoboidentitiesClient } from './type';
|
||||
import { type RoboidentitiesClient } from './type';
|
||||
|
||||
export const roboidentitiesClient: RoboidentitiesClient = new RoboidentitiesClientWebClient();
|
||||
|
@ -5,7 +5,7 @@ import ApiWebClient from '../ApiWebClient';
|
||||
class ApiNativeClient implements ApiClient {
|
||||
public useProxy = true;
|
||||
|
||||
private webClient: ApiClient = new ApiWebClient();
|
||||
private readonly webClient: ApiClient = new ApiWebClient();
|
||||
|
||||
private readonly assetsPromises = new Map<string, Promise<string | undefined>>();
|
||||
|
||||
@ -55,7 +55,7 @@ class ApiNativeClient implements ApiClient {
|
||||
|
||||
public delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined> =
|
||||
async (baseUrl, path, auth) => {
|
||||
if (!this.useProxy) return this.webClient.delete(baseUrl, path, auth);
|
||||
if (!this.useProxy) return await this.webClient.delete(baseUrl, path, auth);
|
||||
return await window.NativeRobosats?.postMessage({
|
||||
category: 'http',
|
||||
type: 'delete',
|
||||
@ -71,7 +71,7 @@ class ApiNativeClient implements ApiClient {
|
||||
body: object,
|
||||
auth?: Auth,
|
||||
) => Promise<object | undefined> = async (baseUrl, path, body, auth) => {
|
||||
if (!this.useProxy) return this.webClient.post(baseUrl, path, body, auth);
|
||||
if (!this.useProxy) return await this.webClient.post(baseUrl, path, body, auth);
|
||||
return await window.NativeRobosats?.postMessage({
|
||||
category: 'http',
|
||||
type: 'post',
|
||||
@ -87,7 +87,7 @@ class ApiNativeClient implements ApiClient {
|
||||
path,
|
||||
auth,
|
||||
) => {
|
||||
if (!this.useProxy) return this.webClient.get(baseUrl, path, auth);
|
||||
if (!this.useProxy) return await this.webClient.get(baseUrl, path, auth);
|
||||
return await window.NativeRobosats?.postMessage({
|
||||
category: 'http',
|
||||
type: 'get',
|
||||
|
@ -16,14 +16,6 @@ export interface AggregatedInfo {
|
||||
version: Version;
|
||||
}
|
||||
|
||||
type toAdd =
|
||||
| 'num_public_buy_orders'
|
||||
| 'num_public_sell_orders'
|
||||
| 'book_liquidity'
|
||||
| 'active_robots_today'
|
||||
| 'last_day_volume'
|
||||
| 'lifetime_volume';
|
||||
|
||||
export const weightedMean = (arrValues: number[], arrWeights: number[]): number => {
|
||||
if (arrValues.length === 0) {
|
||||
return 0;
|
||||
|
@ -75,6 +75,7 @@
|
||||
"74": "IRT",
|
||||
"75": "BDT",
|
||||
"76": "ALL",
|
||||
"77": "DZD",
|
||||
"300": "XAU",
|
||||
"1000": "BTC"
|
||||
}
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "En disputa",
|
||||
"Collaboratively cancelled": "Cancel·lat col·laborativament",
|
||||
"Sending satoshis to buyer": "Enviant satoshis al comprador",
|
||||
"Sucessful trade": "Intercanvi exitós",
|
||||
"Successful trade": "Intercanvi exitós",
|
||||
"Failed lightning network routing": "L'enrrutament lightning network ha fallat",
|
||||
"Wait for dispute resolution": "Esperant la resolució de la disputa",
|
||||
"Maker lost dispute": "El creador ha perdut la disputa",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "En disputa",
|
||||
"Collaboratively cancelled": "Cancelada colaborativamente",
|
||||
"Sending satoshis to buyer": "Enviando Sats al comprador",
|
||||
"Sucessful trade": "Intercambio exitoso",
|
||||
"Successful trade": "Intercambio exitoso",
|
||||
"Failed lightning network routing": "Enrutamiento fallido en la red Lightning",
|
||||
"Wait for dispute resolution": "Espera a la resolución de la disputa",
|
||||
"Maker lost dispute": "El creador ha perdido la disputa",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "En litige",
|
||||
"Collaboratively cancelled": "Annulation commune",
|
||||
"Sending satoshis to buyer": "Envoi des satoshis à l'acheteur",
|
||||
"Sucessful trade": "Transaction réussie",
|
||||
"Successful trade": "Transaction réussie",
|
||||
"Failed lightning network routing": "Échec du routage du réseau lightning",
|
||||
"Wait for dispute resolution": "Attendre la résolution du litige",
|
||||
"Maker lost dispute": "Litige perdu par l'auteur",
|
||||
|
@ -32,7 +32,7 @@ phrases = OrderedDict(
|
||||
("In dispute", "In dispute"),
|
||||
("Collaboratively cancelled", "Collaboratively cancelled"),
|
||||
("Sending satoshis to buyer", "Sending satoshis to buyer"),
|
||||
("Sucessful trade", "Successful trade"),
|
||||
("Successful trade", "Successful trade"),
|
||||
("Failed lightning network routing", "Failed lightning network routing"),
|
||||
("Wait for dispute resolution", "Wait for dispute resolution"),
|
||||
("Maker lost dispute", "Maker lost dispute"),
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In contestazione",
|
||||
"Collaboratively cancelled": "Annullato collaborativamente",
|
||||
"Sending satoshis to buyer": "Invio satoshi all'acquirente",
|
||||
"Sucessful trade": "Scambio completato con successo",
|
||||
"Successful trade": "Scambio completato con successo",
|
||||
"Failed lightning network routing": "Routing Lightning Network fallito",
|
||||
"Wait for dispute resolution": "In attesa della risoluzione della contestazione",
|
||||
"Maker lost dispute": "Il maker ha perso la contestazione",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "紛争中",
|
||||
"Collaboratively cancelled": "共同でキャンセルされました",
|
||||
"Sending satoshis to buyer": "買い手にSatsを送信しています",
|
||||
"Sucessful trade": "成功した取引",
|
||||
"Successful trade": "成功した取引",
|
||||
"Failed lightning network routing": "ライトニングネットワークのルーティングに失敗しました",
|
||||
"Wait for dispute resolution": "紛争の解決を待ってください",
|
||||
"Maker lost dispute": "オーダーメイカーが紛争に負けました",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "В диспуте",
|
||||
"Collaboratively cancelled": "Совместно отменено",
|
||||
"Sending satoshis to buyer": "Отправка Сатоши покупателю",
|
||||
"Sucessful trade": "Успешная торговля",
|
||||
"Successful trade": "Успешная торговля",
|
||||
"Failed lightning network routing": "Неудачная маршрутизация сети Lightning",
|
||||
"Wait for dispute resolution": "Дождитесь разрешения диспута",
|
||||
"Maker lost dispute": "Мейкер проиграл диспут",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "Katika mzozo",
|
||||
"Collaboratively cancelled": "Kufutwa kwa ushirikiano",
|
||||
"Sending satoshis to buyer": "Inatumwa satoshis kwa mnunuzi",
|
||||
"Sucessful trade": "Biashara imefanikiwa",
|
||||
"Successful trade": "Biashara imefanikiwa",
|
||||
"Failed lightning network routing": "Utaratibu wa mtandao wa umeme umeshindwa",
|
||||
"Wait for dispute resolution": "Kusubiri suluhisho la mzozo",
|
||||
"Maker lost dispute": "Mtengenezaji amepoteza mzozo",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "In dispute",
|
||||
"Collaboratively cancelled": "Collaboratively cancelled",
|
||||
"Sending satoshis to buyer": "Sending satoshis to buyer",
|
||||
"Sucessful trade": "Successful trade",
|
||||
"Successful trade": "Successful trade",
|
||||
"Failed lightning network routing": "Failed lightning network routing",
|
||||
"Wait for dispute resolution": "Wait for dispute resolution",
|
||||
"Maker lost dispute": "Maker lost dispute",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "正在争议中",
|
||||
"Collaboratively cancelled": "已合作取消",
|
||||
"Sending satoshis to buyer": "正在向买方发送聪",
|
||||
"Sucessful trade": "成功交易",
|
||||
"Successful trade": "成功交易",
|
||||
"Failed lightning network routing": "闪电路由失败",
|
||||
"Wait for dispute resolution": "等待争议解决",
|
||||
"Maker lost dispute": "挂单方失去了争议",
|
||||
|
@ -700,7 +700,7 @@
|
||||
"In dispute": "正在爭議中",
|
||||
"Collaboratively cancelled": "已合作取消",
|
||||
"Sending satoshis to buyer": "正在向買方發送聰",
|
||||
"Sucessful trade": "成功交易",
|
||||
"Successful trade": "成功交易",
|
||||
"Failed lightning network routing": "閃電路由失敗",
|
||||
"Wait for dispute resolution": "等待爭議解決",
|
||||
"Maker lost dispute": "掛單方失去了爭議",
|
||||
|
2229
mobile/package-lock.json
generated
2229
mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/react-native": "^0.71.3",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^5.59.6",
|
||||
"babel-jest": "^29.7.0",
|
||||
"eslint": "^8.39.0",
|
||||
@ -35,13 +35,13 @@
|
||||
"eslint-import-resolver-typescript": "^3.6.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.7.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-promise": "^6.6.0",
|
||||
"eslint-plugin-react": "^7.35.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"jest": "^29.7.0",
|
||||
"metro-react-native-babel-preset": "^0.75.1",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier": "^3.3.3",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
django==5.0.6
|
||||
django==5.0.8
|
||||
django-admin-relation-links==0.2.5
|
||||
django-celery-beat==2.6.0
|
||||
django-celery-results==2.5.1
|
||||
@ -19,9 +19,9 @@ ring==0.10.1
|
||||
gunicorn==22.0.0
|
||||
psycopg2==2.9.9
|
||||
SQLAlchemy==2.0.16
|
||||
django-import-export==4.1.0
|
||||
django-import-export==4.1.1
|
||||
requests[socks]
|
||||
shapely==2.0.4
|
||||
shapely==2.0.5
|
||||
python-gnupg==0.5.2
|
||||
daphne==4.1.2
|
||||
drf-spectacular==0.27.2
|
||||
|
@ -1,4 +1,4 @@
|
||||
coverage==7.5.0
|
||||
ruff==0.5.1
|
||||
coverage==7.6.0
|
||||
ruff==0.5.7
|
||||
git+https://github.com/Reckless-Satoshi/drf-openapi-tester.git@soften-django-requirements
|
||||
pre-commit==3.7.0
|
||||
pre-commit==3.8.0
|
Loading…
Reference in New Issue
Block a user