refactoring: dex/trading
This commit is contained in:
parent
e9b6c353b2
commit
dbdfbd1ed6
80 changed files with 3453 additions and 3177 deletions
|
|
@ -1,17 +1,17 @@
|
|||
.content__preloader__wrapper {
|
||||
.loader {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
> div {
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
&__text {
|
||||
color: var(--font-dimmed-color);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import Preloader from '@/components/UI/Preloader/Preloader';
|
||||
import { classes } from '@/utils/utils';
|
||||
import styles from './ContentPreloader.module.scss';
|
||||
import { ContentPreloaderProps } from './types';
|
||||
|
||||
function ContentPreloader(props: { className?: string }) {
|
||||
function ContentPreloader({ className, style }: ContentPreloaderProps) {
|
||||
return (
|
||||
<div className={`${styles.content__preloader__wrapper} ${props.className}`}>
|
||||
<div>
|
||||
<div style={style} className={classes(styles.loader, className)}>
|
||||
<div className={styles.loader__content}>
|
||||
<Preloader />
|
||||
<p>Loading...</p>
|
||||
|
||||
<p className={styles.loder__text}>Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
6
src/components/UI/ContentPreloader/types.ts
Normal file
6
src/components/UI/ContentPreloader/types.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { CSSProperties } from 'react';
|
||||
|
||||
export interface ContentPreloaderProps {
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
16
src/components/UI/EmptyMessage/index.tsx
Normal file
16
src/components/UI/EmptyMessage/index.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
import { ReactComponent as NoOffersIcon } from '@/assets/images/UI/no_offers.svg';
|
||||
import { classes } from '@/utils/utils';
|
||||
import styles from './styles.module.scss';
|
||||
import { EmptyMessageProps } from './types';
|
||||
|
||||
const EmptyMessage = ({ text, customIcon }: EmptyMessageProps) => {
|
||||
return (
|
||||
<div className={classes(styles.empty, styles.all__orders__msg)}>
|
||||
{!customIcon ? <NoOffersIcon className={styles.empty__icon} /> : customIcon}
|
||||
<h6 className={styles.empty__text}>{text}</h6>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyMessage;
|
||||
17
src/components/UI/EmptyMessage/styles.module.scss
Normal file
17
src/components/UI/EmptyMessage/styles.module.scss
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.empty {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
|
||||
&__text {
|
||||
color: var(--font-dimmed-color);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
6
src/components/UI/EmptyMessage/types.ts
Normal file
6
src/components/UI/EmptyMessage/types.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
export interface EmptyMessageProps {
|
||||
text: string;
|
||||
customIcon?: ReactNode;
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
background: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
z-index: 5;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
@ -81,18 +81,4 @@
|
|||
border: 2px solid var(--window-bg-color);
|
||||
transition: background 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
// .input__range::-webkit-slider-runnable-track::-webkit-slider-thumb {
|
||||
// background-color: #fff;
|
||||
// border: 2px solid #555;
|
||||
// }
|
||||
|
||||
// .input__range::-webkit-slider-runnable-track {
|
||||
// -webkit-appearance: none;
|
||||
// height: 20px;
|
||||
// background-color: #1F8FEB;
|
||||
// border: 1px solid #ffffff;
|
||||
// box-shadow: none;
|
||||
// background: transparent;
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@
|
|||
position: relative;
|
||||
|
||||
&.lg {
|
||||
padding: 0 60px;
|
||||
padding-inline: 60px;
|
||||
|
||||
@media screen and (max-width: 1060px) {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
@media screen and (max-width: 1600px) {
|
||||
padding-inline: 40px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
padding-inline: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
36
src/constants/index.ts
Normal file
36
src/constants/index.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import PeriodState from '@/interfaces/states/pages/dex/trading/InputPanelItem/PeriodState';
|
||||
import SelectValue from '@/interfaces/states/pages/dex/trading/InputPanelItem/SelectValue';
|
||||
|
||||
export const periods: PeriodState[] = [
|
||||
{
|
||||
name: '1H',
|
||||
code: '1h',
|
||||
},
|
||||
{
|
||||
name: '1D',
|
||||
code: '1d',
|
||||
},
|
||||
{
|
||||
name: '1W',
|
||||
code: '1w',
|
||||
},
|
||||
{
|
||||
name: '1M',
|
||||
code: '1m',
|
||||
},
|
||||
];
|
||||
|
||||
export const buySellValues: SelectValue[] = [
|
||||
{
|
||||
name: 'All',
|
||||
code: 'all',
|
||||
},
|
||||
{
|
||||
name: 'Buy',
|
||||
code: 'buy',
|
||||
},
|
||||
{
|
||||
name: 'Sell',
|
||||
code: 'sell',
|
||||
},
|
||||
];
|
||||
22
src/hook/useAlert.ts
Normal file
22
src/hook/useAlert.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import AlertType from '@/interfaces/common/AlertType';
|
||||
import { Store } from '@/store/store-reducer';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const useAlert = () => {
|
||||
const { state, dispatch } = useContext(Store);
|
||||
|
||||
const setAlertState = (state: AlertType) => {
|
||||
dispatch({ type: 'ALERT_STATE_UPDATED', payload: state });
|
||||
};
|
||||
|
||||
const setAlertSubtitle = (subtitle: string) => {
|
||||
dispatch({ type: 'ALERT_SUBTITLE_UPDATED', payload: subtitle });
|
||||
};
|
||||
|
||||
return {
|
||||
alertState: state.alertState,
|
||||
alertSubtitle: state.alertSubtitle,
|
||||
setAlertState,
|
||||
setAlertSubtitle,
|
||||
};
|
||||
};
|
||||
21
src/hook/useMouseLeave.ts
Normal file
21
src/hook/useMouseLeave.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { ForwardedRef, useEffect } from 'react';
|
||||
|
||||
const useMouseLeave = (ref: ForwardedRef<HTMLElement>, callbackFn: () => void) => {
|
||||
useEffect(() => {
|
||||
const targetEl = (event: MouseEvent) => {
|
||||
if (ref && typeof ref !== 'function' && ref.current) {
|
||||
if (ref?.current && !ref?.current.contains(event.target as Node)) {
|
||||
callbackFn();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', targetEl);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', targetEl);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useMouseLeave;
|
||||
20
src/hook/useScroll.ts
Normal file
20
src/hook/useScroll.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { useCallback, useRef } from 'react';
|
||||
|
||||
function useScroll<T extends HTMLElement>() {
|
||||
const elementRef = useRef<T | null>(null);
|
||||
|
||||
const scrollToElement = useCallback((options?: ScrollIntoViewOptions) => {
|
||||
if (elementRef.current) {
|
||||
elementRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
inline: 'nearest',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { elementRef, scrollToElement };
|
||||
}
|
||||
|
||||
export default useScroll;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { Dispatch } from 'react';
|
||||
import { GetUserResData } from '../responses/user/GetUserRes';
|
||||
import { GetConfigResData } from '../responses/config/GetConfigRes';
|
||||
import AlertType from './AlertType';
|
||||
|
||||
export interface Asset {
|
||||
name: string;
|
||||
|
|
@ -50,6 +51,8 @@ interface ContextState {
|
|||
offers: number;
|
||||
};
|
||||
closed_notifications: number[];
|
||||
alertState: AlertType;
|
||||
alertSubtitle: string;
|
||||
}
|
||||
|
||||
type ContextAction =
|
||||
|
|
@ -75,6 +78,14 @@ type ContextAction =
|
|||
| {
|
||||
type: 'CLOSED_NOTIFICATIONS_UPDATED';
|
||||
payload: number[];
|
||||
}
|
||||
| {
|
||||
type: 'ALERT_STATE_UPDATED';
|
||||
payload: AlertType;
|
||||
}
|
||||
| {
|
||||
type: 'ALERT_SUBTITLE_UPDATED';
|
||||
payload: string;
|
||||
};
|
||||
|
||||
interface ContextValue {
|
||||
|
|
|
|||
6
src/interfaces/common/MatrixAddress.ts
Normal file
6
src/interfaces/common/MatrixAddress.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
interface MatrixAddress {
|
||||
address: string;
|
||||
registered: boolean;
|
||||
}
|
||||
|
||||
export default MatrixAddress;
|
||||
24
src/interfaces/common/orderFormOutput.ts
Normal file
24
src/interfaces/common/orderFormOutput.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
interface OrderFormOutput {
|
||||
price: string;
|
||||
amount: string;
|
||||
total: string;
|
||||
priceValid: boolean;
|
||||
amountValid: boolean;
|
||||
totalValid: boolean;
|
||||
totalUsd: string | undefined;
|
||||
rangeInputValue: string;
|
||||
setRangeInputValue: Dispatch<SetStateAction<string>>;
|
||||
onPriceChange: (_inputValue: string) => void;
|
||||
onAmountChange: (_inputValue: string) => void;
|
||||
resetForm: () => void;
|
||||
setTotal: Dispatch<SetStateAction<string>>;
|
||||
setPrice: Dispatch<SetStateAction<string>>;
|
||||
setAmount: Dispatch<SetStateAction<string>>;
|
||||
setPriceValid: Dispatch<SetStateAction<boolean>>;
|
||||
setAmountValid: Dispatch<SetStateAction<boolean>>;
|
||||
setTotalValid: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export default OrderFormOutput;
|
||||
|
|
@ -1,22 +1,20 @@
|
|||
import AlertType from '@/interfaces/common/AlertType';
|
||||
import SelectValue from '@/interfaces/states/pages/dex/trading/InputPanelItem/SelectValue';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
interface InputPanelItemProps {
|
||||
currencyNames: {
|
||||
firstCurrencyName: string;
|
||||
secondCurrencyName: string;
|
||||
};
|
||||
priceState: string;
|
||||
amountState: string;
|
||||
totalState: string;
|
||||
buySellValues: SelectValue[];
|
||||
buySellState: SelectValue;
|
||||
// setBuySellState: Dispatch<SetStateAction<SelectValue>>;
|
||||
setPriceFunction: (_value: string) => void;
|
||||
setAmountFunction: (_value: string) => void;
|
||||
setAlertState: Dispatch<SetStateAction<AlertType>>;
|
||||
setAlertSubtitle: Dispatch<SetStateAction<string>>;
|
||||
setRangeInputValue: Dispatch<SetStateAction<string>>;
|
||||
rangeInputValue: string;
|
||||
firstCurrencyName: string;
|
||||
secondCurrencyName: string;
|
||||
balance: number | undefined;
|
||||
amountValid: boolean;
|
||||
priceValid: boolean;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ interface StatItemProps {
|
|||
title: string;
|
||||
value: string;
|
||||
coefficient?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default StatItemProps;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import '@/styles/globals.scss';
|
||||
import '@/styles/themes/light.scss';
|
||||
import '@/styles/themes/dark.scss';
|
||||
import Head from 'next/head';
|
||||
import { StoreProvider } from '@/store/store-reducer';
|
||||
import NextApp, { AppContext, AppProps } from 'next/app';
|
||||
|
|
|
|||
|
|
@ -1,243 +0,0 @@
|
|||
.input_panel__item {
|
||||
width: 100%;
|
||||
|
||||
&_header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 10px;
|
||||
|
||||
h5 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.input_panel__fees {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
|
||||
p {
|
||||
color: var(--table-th-color);
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&_body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
> .buy_btn {
|
||||
background-color: #16d1d6;
|
||||
|
||||
&:hover {
|
||||
background-color: #45dade;
|
||||
}
|
||||
}
|
||||
|
||||
> .sell_btn {
|
||||
background-color: #ff6767;
|
||||
|
||||
&:hover {
|
||||
background-color: #ff8585;
|
||||
}
|
||||
}
|
||||
|
||||
.input_panel__range {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.input_panel__expiration {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
|
||||
h6 {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.expiration__dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1500px) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 530px) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.labeled_input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
h6 {
|
||||
font-size: 11px;
|
||||
font-family: 700;
|
||||
color: var(--table-th-color);
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background-color: var(--bordered-input-bg);
|
||||
border: 1px solid var(--window-border-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 13px 15px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.labeled_input__value {
|
||||
padding-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> p {
|
||||
color: var(--table-th-color);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.labeled_input__currency {
|
||||
min-width: 82px;
|
||||
max-width: 150px;
|
||||
padding: 0 15px;
|
||||
background-color: var(--dex-input-currency);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
> p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.labeled_input__invalid > div {
|
||||
border-color: #ff6767;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 430px) {
|
||||
> div {
|
||||
input,
|
||||
.labeled_input__value > p,
|
||||
.labeled_input__currency > p {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 19px 15px;
|
||||
}
|
||||
|
||||
.labeled_input__currency {
|
||||
min-width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buy-sell-switch {
|
||||
padding: 3px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 100px;
|
||||
border: 1px solid var(--dex-buy-sell-border);
|
||||
|
||||
.buy-sell-switch__item {
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border-radius: 100px;
|
||||
|
||||
&.item_selected-buy {
|
||||
background-color: #16d1d6;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&.item_selected-sell {
|
||||
background-color: #ff6767;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.apply__alert {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
max-width: 125px;
|
||||
background-color: var(--alert-btn-bg);
|
||||
color: #1f8feb;
|
||||
padding: 7px 32px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--alert-btn-hover);
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
.orders-buy-sell-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background-color: transparent !important;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> p {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #16d1d6;
|
||||
}
|
||||
|
||||
> svg > * {
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
&.orders-buy-sell-switch_sell {
|
||||
> p {
|
||||
color: #ff6767;
|
||||
}
|
||||
|
||||
> svg > * {
|
||||
stroke: #ff6767;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import HorizontalSelectProps from '@/interfaces/props/components/UI/HorizontalSelect/HorizontalSelectProps';
|
||||
import { ReactComponent as ArrowIcon } from '@/assets/images/UI/trade_arrow.svg';
|
||||
import SelectValue from '@/interfaces/states/pages/dex/trading/InputPanelItem/SelectValue';
|
||||
import { classes } from '@/utils/utils';
|
||||
import styles from './OrdersBuySellSwitch.module.scss';
|
||||
|
||||
export default function OrdersBuySellSwitch({
|
||||
body,
|
||||
value,
|
||||
setValue,
|
||||
className,
|
||||
}: HorizontalSelectProps<SelectValue>) {
|
||||
const defaultValue = body[0];
|
||||
|
||||
const buyValue = body.find((e) => e.code === 'buy');
|
||||
const sellValue = body.find((e) => e.code === 'sell');
|
||||
|
||||
const isBuy = value.code === 'buy';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classes(
|
||||
styles['orders-buy-sell-switch'],
|
||||
isBuy ? styles['orders-buy-sell-switch_sell'] : undefined,
|
||||
className,
|
||||
)}
|
||||
onClick={() => setValue((isBuy ? sellValue : buyValue) || defaultValue)}
|
||||
>
|
||||
<p>To {isBuy ? 'Sell' : 'Buy'} Orders</p>
|
||||
<ArrowIcon />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
82
src/pages/dex/trading/components/AllTrades/index.tsx
Normal file
82
src/pages/dex/trading/components/AllTrades/index.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import React from 'react';
|
||||
import { classes, formatTime } from '@/utils/utils';
|
||||
import ContentPreloader from '@/components/UI/ContentPreloader/ContentPreloader';
|
||||
import EmptyMessage from '@/components/UI/EmptyMessage';
|
||||
import styles from './styles.module.scss';
|
||||
import { AllTradesProps } from './types';
|
||||
|
||||
const AllTrades = ({
|
||||
setTradesType,
|
||||
tradesType,
|
||||
filteredTrades,
|
||||
tradesLoading,
|
||||
currencyNames,
|
||||
}: AllTradesProps) => {
|
||||
return (
|
||||
<div className={styles.allTrades}>
|
||||
<div className={styles.allTrades__header}>
|
||||
<button
|
||||
onClick={() => setTradesType('all')}
|
||||
className={classes(
|
||||
styles.allTrades__header_btn,
|
||||
tradesType === 'all' && styles.active,
|
||||
)}
|
||||
>
|
||||
All Trades
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setTradesType('my')}
|
||||
className={classes(
|
||||
styles.allTrades__header_btn,
|
||||
tradesType === 'my' && styles.active,
|
||||
)}
|
||||
>
|
||||
My Trades
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles.allTrades__content}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Price ({currencyNames.secondCurrencyName})</th>
|
||||
<th>Amount ({currencyNames.firstCurrencyName})</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{!tradesLoading && !!filteredTrades.length && (
|
||||
<tbody className="orders-scroll">
|
||||
{filteredTrades.map((trade) => (
|
||||
<tr key={trade.id}>
|
||||
<td>
|
||||
<p
|
||||
style={{
|
||||
color: trade.id % 2 === 0 ? '#16D1D6' : '#FF6767',
|
||||
}}
|
||||
>
|
||||
{trade.price}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>{trade.amount}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>{formatTime(trade.timestamp)}</p>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
|
||||
{!filteredTrades.length && !tradesLoading && <EmptyMessage text="No trades" />}
|
||||
|
||||
{tradesLoading && <ContentPreloader style={{ marginTop: 40 }} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AllTrades;
|
||||
116
src/pages/dex/trading/components/AllTrades/styles.module.scss
Normal file
116
src/pages/dex/trading/components/AllTrades/styles.module.scss
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
.allTrades {
|
||||
max-width: 415px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
@media screen and (max-width: 1480px) {
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22px;
|
||||
padding: 10px;
|
||||
padding-bottom: 0;
|
||||
|
||||
&_btn {
|
||||
padding-bottom: 7px;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
font-weight: 600;
|
||||
background-color: transparent !important;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-color: #1f8feb;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1f8feb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 10px;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-inline: 10px;
|
||||
margin-bottom: 9px;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
th {
|
||||
min-width: 80px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: start;
|
||||
color: var(--table-th-color);
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
height: 29dvh;
|
||||
min-height: 265px;
|
||||
max-height: 380px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
tr {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: var(--table-even-bg);
|
||||
}
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
> p {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
> p {
|
||||
min-width: 80px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/pages/dex/trading/components/AllTrades/types.ts
Normal file
15
src/pages/dex/trading/components/AllTrades/types.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Trade } from '@/interfaces/responses/trades/GetTradeRes';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
type tradeType = 'all' | 'my';
|
||||
|
||||
export interface AllTradesProps {
|
||||
setTradesType: Dispatch<SetStateAction<tradeType>>;
|
||||
tradesType: tradeType;
|
||||
filteredTrades: Trade[];
|
||||
tradesLoading: boolean;
|
||||
currencyNames: {
|
||||
firstCurrencyName: string;
|
||||
secondCurrencyName: string;
|
||||
};
|
||||
}
|
||||
26
src/pages/dex/trading/components/BadgeStatus/index.tsx
Normal file
26
src/pages/dex/trading/components/BadgeStatus/index.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { classes } from '@/utils/utils';
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
import LightningImg from '@/assets/images/UI/lightning.png';
|
||||
import RocketImg from '@/assets/images/UI/rocket.png';
|
||||
import styles from './styles.module.scss';
|
||||
import { BadgeStatusProps } from './types';
|
||||
|
||||
function BadgeStatus({ type, icon }: BadgeStatusProps) {
|
||||
return (
|
||||
<div className={classes(styles.badge, type === 'high' && styles.high, icon && styles.icon)}>
|
||||
<Image
|
||||
className={styles.badge__img}
|
||||
src={type === 'instant' ? LightningImg : RocketImg}
|
||||
alt="badge image"
|
||||
/>
|
||||
{!icon && (
|
||||
<span className={styles.badge__text}>
|
||||
{type === 'instant' ? 'instant' : 'high volume'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BadgeStatus;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
.badge {
|
||||
width: fit-content;
|
||||
padding: 1px 4px;
|
||||
padding-right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: 100px;
|
||||
background: radial-gradient(100% 246.57% at 0% 0%, #a366ff 0%, #601fff 100%);
|
||||
|
||||
&.high {
|
||||
padding: 2px 4px;
|
||||
background: radial-gradient(100% 188.88% at 0% 0%, #16d1d6 0%, #274cff 100%);
|
||||
}
|
||||
|
||||
&.icon {
|
||||
min-width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
|
||||
.badge__img {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
&__img {
|
||||
height: 13px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
4
src/pages/dex/trading/components/BadgeStatus/types.ts
Normal file
4
src/pages/dex/trading/components/BadgeStatus/types.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export interface BadgeStatusProps {
|
||||
type?: 'instant' | 'high';
|
||||
icon?: boolean;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import Decimal from 'decimal.js';
|
|||
import * as echarts from 'echarts';
|
||||
import CandleRow from '@/interfaces/common/CandleRow';
|
||||
import testCandles from './testCandles.json';
|
||||
import styles from './CandleChart.module.scss';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
const TESTING_MODE = false;
|
||||
|
||||
|
|
@ -214,7 +214,7 @@ function CandleChart(props: CandleChartProps) {
|
|||
console.log('option', option);
|
||||
|
||||
return (
|
||||
<div className={styles.candle__chart__wrapper}>
|
||||
<div className={styles.chart}>
|
||||
<ReactECharts
|
||||
option={option}
|
||||
style={{
|
||||
|
|
@ -229,7 +229,9 @@ function CandleChart(props: CandleChartProps) {
|
|||
ref={chartRef}
|
||||
/>
|
||||
|
||||
{!candles?.length && isLoaded && <h1>[ Low volume ]</h1>}
|
||||
{!candles?.length && isLoaded && (
|
||||
<h1 className={styles.chart__lowVolume}>[ Low volume ]</h1>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.candle__chart__wrapper {
|
||||
.chart {
|
||||
position: relative;
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
cursor: crosshair;
|
||||
}
|
||||
|
||||
h1 {
|
||||
&__lowVolume {
|
||||
font-size: 72px;
|
||||
color: var(--font-dimmed-color);
|
||||
white-space: nowrap;
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import LabeledInputProps from '@/interfaces/props/pages/dex/trading/InputPanelItem/LabeledInputProps';
|
||||
import { classes, formatDollarValue } from '@/utils/utils';
|
||||
import { useRef } from 'react';
|
||||
import Input from '@/components/UI/Input/Input';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
function LabeledInput(props: LabeledInputProps) {
|
||||
const labelRef = useRef<HTMLParagraphElement>(null);
|
||||
const {
|
||||
label = '',
|
||||
placeholder = '',
|
||||
currency = '',
|
||||
value,
|
||||
readonly,
|
||||
usd,
|
||||
setValue,
|
||||
invalid,
|
||||
} = props;
|
||||
|
||||
const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
if (!readonly && setValue) {
|
||||
setValue(e.currentTarget.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.labeledInput}>
|
||||
<h6 className={styles.labeledInput__label}>{label}</h6>
|
||||
<div className={classes(styles.labeledInput__wrapper, invalid && styles.invalid)}>
|
||||
<Input
|
||||
bordered
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
readOnly={readonly}
|
||||
onInput={handleInput}
|
||||
/>
|
||||
|
||||
{usd && (
|
||||
<div className={styles.labeledInput__value}>
|
||||
<p>~${formatDollarValue(usd)}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.labeledInput__currency} ref={labelRef}>
|
||||
<p>{currency}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LabeledInput;
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
.labeledInput {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
&__label {
|
||||
font-size: 11px;
|
||||
font-family: 700;
|
||||
color: var(--table-th-color);
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background-color: var(--bordered-input-bg);
|
||||
border: 1px solid var(--window-border-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
&.invalid {
|
||||
border-color: #ff6767;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 13px 15px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
&__value {
|
||||
padding-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> p {
|
||||
color: var(--table-th-color);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
&__currency {
|
||||
min-width: 82px;
|
||||
max-width: 150px;
|
||||
padding: 0 15px;
|
||||
background-color: var(--dex-input-currency);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
> p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,22 @@
|
|||
import { Store } from '@/store/store-reducer';
|
||||
import { createOrder } from '@/utils/methods';
|
||||
import { ChangeEvent, useContext, useRef, useState } from 'react';
|
||||
import Input from '@/components/UI/Input/Input';
|
||||
import { ChangeEvent, useContext, useState } from 'react';
|
||||
import RangeInput from '@/components/UI/RangeInput/RangeInput';
|
||||
import ConnectButton from '@/components/UI/ConnectButton/ConnectButton';
|
||||
import Button from '@/components/UI/Button/Button';
|
||||
import { useRouter } from 'next/router';
|
||||
import { classes, formatDollarValue } from '@/utils/utils';
|
||||
import { classes } from '@/utils/utils';
|
||||
import InputPanelItemProps from '@/interfaces/props/pages/dex/trading/InputPanelItem/InputPanelItemProps';
|
||||
import LabeledInputProps from '@/interfaces/props/pages/dex/trading/InputPanelItem/LabeledInputProps';
|
||||
import CreateOrderData from '@/interfaces/fetch-data/create-order/CreateOrderData';
|
||||
import Decimal from 'decimal.js';
|
||||
import Alert from '@/components/UI/Alert/Alert';
|
||||
import infoIcon from '@/assets/images/UI/info_alert_icon.svg';
|
||||
import Image from 'next/image';
|
||||
import styles from './InputPanelItem.module.scss';
|
||||
import { useAlert } from '@/hook/useAlert';
|
||||
import styles from './styles.module.scss';
|
||||
import LabeledInput from './components/LabeledInput';
|
||||
|
||||
function InputPanelItem(props: InputPanelItemProps) {
|
||||
const { state } = useContext(Store);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
priceState = '',
|
||||
amountState = '',
|
||||
|
|
@ -29,71 +25,32 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
buySellState = buySellValues[0],
|
||||
setPriceFunction,
|
||||
setAmountFunction,
|
||||
setAlertState,
|
||||
setAlertSubtitle,
|
||||
setRangeInputValue,
|
||||
rangeInputValue = '50',
|
||||
firstCurrencyName = '',
|
||||
secondCurrencyName = '',
|
||||
balance = 0,
|
||||
amountValid,
|
||||
priceValid,
|
||||
totalValid,
|
||||
totalUsd,
|
||||
scrollToOrderList,
|
||||
currencyNames,
|
||||
} = props;
|
||||
|
||||
const { state } = useContext(Store);
|
||||
const router = useRouter();
|
||||
const { setAlertState, setAlertSubtitle } = useAlert();
|
||||
const [creatingState, setCreatingState] = useState(false);
|
||||
const { firstCurrencyName, secondCurrencyName } = currencyNames;
|
||||
|
||||
const [hasImmediateMatch, setHasImmediateMatch] = useState(false);
|
||||
|
||||
function LabeledInput(props: LabeledInputProps) {
|
||||
const labelRef = useRef<HTMLParagraphElement>(null);
|
||||
const {
|
||||
label = '',
|
||||
placeholder = '',
|
||||
currency = '',
|
||||
value,
|
||||
readonly,
|
||||
usd,
|
||||
setValue,
|
||||
invalid,
|
||||
} = props;
|
||||
|
||||
const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
if (!readonly && setValue) {
|
||||
setValue(e.currentTarget.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes(styles.labeled_input, invalid && styles.labeled_input__invalid)}
|
||||
>
|
||||
<h6>{label}</h6>
|
||||
<div>
|
||||
<Input
|
||||
bordered
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
readOnly={readonly}
|
||||
onInput={handleInput}
|
||||
/>
|
||||
{usd && (
|
||||
<div className={styles.labeled_input__value}>
|
||||
<p>~${formatDollarValue(usd)}</p>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.labeled_input__currency} ref={labelRef}>
|
||||
<p>{currency}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const isBuy = buySellState?.code === 'buy';
|
||||
|
||||
function resetForm() {
|
||||
setPriceFunction('');
|
||||
setAmountFunction('');
|
||||
setRangeInputValue('50');
|
||||
}
|
||||
|
||||
async function postOrder() {
|
||||
const price = new Decimal(priceState);
|
||||
const amount = new Decimal(amountState);
|
||||
|
|
@ -122,6 +79,8 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
if (result.data?.immediateMatch) {
|
||||
setHasImmediateMatch(true);
|
||||
}
|
||||
|
||||
resetForm();
|
||||
} else {
|
||||
setAlertState('error');
|
||||
if (result.data === 'Same order') {
|
||||
|
|
@ -139,35 +98,30 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
|
||||
function onRangeInput(e: ChangeEvent<HTMLInputElement>) {
|
||||
setRangeInputValue(e.target.value);
|
||||
if (balance) {
|
||||
if (balance > 0) {
|
||||
const rangeValue = new Decimal(e.target.value || '0');
|
||||
const balanceDecimal = new Decimal(balance || '0');
|
||||
const balanceDecimal = new Decimal(balance);
|
||||
const calculatedAmount = balanceDecimal.mul(rangeValue.div(100)).toString();
|
||||
setAmountFunction(calculatedAmount || '');
|
||||
}
|
||||
}
|
||||
|
||||
let buttonText;
|
||||
|
||||
if (creatingState) {
|
||||
buttonText = 'Creating...';
|
||||
} else {
|
||||
buttonText = 'Create Order';
|
||||
}
|
||||
const buttonText = creatingState ? 'Creating...' : 'Create Order';
|
||||
const isButtonDisabled = !priceValid || !amountValid || !totalValid || creatingState;
|
||||
|
||||
return (
|
||||
<div className={styles.input_panel__item}>
|
||||
<div className={styles.inputPanel}>
|
||||
{hasImmediateMatch && (
|
||||
<Alert
|
||||
type="custom"
|
||||
customContent={
|
||||
<div className={styles.apply__alert}>
|
||||
<div className={styles.applyAlert}>
|
||||
<Image src={infoIcon} alt="success" width={64} height={64} />
|
||||
<div className={styles.apply__alert__content}>
|
||||
<div className={styles.applyAlert__content}>
|
||||
<h2>Apply the order</h2>
|
||||
<p>You have to apply the order</p>
|
||||
<Button
|
||||
className={styles.apply__alert__button}
|
||||
className={styles.applyAlert__button}
|
||||
onClick={() => {
|
||||
scrollToOrderList();
|
||||
setHasImmediateMatch(false);
|
||||
|
|
@ -182,19 +136,17 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
/>
|
||||
)}
|
||||
|
||||
<div className={styles.input_panel__item_header}>
|
||||
<h5>
|
||||
<div className={styles.inputPanel__header}>
|
||||
<h5 className={styles.title}>
|
||||
{isBuy ? 'Buy' : 'Sell'} {secondCurrencyName}
|
||||
</h5>
|
||||
|
||||
<div className={styles.input_panel__fees}>
|
||||
<p>
|
||||
Fee: <span>0.01 Zano</span>
|
||||
</p>
|
||||
</div>
|
||||
<p className={styles.inputPanel__fees}>
|
||||
Fee: <span>0.01 Zano</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.input_panel__item_body}>
|
||||
<div className={styles.inputPanel__body}>
|
||||
{LabeledInput({
|
||||
value: priceState,
|
||||
setValue: setPriceFunction,
|
||||
|
|
@ -203,6 +155,7 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
label: 'Price',
|
||||
invalid: !!priceState && !priceValid,
|
||||
})}
|
||||
|
||||
{LabeledInput({
|
||||
value: amountState,
|
||||
setValue: setAmountFunction,
|
||||
|
|
@ -211,7 +164,9 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
label: 'Amount',
|
||||
invalid: !!amountState && !amountValid,
|
||||
})}
|
||||
|
||||
<RangeInput value={rangeInputValue} onInput={onRangeInput} />
|
||||
|
||||
{LabeledInput({
|
||||
value: totalState,
|
||||
setValue: () => undefined,
|
||||
|
|
@ -224,14 +179,22 @@ function InputPanelItem(props: InputPanelItemProps) {
|
|||
})}
|
||||
{state.wallet?.connected ? (
|
||||
<Button
|
||||
disabled={!priceValid || !amountValid || !totalValid}
|
||||
disabled={isButtonDisabled}
|
||||
onClick={postOrder}
|
||||
className={isBuy ? styles.buy_btn : styles.sell_btn}
|
||||
className={classes(
|
||||
styles.inputPanel__body_btn,
|
||||
isBuy ? styles.buy : styles.sell,
|
||||
)}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
) : (
|
||||
<ConnectButton className={isBuy ? styles.buy_btn : styles.sell_btn} />
|
||||
<ConnectButton
|
||||
className={classes(
|
||||
styles.inputPanel__body_btn,
|
||||
isBuy ? styles.buy : styles.sell,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
.inputPanel {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 10px;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&__fees {
|
||||
color: var(--table-th-color);
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
|
||||
span {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
&_btn {
|
||||
margin-top: 10px;
|
||||
|
||||
&.buy {
|
||||
background-color: #16d1d6;
|
||||
|
||||
&:hover {
|
||||
background-color: #45dade;
|
||||
}
|
||||
}
|
||||
|
||||
&.sell {
|
||||
background-color: #ff6767;
|
||||
|
||||
&:hover {
|
||||
background-color: #ff8585;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.applyAlert {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
max-width: 125px;
|
||||
background-color: var(--alert-btn-bg);
|
||||
color: #1f8feb;
|
||||
padding: 7px 32px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--alert-btn-hover);
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import Tooltip from '@/components/UI/Tooltip/Tooltip';
|
||||
import { useState } from 'react';
|
||||
import { ReactComponent as ConnectionIcon } from '@/assets/images/UI/connection.svg';
|
||||
import styles from './styles.module.scss';
|
||||
import { MatrixConnectionBadgeProps } from './types';
|
||||
|
||||
function MatrixConnectionBadge({
|
||||
userAdress,
|
||||
userAlias,
|
||||
matrixAddresses,
|
||||
}: MatrixConnectionBadgeProps) {
|
||||
const hasConnection = (address: string) =>
|
||||
matrixAddresses.some((item) => item.address === address && item.registered);
|
||||
|
||||
const [connectionTooltip, setConnectionTooltip] = useState(false);
|
||||
return userAdress && hasConnection(userAdress) ? (
|
||||
<div className={styles.badge}>
|
||||
<a
|
||||
href={`https://matrix.to/#/@${userAlias}:zano.org`}
|
||||
target="_blank"
|
||||
onMouseEnter={() => setConnectionTooltip(true)}
|
||||
onMouseLeave={() => setConnectionTooltip(false)}
|
||||
className={styles.badge__link}
|
||||
>
|
||||
<ConnectionIcon />
|
||||
</a>
|
||||
|
||||
<Tooltip
|
||||
className={styles.badge__tooltip}
|
||||
arrowClass={styles.badge__tooltip_arrow}
|
||||
shown={connectionTooltip}
|
||||
>
|
||||
<p className={styles.badge__tooltip_text}>Matrix connection</p>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
|
||||
export default MatrixConnectionBadge;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
.badge {
|
||||
position: relative;
|
||||
|
||||
&__link {
|
||||
margin-top: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__tooltip {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: var(--trade-table-tooltip);
|
||||
font-size: 12px;
|
||||
z-index: 9999;
|
||||
|
||||
&_text {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
&_arrow {
|
||||
border-radius: 2px;
|
||||
left: 50%;
|
||||
background-color: var(--trade-table-tooltip) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import MatrixAddress from '@/interfaces/common/MatrixAddress';
|
||||
|
||||
export interface MatrixConnectionBadgeProps {
|
||||
userAdress?: string;
|
||||
userAlias?: string;
|
||||
matrixAddresses: MatrixAddress[];
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import Tooltip from '@/components/UI/Tooltip/Tooltip';
|
||||
import { useState } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import { OrderRowTooltipCellProps } from './types';
|
||||
|
||||
function OrderRowTooltipCell({
|
||||
style,
|
||||
children,
|
||||
sideText,
|
||||
sideTextColor,
|
||||
noTooltip,
|
||||
}: OrderRowTooltipCellProps) {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
const tooltipText = `${children}${sideText ? ` ~${sideText}` : ''}`;
|
||||
|
||||
const isLongContent = tooltipText.length > 14;
|
||||
|
||||
return (
|
||||
<td className={styles.row}>
|
||||
<p
|
||||
style={style}
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
>
|
||||
{children}
|
||||
{sideText && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '15px',
|
||||
margin: 0,
|
||||
color: sideTextColor || 'var(--font-dimmed-color)',
|
||||
}}
|
||||
>
|
||||
{sideText}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
{isLongContent && !noTooltip && (
|
||||
<Tooltip
|
||||
className={styles.tooltip}
|
||||
arrowClass={styles.tooltip__arrow}
|
||||
shown={showTooltip}
|
||||
>
|
||||
{tooltipText}
|
||||
</Tooltip>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default OrderRowTooltipCell;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
.row {
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
> p {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
> p {
|
||||
min-width: 80px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 20%;
|
||||
transform: translateX(-50%);
|
||||
background-color: var(--trade-table-tooltip);
|
||||
|
||||
&__arrow {
|
||||
background-color: var(--trade-table-tooltip);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
export interface OrderRowTooltipCellProps {
|
||||
style?: React.CSSProperties;
|
||||
children: string | ReactNode;
|
||||
sideText?: string;
|
||||
sideTextColor?: string;
|
||||
noTooltip?: boolean;
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import React from 'react';
|
||||
import { classes, notationToString } from '@/utils/utils';
|
||||
import { nanoid } from 'nanoid';
|
||||
import Decimal from 'decimal.js';
|
||||
import styles from './styles.module.scss';
|
||||
import { OrdersRowProps } from './types';
|
||||
import OrderRowTooltipCell from '../../../OrderRowTooltipCell';
|
||||
|
||||
function OrdersRow({
|
||||
orderData,
|
||||
percentage,
|
||||
takeOrderClick,
|
||||
setOrdersInfoTooltip,
|
||||
}: OrdersRowProps) {
|
||||
const e = orderData || {};
|
||||
|
||||
const totalDecimal = new Decimal(e.left).mul(new Decimal(e.price));
|
||||
|
||||
return (
|
||||
<tr
|
||||
onMouseEnter={() => setOrdersInfoTooltip(e)}
|
||||
onClick={(event) => takeOrderClick(event, e)}
|
||||
className={classes(styles.row, e.type === 'sell' && styles.sell_section)}
|
||||
style={{ '--line-width': `${percentage}%` } as React.CSSProperties}
|
||||
key={nanoid(16)}
|
||||
>
|
||||
<OrderRowTooltipCell
|
||||
style={{
|
||||
color: e.type === 'buy' ? '#16D1D6' : '#FF6767',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '8px',
|
||||
maxWidth: 'max-content',
|
||||
}}
|
||||
>
|
||||
{notationToString(e.price)}
|
||||
</OrderRowTooltipCell>
|
||||
<OrderRowTooltipCell>{notationToString(e.amount)}</OrderRowTooltipCell>
|
||||
<OrderRowTooltipCell
|
||||
noTooltip
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '8px',
|
||||
maxWidth: 'max-content',
|
||||
}}
|
||||
>
|
||||
{notationToString(totalDecimal.toString())}
|
||||
</OrderRowTooltipCell>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default OrdersRow;
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
.row {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: var(--table-even-bg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: var(--line-width, 0%);
|
||||
height: 100%;
|
||||
background: #16d1d61a;
|
||||
}
|
||||
|
||||
&.sell_section {
|
||||
&::after {
|
||||
background: #ff67671a;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
> p {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
> p {
|
||||
min-width: 80px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
export interface OrdersRowProps {
|
||||
orderData: PageOrderData;
|
||||
percentage: number;
|
||||
takeOrderClick: (
|
||||
_event:
|
||||
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
||||
| React.MouseEvent<HTMLTableRowElement, MouseEvent>,
|
||||
_e: PageOrderData,
|
||||
) => void;
|
||||
setOrdersInfoTooltip: Dispatch<SetStateAction<PageOrderData | null>>;
|
||||
}
|
||||
198
src/pages/dex/trading/components/OrdersPool/index.tsx
Normal file
198
src/pages/dex/trading/components/OrdersPool/index.tsx
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
import React, { useRef, useState } from 'react';
|
||||
import { classes, cutAddress, formatDollarValue, notationToString } from '@/utils/utils';
|
||||
import { nanoid } from 'nanoid';
|
||||
import Decimal from 'decimal.js';
|
||||
import Tooltip from '@/components/UI/Tooltip/Tooltip';
|
||||
import ContentPreloader from '@/components/UI/ContentPreloader/ContentPreloader';
|
||||
import { buySellValues } from '@/constants';
|
||||
import EmptyMessage from '@/components/UI/EmptyMessage';
|
||||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import useMouseLeave from '@/hook/useMouseLeave';
|
||||
import OrdersRow from './components/OrdersRow';
|
||||
import styles from './styles.module.scss';
|
||||
import BadgeStatus from '../BadgeStatus';
|
||||
import { OrdersPoolProps } from './types';
|
||||
|
||||
const OrdersPool = (props: OrdersPoolProps) => {
|
||||
const {
|
||||
ordersBuySell,
|
||||
setOrdersBuySell,
|
||||
currencyNames,
|
||||
ordersLoading,
|
||||
filteredOrdersHistory,
|
||||
secondAssetUsdPrice,
|
||||
takeOrderClick,
|
||||
} = props;
|
||||
const ordersInfoRef = useRef<HTMLTableSectionElement | null>(null);
|
||||
const { firstCurrencyName, secondCurrencyName } = currencyNames;
|
||||
const [infoTooltipPos, setInfoTooltipPos] = useState({ x: 0, y: 0 });
|
||||
const [ordersInfoTooltip, setOrdersInfoTooltip] = useState<PageOrderData | null>(null);
|
||||
|
||||
const moveInfoTooltip = (event: React.MouseEvent) => {
|
||||
setInfoTooltipPos({ x: event.clientX, y: event.clientY });
|
||||
};
|
||||
|
||||
useMouseLeave(ordersInfoRef, () => setOrdersInfoTooltip(null));
|
||||
return (
|
||||
<>
|
||||
<div className={styles.ordersPool}>
|
||||
<div className={styles.ordersPool__header}>
|
||||
<h5 className={styles.ordersPool__header_title}>Orders pool</h5>
|
||||
|
||||
<div className={styles.ordersPool__header_type}>
|
||||
<button
|
||||
onClick={() => setOrdersBuySell(buySellValues[0])}
|
||||
className={classes(
|
||||
styles.btn,
|
||||
styles.all,
|
||||
ordersBuySell.code === 'all' && styles.selected,
|
||||
)}
|
||||
></button>
|
||||
|
||||
<button
|
||||
onClick={() => setOrdersBuySell(buySellValues[1])}
|
||||
className={classes(
|
||||
styles.btn,
|
||||
styles.buy,
|
||||
ordersBuySell.code === 'buy' && styles.selected,
|
||||
)}
|
||||
>
|
||||
B
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setOrdersBuySell(buySellValues[2])}
|
||||
className={classes(
|
||||
styles.btn,
|
||||
styles.sell,
|
||||
ordersBuySell.code === 'sell' && styles.selected,
|
||||
)}
|
||||
>
|
||||
S
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.ordersPool__content}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Price ({secondCurrencyName})</th>
|
||||
<th>Amount ({firstCurrencyName})</th>
|
||||
<th>Total ({secondCurrencyName})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{!ordersLoading && !!filteredOrdersHistory.length && (
|
||||
<tbody
|
||||
ref={ordersInfoRef}
|
||||
onMouseMove={moveInfoTooltip}
|
||||
onMouseLeave={() => setOrdersInfoTooltip(null)}
|
||||
className="orders-scroll"
|
||||
>
|
||||
{filteredOrdersHistory?.map((e) => {
|
||||
const maxValue = Math.max(
|
||||
...filteredOrdersHistory.map((order) =>
|
||||
parseFloat(String(order.left)),
|
||||
),
|
||||
);
|
||||
const percentage = (
|
||||
(parseFloat(String(e.left)) / maxValue) *
|
||||
100
|
||||
).toFixed(2);
|
||||
|
||||
return (
|
||||
<OrdersRow
|
||||
orderData={e}
|
||||
percentage={Number(percentage)}
|
||||
key={nanoid(16)}
|
||||
takeOrderClick={takeOrderClick}
|
||||
setOrdersInfoTooltip={setOrdersInfoTooltip}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
|
||||
{!filteredOrdersHistory.length && !ordersLoading && (
|
||||
<EmptyMessage text="No orders" />
|
||||
)}
|
||||
{ordersLoading && <ContentPreloader style={{ marginTop: 40 }} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Order tooltip */}
|
||||
{ordersInfoTooltip &&
|
||||
(() => {
|
||||
const totalDecimal = new Decimal(ordersInfoTooltip?.left).mul(
|
||||
new Decimal(ordersInfoTooltip?.price),
|
||||
);
|
||||
const totalValue = secondAssetUsdPrice
|
||||
? totalDecimal.mul(secondAssetUsdPrice).toFixed(2)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={nanoid(16)}
|
||||
className={styles.tooltip}
|
||||
arrowClass={styles.tooltip__arrow}
|
||||
style={{
|
||||
left: infoTooltipPos.x,
|
||||
top: infoTooltipPos.y + 20,
|
||||
}}
|
||||
shown
|
||||
>
|
||||
<div>
|
||||
<h6>Alias</h6>
|
||||
<p>
|
||||
@{cutAddress(ordersInfoTooltip?.user?.alias || 'no alias', 12)}{' '}
|
||||
{ordersInfoTooltip?.isInstant && (
|
||||
<BadgeStatus type="instant" icon />
|
||||
)}
|
||||
</p>
|
||||
|
||||
<h6>Price ({secondCurrencyName})</h6>
|
||||
<p
|
||||
style={{
|
||||
color:
|
||||
ordersInfoTooltip?.type === 'buy'
|
||||
? '#16D1D6'
|
||||
: '#FF6767',
|
||||
}}
|
||||
>
|
||||
{notationToString(ordersInfoTooltip?.price)}
|
||||
</p>
|
||||
<span>
|
||||
~
|
||||
{secondAssetUsdPrice && ordersInfoTooltip?.price !== undefined
|
||||
? (() => {
|
||||
const total =
|
||||
secondAssetUsdPrice * ordersInfoTooltip.price;
|
||||
const formatted =
|
||||
ordersInfoTooltip.price < 0.9
|
||||
? `$${total.toFixed(5)}`
|
||||
: `$${total.toFixed(2)}`;
|
||||
return formatted;
|
||||
})()
|
||||
: 'undefined'}
|
||||
</span>
|
||||
|
||||
<h6>Amount ({firstCurrencyName})</h6>
|
||||
<p>{notationToString(ordersInfoTooltip?.amount)}</p>
|
||||
|
||||
<h6>Total ({secondCurrencyName})</h6>
|
||||
<p>{notationToString(totalDecimal.toString())}</p>
|
||||
<span>
|
||||
~{' '}
|
||||
{totalValue ? `$${formatDollarValue(totalValue)}` : 'undefined'}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
})()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrdersPool;
|
||||
151
src/pages/dex/trading/components/OrdersPool/styles.module.scss
Normal file
151
src/pages/dex/trading/components/OrdersPool/styles.module.scss
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
.ordersPool {
|
||||
max-width: 415px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
@media screen and (max-width: 1480px) {
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
|
||||
&_title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&_type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
transition: 0.3s opacity ease;
|
||||
color: #ffffff;
|
||||
|
||||
&.selected,
|
||||
&:hover {
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
&.all {
|
||||
background: linear-gradient(to left, #ff6767 50%, #16d1d6 50%);
|
||||
}
|
||||
|
||||
&.buy {
|
||||
background-color: #16d1d6;
|
||||
}
|
||||
|
||||
&.sell {
|
||||
background-color: #ff6767;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 10px;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-inline: 10px;
|
||||
margin-bottom: 9px;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
th {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: start;
|
||||
color: var(--table-th-color);
|
||||
min-width: 80px;
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
height: 29dvh;
|
||||
min-height: 265px;
|
||||
max-height: 380px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding-bottom: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
border: 1px solid var(--dex-tooltip-border-color);
|
||||
min-width: 140px;
|
||||
max-width: 180px;
|
||||
padding: 10px;
|
||||
transform: translateX(-50%);
|
||||
background-color: var(--dex-tooltip-bg);
|
||||
|
||||
&__arrow {
|
||||
border-top: 1px solid var(--dex-tooltip-border-color);
|
||||
background-color: var(--dex-tooltip-bg) !important;
|
||||
}
|
||||
|
||||
h6 {
|
||||
color: var(--table-th-color);
|
||||
margin-top: 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
color: #8d95ae;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
21
src/pages/dex/trading/components/OrdersPool/types.ts
Normal file
21
src/pages/dex/trading/components/OrdersPool/types.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
import SelectValue from '@/interfaces/states/pages/dex/trading/InputPanelItem/SelectValue';
|
||||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
|
||||
export interface OrdersPoolProps {
|
||||
ordersBuySell: SelectValue;
|
||||
setOrdersBuySell: Dispatch<SetStateAction<SelectValue>>;
|
||||
currencyNames: {
|
||||
firstCurrencyName: string;
|
||||
secondCurrencyName: string;
|
||||
};
|
||||
ordersLoading: boolean;
|
||||
filteredOrdersHistory: PageOrderData[];
|
||||
secondAssetUsdPrice: number | undefined;
|
||||
takeOrderClick: (
|
||||
_event:
|
||||
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
||||
| React.MouseEvent<HTMLTableRowElement, MouseEvent>,
|
||||
_e: PageOrderData,
|
||||
) => void;
|
||||
}
|
||||
32
src/pages/dex/trading/components/StatItem/index.tsx
Normal file
32
src/pages/dex/trading/components/StatItem/index.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import StatItemProps from '@/interfaces/props/pages/dex/trading/StatItemProps';
|
||||
import { classes } from '@/utils/utils';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
function StatItem({ Img, title, value, className, coefficient }: StatItemProps) {
|
||||
return (
|
||||
<div className={classes(styles.statItem, className)}>
|
||||
<div className={styles.statItem__nav}>
|
||||
<Img />
|
||||
<p className={styles.statItem__nav_title}>{title}</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.statItem__content}>
|
||||
<p className={styles.statItem__content_val}>{value}</p>
|
||||
|
||||
{coefficient !== undefined && (
|
||||
<p
|
||||
className={classes(
|
||||
styles.statItem__content_coefficient,
|
||||
coefficient >= 0 ? styles.green : styles.red,
|
||||
)}
|
||||
>
|
||||
{coefficient >= 0 ? '+' : ''}
|
||||
{coefficient?.toFixed(2)}%
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StatItem;
|
||||
44
src/pages/dex/trading/components/StatItem/styles.module.scss
Normal file
44
src/pages/dex/trading/components/StatItem/styles.module.scss
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
.statItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
&__nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
&_title {
|
||||
color: var(--footer-selected-link);
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
&_val {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&_coefficient {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
&.green {
|
||||
color: #16d1d6;
|
||||
}
|
||||
|
||||
&.red {
|
||||
color: #ff6767;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { shortenAddress } from '@/utils/utils';
|
||||
import Link from 'next/link';
|
||||
import CurrencyIcon from '../CurrencyIcon';
|
||||
import styles from './styles.module.scss';
|
||||
import { AssetRowProps } from './types';
|
||||
|
||||
const AssetRow = ({ name, link, id, code }: AssetRowProps) => (
|
||||
<div className={styles.asset}>
|
||||
<p className={styles.asset__name}>
|
||||
<CurrencyIcon code={code} size={16} /> {name}:
|
||||
</p>
|
||||
<Link
|
||||
className={styles.asset__address}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={link}
|
||||
>
|
||||
{shortenAddress(id)}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default AssetRow;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
.asset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
|
||||
&__name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&__address {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export interface AssetRowProps {
|
||||
name: string;
|
||||
link: string;
|
||||
id: string;
|
||||
code: string | undefined;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import Image from 'next/image';
|
||||
import { CurrencyIconProps } from './types';
|
||||
|
||||
const CurrencyIcon = ({ code, size = 50 }: CurrencyIconProps) => (
|
||||
<Image width={size} height={size} src={`/currencies/trade_${code}.svg`} alt="currency" />
|
||||
);
|
||||
|
||||
export default CurrencyIcon;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export interface CurrencyIconProps {
|
||||
code: string | undefined;
|
||||
size?: number;
|
||||
}
|
||||
127
src/pages/dex/trading/components/TradingHeader/index.tsx
Normal file
127
src/pages/dex/trading/components/TradingHeader/index.tsx
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import { ReactComponent as ClockIcon } from '@/assets/images/UI/clock_icon.svg';
|
||||
import { ReactComponent as UpIcon } from '@/assets/images/UI/up_icon.svg';
|
||||
import { ReactComponent as DownIcon } from '@/assets/images/UI/down_icon.svg';
|
||||
import { ReactComponent as VolumeIcon } from '@/assets/images/UI/volume_icon.svg';
|
||||
import BackButton from '@/components/default/BackButton/BackButton';
|
||||
import { tradingKnownCurrencies, roundTo, notationToString } from '@/utils/utils';
|
||||
import styles from './styles.module.scss';
|
||||
import StatItem from '../StatItem';
|
||||
import { TradingHeaderProps } from './types';
|
||||
import CurrencyIcon from './components/CurrencyIcon';
|
||||
import AssetRow from './components/AssetRow';
|
||||
|
||||
const getCurrencyCode = (code?: string) =>
|
||||
tradingKnownCurrencies.includes(code || '') ? code : 'tsds';
|
||||
|
||||
const TradingHeader = ({
|
||||
pairStats,
|
||||
pairRateUsd,
|
||||
firstAssetLink,
|
||||
secondAssetLink,
|
||||
firstAssetId,
|
||||
secondAssetId,
|
||||
pairData,
|
||||
}: TradingHeaderProps) => {
|
||||
const currencyNames = {
|
||||
firstCurrencyName: pairData?.first_currency?.name || '',
|
||||
secondCurrencyName: pairData?.second_currency?.name || '',
|
||||
};
|
||||
|
||||
const { firstCurrencyName, secondCurrencyName } = currencyNames;
|
||||
const imgCode = getCurrencyCode(pairData?.first_currency?.code || '');
|
||||
const imgCode2 = getCurrencyCode(pairData?.second_currency?.code || '');
|
||||
|
||||
const coefficient = pairStats?.coefficient || 0;
|
||||
const coefficientOutput =
|
||||
parseFloat(coefficient?.toFixed(2) || '0') === -100
|
||||
? -99.99
|
||||
: parseFloat(coefficient?.toFixed(2) || '0');
|
||||
|
||||
const stats = [
|
||||
{
|
||||
Img: ClockIcon,
|
||||
title: '24h change',
|
||||
value: `${roundTo(notationToString(pairStats?.rate || 0), 8)} ${secondCurrencyName}`,
|
||||
coefficient: coefficientOutput,
|
||||
},
|
||||
{
|
||||
Img: UpIcon,
|
||||
title: '24h high',
|
||||
value: `${roundTo(notationToString(pairStats?.high || 0), 8)} ${secondCurrencyName}`,
|
||||
},
|
||||
{
|
||||
Img: DownIcon,
|
||||
title: '24h low',
|
||||
value: `${roundTo(notationToString(pairStats?.low || 0), 8)} ${secondCurrencyName}`,
|
||||
},
|
||||
{
|
||||
Img: VolumeIcon,
|
||||
title: '24h volume',
|
||||
value: `${roundTo(notationToString(pairStats?.volume || 0), 8)} ${secondCurrencyName}`,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.header}>
|
||||
<div className={styles.header__currency}>
|
||||
<div className={styles.header__currency_icon}>
|
||||
<CurrencyIcon code={imgCode} />
|
||||
</div>
|
||||
|
||||
<div className={styles.header__currency_item}>
|
||||
<p className={styles.currencyName}>
|
||||
{!pairData ? (
|
||||
'...'
|
||||
) : (
|
||||
<>
|
||||
{firstCurrencyName}
|
||||
<span>/{secondCurrencyName}</span>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className={styles.price}>
|
||||
<p className={styles.price__secondCurrency}>
|
||||
{notationToString(pairStats?.rate || 0)} {secondCurrencyName}
|
||||
</p>
|
||||
{pairRateUsd && <p className={styles.price__usd}>~ ${pairRateUsd}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.header__stats}>
|
||||
{pairData && firstAssetLink && secondAssetLink && (
|
||||
<div className={styles.header__stats_assets}>
|
||||
<AssetRow
|
||||
name={firstCurrencyName}
|
||||
link={firstAssetLink}
|
||||
id={firstAssetId || ''}
|
||||
code={imgCode}
|
||||
/>
|
||||
<AssetRow
|
||||
name={secondCurrencyName}
|
||||
link={secondAssetLink}
|
||||
id={secondAssetId || ''}
|
||||
code={imgCode2}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{stats.map(({ Img, title, value, coefficient }) => (
|
||||
<StatItem
|
||||
key={title}
|
||||
className={styles.header__stats_item}
|
||||
Img={Img}
|
||||
title={title}
|
||||
value={value}
|
||||
coefficient={coefficient}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<BackButton />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TradingHeader;
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 25px;
|
||||
position: relative;
|
||||
border: none;
|
||||
|
||||
&__currency {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
&_icon {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--icon-bg-color);
|
||||
border-radius: 50%;
|
||||
|
||||
> img {
|
||||
width: 26px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&_item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.currencyName {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
|
||||
span {
|
||||
color: var(--footer-selected-link);
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
&__secondCurrency {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__usd {
|
||||
color: var(--footer-selected-link);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__stats {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 20px;
|
||||
|
||||
&_assets {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
&_item {
|
||||
padding-left: 20px;
|
||||
border-left: 1px solid var(--delimiter-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/pages/dex/trading/components/TradingHeader/types.ts
Normal file
12
src/pages/dex/trading/components/TradingHeader/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import PairData from '@/interfaces/common/PairData';
|
||||
import { PairStats } from '@/interfaces/responses/orders/GetPairStatsRes';
|
||||
|
||||
export interface TradingHeaderProps {
|
||||
pairStats: PairStats | null;
|
||||
pairRateUsd: string | undefined;
|
||||
firstAssetLink?: string;
|
||||
secondAssetLink?: string;
|
||||
firstAssetId?: string | null;
|
||||
secondAssetId?: string | null;
|
||||
pairData: PairData | null;
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import { cutAddress, formatDollarValue, notationToString } from '@/utils/utils';
|
||||
import { nanoid } from 'nanoid';
|
||||
import Tooltip from '@/components/UI/Tooltip/Tooltip';
|
||||
import Decimal from 'decimal.js';
|
||||
import { confirmIonicSwap, ionicSwap } from '@/utils/wallet';
|
||||
import { applyOrder, confirmTransaction } from '@/utils/methods';
|
||||
import { updateAutoClosedNotification } from '@/store/actions';
|
||||
import Link from 'next/link';
|
||||
import { Store } from '@/store/store-reducer';
|
||||
import { useAlert } from '@/hook/useAlert';
|
||||
import OrderRowTooltipCell from '../../../OrderRowTooltipCell';
|
||||
import MatrixConnectionBadge from '../../../MatrixConnectionBadge';
|
||||
import styles from '../../styles.module.scss';
|
||||
import { MyOrdersApplyRowProps } from './types';
|
||||
import BadgeStatus from '../../../BadgeStatus';
|
||||
|
||||
function MyOrdersApplyRow(props: MyOrdersApplyRowProps) {
|
||||
const {
|
||||
orderData,
|
||||
fetchUser,
|
||||
matrixAddresses,
|
||||
secondAssetUsdPrice,
|
||||
updateOrders,
|
||||
updateUserOrders,
|
||||
fetchTrades,
|
||||
pairData,
|
||||
userOrders,
|
||||
} = props;
|
||||
const e = orderData || {};
|
||||
|
||||
const { state, dispatch } = useContext(Store);
|
||||
const { setAlertState, setAlertSubtitle } = useAlert();
|
||||
const [applyingState, setApplyingState] = useState(false);
|
||||
|
||||
const connectedOrder = userOrders.find((order) => order.id === e.connected_order_id);
|
||||
|
||||
const totalDecimal = new Decimal(e.left).mul(new Decimal(e.price));
|
||||
const totalValue = secondAssetUsdPrice
|
||||
? totalDecimal.mul(secondAssetUsdPrice).toFixed(2)
|
||||
: undefined;
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
async function applyClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
|
||||
event.preventDefault();
|
||||
|
||||
if (e.id) {
|
||||
updateAutoClosedNotification(dispatch, [
|
||||
...state.closed_notifications,
|
||||
parseInt(e.id, 10),
|
||||
]);
|
||||
}
|
||||
|
||||
function alertErr(subtitle: string) {
|
||||
setAlertState('error');
|
||||
setAlertSubtitle(subtitle);
|
||||
setTimeout(() => {
|
||||
setAlertState(null);
|
||||
setAlertSubtitle('');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
setApplyingState(true);
|
||||
interface SwapOperationResult {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
errorCode?: number;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
let result: SwapOperationResult | null = null;
|
||||
|
||||
await (async () => {
|
||||
if (e.transaction) {
|
||||
if (!e.hex_raw_proposal) {
|
||||
alertErr('Invalid transaction data received');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(e.hex_raw_proposal);
|
||||
|
||||
const confirmSwapResult = await confirmIonicSwap(e.hex_raw_proposal);
|
||||
|
||||
console.log(confirmSwapResult);
|
||||
|
||||
if (confirmSwapResult.data?.error?.code === -7) {
|
||||
alertErr('Insufficient funds');
|
||||
return;
|
||||
}
|
||||
if (!confirmSwapResult.data?.result) {
|
||||
alertErr('Companion responded with an error');
|
||||
return;
|
||||
}
|
||||
|
||||
result = await confirmTransaction(e.id);
|
||||
} else {
|
||||
const firstCurrencyId = pairData?.first_currency.asset_id;
|
||||
const secondCurrencyId = pairData?.second_currency.asset_id;
|
||||
|
||||
console.log(firstCurrencyId, secondCurrencyId);
|
||||
|
||||
if (!(firstCurrencyId && secondCurrencyId)) {
|
||||
alertErr('Invalid transaction data received');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connectedOrder) return;
|
||||
|
||||
const leftDecimal = new Decimal(e.left);
|
||||
const priceDecimal = new Decimal(e.price);
|
||||
|
||||
const params = {
|
||||
destinationAssetID: e.type === 'buy' ? secondCurrencyId : firstCurrencyId,
|
||||
destinationAssetAmount: notationToString(
|
||||
e.type === 'buy'
|
||||
? leftDecimal.mul(priceDecimal).toString()
|
||||
: leftDecimal.toString(),
|
||||
),
|
||||
currentAssetID: e.type === 'buy' ? firstCurrencyId : secondCurrencyId,
|
||||
currentAssetAmount: notationToString(
|
||||
e.type === 'buy'
|
||||
? leftDecimal.toString()
|
||||
: leftDecimal.mul(priceDecimal).toString(),
|
||||
),
|
||||
|
||||
destinationAddress: e.user.address,
|
||||
};
|
||||
|
||||
console.log(params);
|
||||
|
||||
const createSwapResult = await ionicSwap(params);
|
||||
|
||||
console.log(createSwapResult);
|
||||
|
||||
const hex = createSwapResult?.data?.result?.hex_raw_proposal;
|
||||
|
||||
if (createSwapResult?.data?.error?.code === -7) {
|
||||
alertErr('Insufficient funds');
|
||||
return;
|
||||
}
|
||||
if (!hex) {
|
||||
alertErr('Companion responded with an error');
|
||||
return;
|
||||
}
|
||||
|
||||
result = await applyOrder({
|
||||
...e,
|
||||
hex_raw_proposal: hex,
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
setApplyingState(false);
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(result as { success: boolean }).success) {
|
||||
alertErr('Server responded with an error');
|
||||
return;
|
||||
}
|
||||
|
||||
await updateOrders();
|
||||
await updateUserOrders();
|
||||
await fetchUser();
|
||||
await fetchTrades();
|
||||
}
|
||||
|
||||
return (
|
||||
<tr key={nanoid(16)}>
|
||||
<td>
|
||||
<p className={styles.alias}>
|
||||
<span
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
className={styles.alias__text}
|
||||
>
|
||||
@{cutAddress(e.user.alias, 12) || 'no alias'}
|
||||
</span>
|
||||
|
||||
<MatrixConnectionBadge
|
||||
userAdress={e.user.address}
|
||||
userAlias={e.user.alias}
|
||||
matrixAddresses={matrixAddresses}
|
||||
/>
|
||||
{e.isInstant && (
|
||||
<div style={{ marginLeft: 2 }}>
|
||||
<BadgeStatus type="instant" icon />
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
|
||||
{(e.isInstant || e.transaction) && <BadgeStatus type="instant" />}
|
||||
|
||||
{e.user?.alias.length > 12 && (
|
||||
<Tooltip
|
||||
className={styles.tooltip}
|
||||
arrowClass={styles.tooltip__arrow}
|
||||
shown={showTooltip}
|
||||
>
|
||||
<p className={styles.tooltip__text}>{e.user?.alias}</p>
|
||||
</Tooltip>
|
||||
)}
|
||||
</td>
|
||||
|
||||
<OrderRowTooltipCell style={{ color: e.type === 'buy' ? '#16D1D6' : '#FF6767' }}>
|
||||
{notationToString(e.price)}
|
||||
</OrderRowTooltipCell>
|
||||
<OrderRowTooltipCell>{notationToString(e.left)}</OrderRowTooltipCell>
|
||||
|
||||
<OrderRowTooltipCell
|
||||
noTooltip
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '4px' }}
|
||||
>
|
||||
{notationToString(totalDecimal.toString())}{' '}
|
||||
<span>~ ${totalValue && formatDollarValue(totalValue)}</span>
|
||||
</OrderRowTooltipCell>
|
||||
|
||||
<td></td>
|
||||
<td>
|
||||
<Link href="/" onClick={applyClick}>
|
||||
{applyingState ? 'Process' : 'Apply'}
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyOrdersApplyRow;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import ApplyTip from '@/interfaces/common/ApplyTip';
|
||||
import MatrixAddress from '@/interfaces/common/MatrixAddress';
|
||||
import OrderRow from '@/interfaces/common/OrderRow';
|
||||
import PairData from '@/interfaces/common/PairData';
|
||||
|
||||
export interface MyOrdersApplyRowProps {
|
||||
orderData: ApplyTip;
|
||||
secondAssetUsdPrice: number | undefined;
|
||||
updateOrders: () => Promise<void>;
|
||||
updateUserOrders: () => Promise<void>;
|
||||
fetchUser: () => Promise<boolean>;
|
||||
fetchTrades: () => Promise<void>;
|
||||
matrixAddresses: MatrixAddress[];
|
||||
pairData: PairData | null;
|
||||
userOrders: OrderRow[];
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
import Decimal from 'decimal.js';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { useContext, useState } from 'react';
|
||||
import { cutAddress, formatDollarValue, notationToString } from '@/utils/utils';
|
||||
import Tooltip from '@/components/UI/Tooltip/Tooltip';
|
||||
import Link from 'next/link';
|
||||
import { cancelOrder } from '@/utils/methods';
|
||||
import { Store } from '@/store/store-reducer';
|
||||
import { useAlert } from '@/hook/useAlert';
|
||||
import BadgeStatus from '../../../BadgeStatus';
|
||||
import styles from '../../styles.module.scss';
|
||||
import MatrixConnectionBadge from '../../../MatrixConnectionBadge';
|
||||
import OrderRowTooltipCell from '../../../OrderRowTooltipCell';
|
||||
import { MyOrdersRowProps } from './types';
|
||||
|
||||
function MyOrdersRow(props: MyOrdersRowProps) {
|
||||
const {
|
||||
orderData,
|
||||
secondAssetUsdPrice,
|
||||
fetchUser,
|
||||
updateOrders,
|
||||
updateUserOrders,
|
||||
matrixAddresses,
|
||||
applyTips,
|
||||
} = props;
|
||||
|
||||
const e = orderData || {};
|
||||
const { state } = useContext(Store);
|
||||
const { setAlertState, setAlertSubtitle } = useAlert();
|
||||
const [cancellingState, setCancellingState] = useState(false);
|
||||
|
||||
const totalDecimal = new Decimal(e.left).mul(new Decimal(e.price));
|
||||
const totalValue = secondAssetUsdPrice
|
||||
? totalDecimal.mul(secondAssetUsdPrice).toFixed(2)
|
||||
: undefined;
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
async function cancelClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
|
||||
event.preventDefault();
|
||||
|
||||
if (cancellingState) return;
|
||||
|
||||
try {
|
||||
setCancellingState(true);
|
||||
const result = await cancelOrder(e.id);
|
||||
|
||||
if (!result.success) {
|
||||
setAlertState('error');
|
||||
setAlertSubtitle('Error while cancelling order');
|
||||
setTimeout(() => {
|
||||
setAlertState(null);
|
||||
setAlertSubtitle('');
|
||||
}, 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
await updateOrders();
|
||||
await updateUserOrders();
|
||||
await fetchUser();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setCancellingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<tr key={nanoid(16)}>
|
||||
<td>
|
||||
<p className={styles.alias}>
|
||||
<span
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
className={styles.alias__text}
|
||||
>
|
||||
@
|
||||
{cutAddress(
|
||||
state.wallet?.connected && state.wallet?.alias
|
||||
? state.wallet.alias
|
||||
: 'no alias',
|
||||
12,
|
||||
)}
|
||||
</span>
|
||||
|
||||
<MatrixConnectionBadge
|
||||
userAdress={state?.wallet?.address}
|
||||
userAlias={state.wallet?.alias}
|
||||
matrixAddresses={matrixAddresses}
|
||||
/>
|
||||
{e.isInstant && (
|
||||
<div style={{ marginLeft: 2 }}>
|
||||
<BadgeStatus type="instant" icon />
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
|
||||
{(state.wallet?.connected && state.wallet?.alias ? state.wallet?.alias : '')
|
||||
?.length > 12 && (
|
||||
<Tooltip
|
||||
className={styles.tooltip}
|
||||
arrowClass={styles.tooltip__arrow}
|
||||
shown={showTooltip}
|
||||
>
|
||||
<p className={styles.tooltip__text}>
|
||||
{state.wallet?.connected && state.wallet?.alias}
|
||||
</p>
|
||||
</Tooltip>
|
||||
)}
|
||||
</td>
|
||||
|
||||
<OrderRowTooltipCell style={{ color: e.type === 'buy' ? '#16D1D6' : '#FF6767' }}>
|
||||
{notationToString(e.price)}
|
||||
</OrderRowTooltipCell>
|
||||
|
||||
<OrderRowTooltipCell>{notationToString(e.amount)}</OrderRowTooltipCell>
|
||||
|
||||
<OrderRowTooltipCell
|
||||
noTooltip
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '4px' }}
|
||||
>
|
||||
{notationToString(totalDecimal.toString())}{' '}
|
||||
<span>~ ${totalValue && formatDollarValue(totalValue)}</span>
|
||||
</OrderRowTooltipCell>
|
||||
|
||||
<td>
|
||||
<p style={{ fontWeight: 700, color: '#1F8FEB' }}>
|
||||
{applyTips?.filter((tip) => tip.connected_order_id === e.id)?.length || 0}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<Link href="/" onClick={cancelClick}>
|
||||
{cancellingState ? 'Process' : 'Cancel'}
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyOrdersRow;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import ApplyTip from '@/interfaces/common/ApplyTip';
|
||||
import MatrixAddress from '@/interfaces/common/MatrixAddress';
|
||||
import OrderRow from '@/interfaces/common/OrderRow';
|
||||
|
||||
export interface MyOrdersRowProps {
|
||||
orderData: OrderRow;
|
||||
secondAssetUsdPrice: number | undefined;
|
||||
updateOrders: () => Promise<void>;
|
||||
updateUserOrders: () => Promise<void>;
|
||||
fetchUser: () => Promise<boolean>;
|
||||
matrixAddresses: MatrixAddress[];
|
||||
applyTips: ApplyTip[];
|
||||
}
|
||||
133
src/pages/dex/trading/components/UserOrders/index.tsx
Normal file
133
src/pages/dex/trading/components/UserOrders/index.tsx
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import { classes } from '@/utils/utils';
|
||||
import ContentPreloader from '@/components/UI/ContentPreloader/ContentPreloader';
|
||||
import useUpdateUser from '@/hook/useUpdateUser';
|
||||
import EmptyMessage from '@/components/UI/EmptyMessage';
|
||||
import styles from './styles.module.scss';
|
||||
import { UserOrdersProps } from './types';
|
||||
import MyOrdersRow from './components/MyOrdersRow';
|
||||
import MyOrdersApplyRow from './components/MyOrdersApplyRow';
|
||||
|
||||
const UserOrders = ({
|
||||
userOrders,
|
||||
applyTips,
|
||||
myOrdersLoading,
|
||||
loggedIn,
|
||||
ordersType,
|
||||
setOrdersType,
|
||||
handleCancelAllOrders,
|
||||
orderListRef,
|
||||
matrixAddresses,
|
||||
secondAssetUsdPrice,
|
||||
updateOrders,
|
||||
updateUserOrders,
|
||||
fetchTrades,
|
||||
pairData,
|
||||
}: UserOrdersProps) => {
|
||||
const fetchUser = useUpdateUser();
|
||||
const firstCurrencyName = pairData?.first_currency?.name || '';
|
||||
const secondCurrencyName = pairData?.second_currency?.name || '';
|
||||
|
||||
return (
|
||||
<div ref={orderListRef} className={styles.userOrders}>
|
||||
<div className={styles.userOrders__header}>
|
||||
<div className={styles.userOrders__header_nav}>
|
||||
<button
|
||||
onClick={() => setOrdersType('opened')}
|
||||
className={classes(
|
||||
styles.navItem,
|
||||
ordersType === 'opened' && styles.active,
|
||||
)}
|
||||
>
|
||||
Opened orders {applyTips?.length ? <span>{applyTips?.length}</span> : ''}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setOrdersType('history')}
|
||||
className={classes(
|
||||
styles.navItem,
|
||||
ordersType === 'history' && styles.active,
|
||||
)}
|
||||
>
|
||||
Orders History
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles.trading__user_cancelOrder}>
|
||||
<button
|
||||
className={styles.userOrders__header_btn}
|
||||
onClick={handleCancelAllOrders}
|
||||
>
|
||||
Cancel all orders
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Alias</th>
|
||||
<th>Price ({secondCurrencyName})</th>
|
||||
<th>Amount ({firstCurrencyName})</th>
|
||||
<th>Total ({secondCurrencyName})</th>
|
||||
<th>Offers</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
{!myOrdersLoading && loggedIn && !!userOrders.length && (
|
||||
<div className={`${styles.userOrders__body} orders-scroll`}>
|
||||
<table>
|
||||
<tbody className={styles.incoming}>
|
||||
{userOrders.map((e) => (
|
||||
<MyOrdersRow
|
||||
key={e.id}
|
||||
orderData={e}
|
||||
applyTips={applyTips}
|
||||
fetchUser={fetchUser}
|
||||
matrixAddresses={matrixAddresses}
|
||||
secondAssetUsdPrice={secondAssetUsdPrice}
|
||||
updateOrders={updateOrders}
|
||||
updateUserOrders={updateUserOrders}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{!!applyTips.length && (
|
||||
<table className={styles.apply}>
|
||||
<tbody>
|
||||
{applyTips.map((e) => (
|
||||
<MyOrdersApplyRow
|
||||
key={e.id}
|
||||
pairData={pairData}
|
||||
orderData={e}
|
||||
userOrders={userOrders}
|
||||
fetchTrades={fetchTrades}
|
||||
fetchUser={fetchUser}
|
||||
matrixAddresses={matrixAddresses}
|
||||
secondAssetUsdPrice={secondAssetUsdPrice}
|
||||
updateOrders={updateOrders}
|
||||
updateUserOrders={updateUserOrders}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{myOrdersLoading && loggedIn && <ContentPreloader style={{ marginTop: 40 }} />}
|
||||
|
||||
{!loggedIn && <EmptyMessage text="Connect wallet to see your orders" />}
|
||||
|
||||
{loggedIn && !userOrders.length && !myOrdersLoading && (
|
||||
<EmptyMessage text="No orders" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserOrders;
|
||||
227
src/pages/dex/trading/components/UserOrders/styles.module.scss
Normal file
227
src/pages/dex/trading/components/UserOrders/styles.module.scss
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
.userOrders {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
@media screen and (max-width: 1440px) {
|
||||
width: 520px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
padding-bottom: 0;
|
||||
|
||||
&_nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22px;
|
||||
|
||||
.navItem {
|
||||
padding-bottom: 7px;
|
||||
position: relative;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
font-weight: 600;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
top: -10px;
|
||||
right: -15px;
|
||||
position: absolute;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
color: #fff;
|
||||
background-color: #ff6767;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #1f8feb;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1f8feb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&_btn {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
background-color: #1f8feb33;
|
||||
color: #1f8feb;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: #1f8feb43;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-inline: 10px;
|
||||
padding-bottom: 5px;
|
||||
margin-top: 10px;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
th {
|
||||
min-width: 100px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
color: var(--table-th-color);
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
min-width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
a {
|
||||
display: block;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 10px;
|
||||
padding-bottom: 20px;
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
&.apply {
|
||||
border-top: 1px solid #1f8feb40;
|
||||
border-bottom: 1px solid #1f8feb40;
|
||||
background-color: var(--blur-color) !important;
|
||||
|
||||
tr {
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.incoming {
|
||||
tr {
|
||||
&:nth-child(even) {
|
||||
background-color: var(--table-even-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
min-width: 100px;
|
||||
|
||||
&:last-child {
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1440px) {
|
||||
min-width: 0;
|
||||
|
||||
&:last-child {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> p {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
|
||||
> span {
|
||||
line-height: 1;
|
||||
color: var(--font-dimmed-color);
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alias {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 14px;
|
||||
|
||||
&__text {
|
||||
color: var(--font-main-color) !important;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 5%;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
|
||||
z-index: 9999;
|
||||
|
||||
&__text {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
border-radius: 2px;
|
||||
left: 30% !important;
|
||||
background-color: var(--trade-table-tooltip) !important;
|
||||
}
|
||||
}
|
||||
22
src/pages/dex/trading/components/UserOrders/types.ts
Normal file
22
src/pages/dex/trading/components/UserOrders/types.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import ApplyTip from '@/interfaces/common/ApplyTip';
|
||||
import MatrixAddress from '@/interfaces/common/MatrixAddress';
|
||||
import OrderRow from '@/interfaces/common/OrderRow';
|
||||
import PairData from '@/interfaces/common/PairData';
|
||||
import { Dispatch, ForwardedRef, SetStateAction } from 'react';
|
||||
|
||||
export interface UserOrdersProps {
|
||||
orderListRef: ForwardedRef<HTMLDivElement>;
|
||||
userOrders: OrderRow[];
|
||||
applyTips: ApplyTip[];
|
||||
myOrdersLoading: boolean;
|
||||
loggedIn: boolean;
|
||||
ordersType: 'opened' | 'history';
|
||||
setOrdersType: Dispatch<SetStateAction<'opened' | 'history'>>;
|
||||
handleCancelAllOrders: () => void;
|
||||
secondAssetUsdPrice: number | undefined;
|
||||
updateOrders: () => Promise<void>;
|
||||
updateUserOrders: () => Promise<void>;
|
||||
fetchTrades: () => Promise<void>;
|
||||
matrixAddresses: MatrixAddress[];
|
||||
pairData: PairData | null;
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import { findPairID } from '@/utils/methods';
|
||||
import { GetServerSideProps } from 'next';
|
||||
|
||||
const getServerSideProps: GetServerSideProps = async (context) => {
|
||||
const { first, second } = context.query;
|
||||
|
||||
if (!first || !second) {
|
||||
return {
|
||||
notFound: true, // Show a 404 page if parameters are missing
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const idFound = await findPairID(
|
||||
first as string,
|
||||
second as string,
|
||||
context.req.headers.host as string,
|
||||
);
|
||||
|
||||
console.log('ID found:', idFound);
|
||||
|
||||
if (typeof idFound === 'number') {
|
||||
return {
|
||||
redirect: {
|
||||
destination: `/dex/trading/${idFound}`,
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching pair ID:', error);
|
||||
return {
|
||||
props: {
|
||||
error: 'Failed to resolve the pair.',
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default getServerSideProps;
|
||||
89
src/pages/dex/trading/helpers/handleInputChange.ts
Normal file
89
src/pages/dex/trading/helpers/handleInputChange.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
import Decimal from 'decimal.js';
|
||||
import { isPositiveFloatStr } from '@/utils/utils';
|
||||
import { validateTokensInput } from 'shared/utils';
|
||||
|
||||
interface HandleInputChangeParams {
|
||||
inputValue: string;
|
||||
priceOrAmount: 'price' | 'amount';
|
||||
otherValue: string;
|
||||
thisDP: number;
|
||||
totalDP: number;
|
||||
setThisState: Dispatch<SetStateAction<string>>;
|
||||
setTotalState: Dispatch<SetStateAction<string>>;
|
||||
setThisValid: Dispatch<SetStateAction<boolean>>;
|
||||
setTotalValid: Dispatch<SetStateAction<boolean>>;
|
||||
balance?: string | undefined;
|
||||
setRangeInputValue?: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export function handleInputChange({
|
||||
inputValue,
|
||||
priceOrAmount,
|
||||
otherValue,
|
||||
thisDP,
|
||||
totalDP,
|
||||
setThisState,
|
||||
setTotalState,
|
||||
setThisValid,
|
||||
setTotalValid,
|
||||
balance,
|
||||
setRangeInputValue,
|
||||
}: HandleInputChangeParams) {
|
||||
if (inputValue !== '' && !isPositiveFloatStr(inputValue)) return;
|
||||
|
||||
const digitsOnly = inputValue.replace('.', '').replace(/^0+/, '');
|
||||
if (digitsOnly.length > 18) return;
|
||||
|
||||
let thisDecimal: Decimal;
|
||||
let otherDecimal: Decimal;
|
||||
|
||||
try {
|
||||
thisDecimal = new Decimal(inputValue || '0');
|
||||
otherDecimal = new Decimal(otherValue || '0');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setThisValid(false);
|
||||
setTotalValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setThisState(inputValue);
|
||||
|
||||
if (!inputValue) {
|
||||
setTotalState('');
|
||||
setTotalValid(false);
|
||||
setThisValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const isValid = validateTokensInput(inputValue, thisDP);
|
||||
if (!isValid.valid) {
|
||||
setTotalState('');
|
||||
setTotalValid(false);
|
||||
setThisValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setThisValid(true);
|
||||
|
||||
if (!thisDecimal.isNaN() && !otherDecimal.isNaN() && otherValue !== '') {
|
||||
const total =
|
||||
priceOrAmount === 'price'
|
||||
? thisDecimal.mul(otherDecimal)
|
||||
: otherDecimal.mul(thisDecimal);
|
||||
|
||||
setTotalState(total.toString());
|
||||
|
||||
const totalValid = validateTokensInput(total.toFixed(totalDP), totalDP);
|
||||
setTotalValid(totalValid.valid);
|
||||
|
||||
if (priceOrAmount === 'amount' && balance && setRangeInputValue) {
|
||||
const percent = thisDecimal.div(balance).mul(100);
|
||||
setRangeInputValue(percent.toFixed());
|
||||
}
|
||||
} else {
|
||||
setTotalState('');
|
||||
setTotalValid(false);
|
||||
}
|
||||
}
|
||||
96
src/pages/dex/trading/helpers/takeOrderClick.ts
Normal file
96
src/pages/dex/trading/helpers/takeOrderClick.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import { notationToString } from '@/utils/utils';
|
||||
import Decimal from 'decimal.js';
|
||||
import React from 'react';
|
||||
import PairDataType from '@/interfaces/common/PairData';
|
||||
import OrderFormOutput from '@/interfaces/common/orderFormOutput';
|
||||
import { handleInputChange } from './handleInputChange';
|
||||
|
||||
interface takeOrderClickParams {
|
||||
event:
|
||||
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
||||
| React.MouseEvent<HTMLTableRowElement, MouseEvent>;
|
||||
PageOrderData: PageOrderData;
|
||||
pairData: PairDataType | null;
|
||||
buyForm: OrderFormOutput;
|
||||
sellForm: OrderFormOutput;
|
||||
balance: string | undefined;
|
||||
scrollToOrderForm: () => void;
|
||||
}
|
||||
|
||||
function takeOrderClick({
|
||||
event,
|
||||
PageOrderData,
|
||||
pairData,
|
||||
buyForm,
|
||||
sellForm,
|
||||
balance,
|
||||
scrollToOrderForm,
|
||||
}: takeOrderClickParams) {
|
||||
event.preventDefault();
|
||||
const e = PageOrderData;
|
||||
|
||||
const priceStr = notationToString(new Decimal(e.price).toString()) || '';
|
||||
const amountStr = notationToString(new Decimal(e.amount).toString()) || '';
|
||||
|
||||
const secondCurrencyDP = pairData?.second_currency?.asset_info?.decimal_point || 12;
|
||||
const firstCurrencyDP = pairData?.first_currency?.asset_info?.decimal_point || 12;
|
||||
|
||||
if (e.type === 'sell') {
|
||||
handleInputChange({
|
||||
inputValue: priceStr,
|
||||
priceOrAmount: 'price',
|
||||
otherValue: amountStr,
|
||||
thisDP: secondCurrencyDP,
|
||||
totalDP: secondCurrencyDP,
|
||||
setThisState: buyForm.setPrice,
|
||||
setTotalState: buyForm.setTotal,
|
||||
setThisValid: buyForm.setPriceValid,
|
||||
setTotalValid: buyForm.setTotalValid,
|
||||
});
|
||||
|
||||
handleInputChange({
|
||||
inputValue: amountStr,
|
||||
priceOrAmount: 'amount',
|
||||
otherValue: priceStr,
|
||||
thisDP: firstCurrencyDP,
|
||||
totalDP: secondCurrencyDP,
|
||||
setThisState: buyForm.setAmount,
|
||||
setTotalState: buyForm.setTotal,
|
||||
setThisValid: buyForm.setAmountValid,
|
||||
setTotalValid: buyForm.setTotalValid,
|
||||
balance,
|
||||
setRangeInputValue: buyForm.setRangeInputValue,
|
||||
});
|
||||
} else {
|
||||
handleInputChange({
|
||||
inputValue: priceStr,
|
||||
priceOrAmount: 'price',
|
||||
otherValue: amountStr,
|
||||
thisDP: secondCurrencyDP,
|
||||
totalDP: secondCurrencyDP,
|
||||
setThisState: sellForm.setPrice,
|
||||
setTotalState: sellForm.setTotal,
|
||||
setThisValid: sellForm.setPriceValid,
|
||||
setTotalValid: sellForm.setTotalValid,
|
||||
});
|
||||
|
||||
handleInputChange({
|
||||
inputValue: amountStr,
|
||||
priceOrAmount: 'amount',
|
||||
otherValue: priceStr,
|
||||
thisDP: firstCurrencyDP,
|
||||
totalDP: secondCurrencyDP,
|
||||
setThisState: sellForm.setAmount,
|
||||
setTotalState: sellForm.setTotal,
|
||||
setThisValid: sellForm.setAmountValid,
|
||||
setTotalValid: sellForm.setTotalValid,
|
||||
balance,
|
||||
setRangeInputValue: sellForm.setRangeInputValue,
|
||||
});
|
||||
}
|
||||
|
||||
scrollToOrderForm();
|
||||
}
|
||||
|
||||
export default takeOrderClick;
|
||||
47
src/pages/dex/trading/hooks/useFilteredData.ts
Normal file
47
src/pages/dex/trading/hooks/useFilteredData.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import { Trade } from '@/interfaces/responses/trades/GetTradeRes';
|
||||
import SelectValue from '@/interfaces/states/pages/dex/trading/InputPanelItem/SelectValue';
|
||||
import { Store } from '@/store/store-reducer';
|
||||
import { useContext } from 'react';
|
||||
|
||||
interface useFilteredDataParams {
|
||||
trades: Trade[];
|
||||
ordersHistory: PageOrderData[];
|
||||
ordersBuySell: SelectValue;
|
||||
tradesType: 'all' | 'my';
|
||||
}
|
||||
|
||||
const useFilteredData = ({
|
||||
ordersHistory,
|
||||
trades,
|
||||
ordersBuySell,
|
||||
tradesType,
|
||||
}: useFilteredDataParams) => {
|
||||
const { state } = useContext(Store);
|
||||
|
||||
const filteredTrades =
|
||||
tradesType === 'my'
|
||||
? trades.filter(
|
||||
(trade) =>
|
||||
trade.buyer.address === state.wallet?.address ||
|
||||
trade.seller.address === state.wallet?.address,
|
||||
)
|
||||
: trades;
|
||||
|
||||
const filteredOrdersHistory = ordersHistory
|
||||
?.filter((e) => (ordersBuySell.code === 'all' ? e : e.type === ordersBuySell.code))
|
||||
?.filter((e) => e.user.address !== state.wallet?.address)
|
||||
?.sort((a, b) => {
|
||||
if (ordersBuySell.code === 'buy') {
|
||||
return parseFloat(b.price.toString()) - parseFloat(a.price.toString());
|
||||
}
|
||||
return parseFloat(a.price.toString()) - parseFloat(b.price.toString());
|
||||
});
|
||||
|
||||
return {
|
||||
filteredOrdersHistory,
|
||||
filteredTrades,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFilteredData;
|
||||
25
src/pages/dex/trading/hooks/useMatrixAddresses.ts
Normal file
25
src/pages/dex/trading/hooks/useMatrixAddresses.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import MatrixAddress from '@/interfaces/common/MatrixAddress';
|
||||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import { getMatrixAddresses } from '@/utils/methods';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const useMatrixAddresses = (ordersHistory: PageOrderData[]) => {
|
||||
const [matrixAddresses, setMatrixAddresses] = useState<MatrixAddress[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchConnections = async () => {
|
||||
const filteredAddresses = ordersHistory?.map((e) => e?.user?.address);
|
||||
if (!filteredAddresses.length) return;
|
||||
|
||||
const data = await getMatrixAddresses(filteredAddresses);
|
||||
|
||||
setMatrixAddresses(data.addresses);
|
||||
};
|
||||
|
||||
fetchConnections();
|
||||
}, [ordersHistory]);
|
||||
|
||||
return matrixAddresses;
|
||||
};
|
||||
|
||||
export default useMatrixAddresses;
|
||||
104
src/pages/dex/trading/hooks/useOrdereForm.ts
Normal file
104
src/pages/dex/trading/hooks/useOrdereForm.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import Decimal from 'decimal.js';
|
||||
import PairData from '@/interfaces/common/PairData';
|
||||
import OrderFormOutput from '@/interfaces/common/orderFormOutput';
|
||||
import { handleInputChange } from '../helpers/handleInputChange';
|
||||
|
||||
interface UseOrderFormParams {
|
||||
type: 'buy' | 'sell';
|
||||
pairData: PairData | null;
|
||||
balance: string | undefined;
|
||||
assetsRates: Map<string, number>;
|
||||
}
|
||||
|
||||
export function useOrderForm({
|
||||
type,
|
||||
pairData,
|
||||
balance,
|
||||
assetsRates,
|
||||
}: UseOrderFormParams): OrderFormOutput {
|
||||
const [price, setPrice] = useState('');
|
||||
const [amount, setAmount] = useState('');
|
||||
const [total, setTotal] = useState('');
|
||||
|
||||
const [priceValid, setPriceValid] = useState(false);
|
||||
const [amountValid, setAmountValid] = useState(false);
|
||||
const [totalValid, setTotalValid] = useState(false);
|
||||
|
||||
const [totalUsd, setTotalUsd] = useState<string | undefined>(undefined);
|
||||
const [rangeInputValue, setRangeInputValue] = useState('50');
|
||||
|
||||
const priceDP = pairData?.second_currency?.asset_info?.decimal_point || 12;
|
||||
const amountDP = pairData?.first_currency?.asset_info?.decimal_point || 12;
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const totalDecimal = new Decimal(total);
|
||||
const zanoPrice = assetsRates.get(pairData?.second_currency?.asset_id || '');
|
||||
setTotalUsd(zanoPrice ? totalDecimal.mul(zanoPrice).toFixed(2) : undefined);
|
||||
} catch (err) {
|
||||
setTotalUsd(undefined);
|
||||
}
|
||||
}, [total, assetsRates, pairData?.second_currency?.asset_id]);
|
||||
|
||||
function onPriceChange(inputValue: string) {
|
||||
handleInputChange({
|
||||
inputValue,
|
||||
priceOrAmount: 'price',
|
||||
otherValue: amount,
|
||||
thisDP: priceDP,
|
||||
totalDP: priceDP,
|
||||
setThisState: setPrice,
|
||||
setTotalState: setTotal,
|
||||
setThisValid: setPriceValid,
|
||||
setTotalValid,
|
||||
});
|
||||
}
|
||||
|
||||
function onAmountChange(inputValue: string) {
|
||||
handleInputChange({
|
||||
inputValue,
|
||||
priceOrAmount: 'amount',
|
||||
otherValue: price,
|
||||
thisDP: amountDP,
|
||||
totalDP: priceDP,
|
||||
setThisState: setAmount,
|
||||
setTotalState: setTotal,
|
||||
setThisValid: setAmountValid,
|
||||
setTotalValid,
|
||||
balance,
|
||||
setRangeInputValue,
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
setPrice('');
|
||||
setAmount('');
|
||||
setTotal('');
|
||||
setPriceValid(false);
|
||||
setAmountValid(false);
|
||||
setTotalValid(false);
|
||||
setRangeInputValue('50');
|
||||
}
|
||||
|
||||
return {
|
||||
price,
|
||||
amount,
|
||||
total,
|
||||
priceValid,
|
||||
amountValid,
|
||||
totalValid,
|
||||
totalUsd,
|
||||
rangeInputValue,
|
||||
setRangeInputValue,
|
||||
onPriceChange,
|
||||
onAmountChange,
|
||||
resetForm,
|
||||
setTotal,
|
||||
setPrice,
|
||||
setAmount,
|
||||
setPriceValid,
|
||||
setAmountValid,
|
||||
setTotalValid,
|
||||
};
|
||||
}
|
||||
85
src/pages/dex/trading/hooks/useSocketListeners.ts
Normal file
85
src/pages/dex/trading/hooks/useSocketListeners.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import ApplyTip from '@/interfaces/common/ApplyTip';
|
||||
import OrderRow from '@/interfaces/common/OrderRow';
|
||||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import { PairStats } from '@/interfaces/responses/orders/GetPairStatsRes';
|
||||
import { getUserOrdersPage } from '@/utils/methods';
|
||||
import socket from '@/utils/socket';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Dispatch, SetStateAction, useEffect } from 'react';
|
||||
|
||||
interface useSocketListenersParams {
|
||||
setUserOrders: Dispatch<SetStateAction<OrderRow[]>>;
|
||||
setApplyTips: Dispatch<SetStateAction<ApplyTip[]>>;
|
||||
setPairStats: Dispatch<SetStateAction<PairStats | null>>;
|
||||
setOrdersHistory: Dispatch<SetStateAction<PageOrderData[]>>;
|
||||
ordersHistory: PageOrderData[];
|
||||
updateOrders: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const useSocketListeners = ({
|
||||
setUserOrders,
|
||||
setApplyTips,
|
||||
setPairStats,
|
||||
setOrdersHistory,
|
||||
ordersHistory,
|
||||
updateOrders,
|
||||
}: useSocketListenersParams) => {
|
||||
const router = useRouter();
|
||||
const pairId = typeof router.query.id === 'string' ? router.query.id : '';
|
||||
|
||||
async function socketUpdateOrders() {
|
||||
const result = await getUserOrdersPage(pairId);
|
||||
|
||||
if (result.success) {
|
||||
setUserOrders(result?.data?.orders || []);
|
||||
setApplyTips(result?.data?.applyTips || []);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
socket.emit('in-trading', { id: router.query.id });
|
||||
|
||||
return () => {
|
||||
socket.emit('out-trading', { id: router.query.id });
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
socket.on('new-order', async (data) => {
|
||||
setOrdersHistory([data.orderData, ...ordersHistory]);
|
||||
await socketUpdateOrders();
|
||||
});
|
||||
|
||||
socket.on('delete-order', async () => {
|
||||
await updateOrders();
|
||||
await socketUpdateOrders();
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('new-order');
|
||||
socket.off('delete-order');
|
||||
};
|
||||
}, [ordersHistory]);
|
||||
|
||||
useEffect(() => {
|
||||
function onUpdateStats({ pairStats }: { pairStats: PairStats }) {
|
||||
setPairStats(pairStats);
|
||||
}
|
||||
|
||||
socket.on('update-pair-stats', onUpdateStats);
|
||||
|
||||
return () => {
|
||||
socket.off('update-pair-stats', onUpdateStats);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
socket.on('update-orders', async () => {
|
||||
await socketUpdateOrders();
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('update-orders');
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
70
src/pages/dex/trading/hooks/useTradeInit.ts
Normal file
70
src/pages/dex/trading/hooks/useTradeInit.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import PairData from '@/interfaces/common/PairData';
|
||||
import { Store } from '@/store/store-reducer';
|
||||
import { useContext } from 'react';
|
||||
import Decimal from 'decimal.js';
|
||||
import { PairStats } from '@/interfaces/responses/orders/GetPairStatsRes';
|
||||
import { useOrderForm } from './useOrdereForm';
|
||||
|
||||
interface useTradeInitParams {
|
||||
pairData: PairData | null;
|
||||
pairStats: PairStats | null;
|
||||
}
|
||||
|
||||
const useTradeInit = ({ pairData, pairStats }: useTradeInitParams) => {
|
||||
const { state } = useContext(Store);
|
||||
|
||||
const currencyNames = {
|
||||
firstCurrencyName: pairData?.first_currency?.name || '',
|
||||
secondCurrencyName: pairData?.second_currency?.name || '',
|
||||
};
|
||||
const loggedIn = !!state.wallet?.connected;
|
||||
|
||||
const assets = state.wallet?.connected ? state.wallet?.assets || [] : [];
|
||||
const balance = assets.find((e) => e.ticker === currencyNames.firstCurrencyName)?.balance;
|
||||
|
||||
const firstAssetId = pairData ? pairData.first_currency?.asset_id : undefined;
|
||||
const secondAssetId = pairData ? pairData.second_currency?.asset_id : undefined;
|
||||
const firstAssetLink = firstAssetId
|
||||
? `https://explorer.zano.org/assets?asset_id=${encodeURIComponent(firstAssetId)}`
|
||||
: undefined;
|
||||
const secondAssetLink = secondAssetId
|
||||
? `https://explorer.zano.org/assets?asset_id=${encodeURIComponent(secondAssetId)}`
|
||||
: undefined;
|
||||
|
||||
const secondAssetUsdPrice = state.assetsRates.get(secondAssetId || '');
|
||||
|
||||
const pairRateUsd =
|
||||
pairStats?.rate !== undefined && secondAssetUsdPrice !== undefined
|
||||
? new Decimal(pairStats.rate)
|
||||
.mul(secondAssetUsdPrice)
|
||||
.toFixed(pairStats.rate < 0.1 ? 6 : 2)
|
||||
: undefined;
|
||||
|
||||
const buyForm = useOrderForm({
|
||||
type: 'buy',
|
||||
pairData,
|
||||
balance,
|
||||
assetsRates: state.assetsRates,
|
||||
});
|
||||
|
||||
const sellForm = useOrderForm({
|
||||
type: 'sell',
|
||||
pairData,
|
||||
balance,
|
||||
assetsRates: state.assetsRates,
|
||||
});
|
||||
|
||||
return {
|
||||
currencyNames,
|
||||
firstAssetLink,
|
||||
secondAssetLink,
|
||||
secondAssetUsdPrice,
|
||||
loggedIn,
|
||||
balance,
|
||||
buyForm,
|
||||
sellForm,
|
||||
pairRateUsd,
|
||||
};
|
||||
};
|
||||
|
||||
export default useTradeInit;
|
||||
144
src/pages/dex/trading/hooks/useTradingData.ts
Normal file
144
src/pages/dex/trading/hooks/useTradingData.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import {
|
||||
getCandles,
|
||||
getOrdersPage,
|
||||
getPair,
|
||||
getPairStats,
|
||||
getUserOrdersPage,
|
||||
getTrades,
|
||||
} from '@/utils/methods';
|
||||
import useUpdateUser from '@/hook/useUpdateUser';
|
||||
import { Dispatch, SetStateAction, useContext, useEffect, useState } from 'react';
|
||||
import CandleRow from '@/interfaces/common/CandleRow';
|
||||
import { PageOrderData } from '@/interfaces/responses/orders/GetOrdersPageRes';
|
||||
import { Trade } from '@/interfaces/responses/trades/GetTradeRes';
|
||||
import PairData from '@/interfaces/common/PairData';
|
||||
import { PairStats } from '@/interfaces/responses/orders/GetPairStatsRes';
|
||||
import OrderRow from '@/interfaces/common/OrderRow';
|
||||
import ApplyTip from '@/interfaces/common/ApplyTip';
|
||||
import { useRouter } from 'next/router';
|
||||
import PeriodState from '@/interfaces/states/pages/dex/trading/InputPanelItem/PeriodState';
|
||||
import { Store } from '@/store/store-reducer';
|
||||
|
||||
interface UseTradingDataParams {
|
||||
periodsState: PeriodState;
|
||||
setCandles: Dispatch<SetStateAction<CandleRow[]>>;
|
||||
setOrdersHistory: Dispatch<SetStateAction<PageOrderData[]>>;
|
||||
setTrades: Dispatch<SetStateAction<Trade[]>>;
|
||||
setPairData: Dispatch<SetStateAction<PairData | null>>;
|
||||
setPairStats: Dispatch<SetStateAction<PairStats | null>>;
|
||||
setUserOrders: Dispatch<SetStateAction<OrderRow[]>>;
|
||||
setApplyTips: Dispatch<SetStateAction<ApplyTip[]>>;
|
||||
setMyOrdersLoading: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export function useTradingData({
|
||||
periodsState,
|
||||
setCandles,
|
||||
setOrdersHistory,
|
||||
setTrades,
|
||||
setPairData,
|
||||
setPairStats,
|
||||
setUserOrders,
|
||||
setApplyTips,
|
||||
setMyOrdersLoading,
|
||||
}: UseTradingDataParams) {
|
||||
const { state } = useContext(Store);
|
||||
const fetchUser = useUpdateUser();
|
||||
const router = useRouter();
|
||||
const [candlesLoaded, setCandlesLoaded] = useState(false);
|
||||
const [ordersLoading, setOrdersLoading] = useState(true);
|
||||
const [tradesLoading, setTradesLoading] = useState(true);
|
||||
const pairId = typeof router.query.id === 'string' ? router.query.id : '';
|
||||
const loggedIn = !!state.wallet?.connected;
|
||||
|
||||
async function fetchCandles() {
|
||||
setCandlesLoaded(false);
|
||||
setCandles([]);
|
||||
const result = await getCandles(pairId, periodsState.code);
|
||||
if (result.success) {
|
||||
setCandles(result.data);
|
||||
} else {
|
||||
setCandles([]);
|
||||
}
|
||||
setCandlesLoaded(true);
|
||||
}
|
||||
|
||||
async function updateOrders() {
|
||||
setOrdersLoading(true);
|
||||
const result = await getOrdersPage(pairId);
|
||||
if (!result.success) return;
|
||||
setOrdersHistory(result?.data || []);
|
||||
setOrdersLoading(false);
|
||||
}
|
||||
|
||||
async function updateUserOrders() {
|
||||
setMyOrdersLoading(true);
|
||||
const result = await getUserOrdersPage(pairId);
|
||||
await fetchUser();
|
||||
|
||||
if (!result.success) return;
|
||||
setUserOrders(result?.data?.orders || []);
|
||||
setApplyTips(result?.data?.applyTips || []);
|
||||
setMyOrdersLoading(false);
|
||||
}
|
||||
|
||||
async function fetchTrades() {
|
||||
setTradesLoading(true);
|
||||
const result = await getTrades(pairId);
|
||||
|
||||
if (result.success) {
|
||||
setTrades(result.data);
|
||||
}
|
||||
|
||||
setTradesLoading(false);
|
||||
}
|
||||
|
||||
async function fetchPairStats() {
|
||||
const result = await getPairStats(pairId);
|
||||
if (!result.success) return;
|
||||
setPairStats(result.data);
|
||||
}
|
||||
|
||||
async function getPairData() {
|
||||
const result = await getPair(pairId);
|
||||
if (!result.success) {
|
||||
router.push('/404');
|
||||
return;
|
||||
}
|
||||
setPairData(result.data);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchPairStats();
|
||||
getPairData();
|
||||
updateOrders();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCandles();
|
||||
}, [periodsState]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await fetchTrades();
|
||||
})();
|
||||
}, [pairId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loggedIn) return;
|
||||
setUserOrders([]);
|
||||
updateUserOrders();
|
||||
}, [state.wallet?.connected && state.wallet?.address]);
|
||||
|
||||
return {
|
||||
fetchCandles,
|
||||
updateOrders,
|
||||
updateUserOrders,
|
||||
fetchTrades,
|
||||
fetchPairStats,
|
||||
getPairData,
|
||||
candlesLoaded,
|
||||
ordersLoading,
|
||||
tradesLoading,
|
||||
};
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ const initialState: ContextState = {
|
|||
offers: 0,
|
||||
},
|
||||
closed_notifications: [],
|
||||
alertState: null,
|
||||
alertSubtitle: '',
|
||||
};
|
||||
|
||||
const reducer = (state: ContextState, action: ContextAction): ContextState => {
|
||||
|
|
@ -31,6 +33,12 @@ const reducer = (state: ContextState, action: ContextAction): ContextState => {
|
|||
case 'CLOSED_NOTIFICATIONS_UPDATED': {
|
||||
return { ...state, closed_notifications: action.payload };
|
||||
}
|
||||
case 'ALERT_STATE_UPDATED': {
|
||||
return { ...state, alertState: action.payload };
|
||||
}
|
||||
case 'ALERT_SUBTITLE_UPDATED': {
|
||||
return { ...state, alertSubtitle: action.payload };
|
||||
}
|
||||
default:
|
||||
return { ...state };
|
||||
}
|
||||
|
|
@ -40,6 +48,7 @@ export const Store = createContext<ContextValue>({
|
|||
state: initialState,
|
||||
dispatch: () => undefined,
|
||||
});
|
||||
|
||||
export const StoreProvider = (props: { children: ReactNode }) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
return <Store.Provider value={{ state, dispatch }}>{props.children}</Store.Provider>;
|
||||
|
|
|
|||
|
|
@ -1,238 +1,18 @@
|
|||
.main {
|
||||
padding: 0 60px;
|
||||
.trading {
|
||||
padding-inline: 60px;
|
||||
padding-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media screen and (max-width: 1060px) {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
@media screen and (max-width: 1600px) {
|
||||
padding-inline: 40px;
|
||||
}
|
||||
|
||||
.orders__preloader {
|
||||
margin-top: 40px;
|
||||
@media screen and (max-width: 1200px) {
|
||||
padding-inline: 20px;
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
.alias {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
&__tooltip {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 5%;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
font-size: 12px;
|
||||
|
||||
&_arrow {
|
||||
border-radius: 2px;
|
||||
left: 50%;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trading__title__wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 25px;
|
||||
position: relative;
|
||||
border: none;
|
||||
|
||||
.currency__stats__wrapper {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 20px;
|
||||
|
||||
&_assets {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
|
||||
.asset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
|
||||
p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> :nth-child(2),
|
||||
:nth-child(4),
|
||||
:nth-child(3) {
|
||||
padding-left: 20px;
|
||||
border-left: 1px solid var(--delimiter-color);
|
||||
}
|
||||
|
||||
.trading__stat__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
&_nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
p {
|
||||
color: var(--footer-selected-link);
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&_content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
.val {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.coefficient {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
&__green {
|
||||
color: #16d1d6;
|
||||
}
|
||||
|
||||
&__red {
|
||||
color: #ff6767;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
gap: 0;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column;
|
||||
|
||||
>div {
|
||||
width: 100% !important;
|
||||
padding-left: 0 !important;
|
||||
border-left: none !important;
|
||||
|
||||
padding: 20px 0 !important;
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trading__currency__wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
.trading__currency__wrapper_top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.coin__icon {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--icon-bg-color);
|
||||
border-radius: 50%;
|
||||
|
||||
img {
|
||||
width: 25px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.coin__currency {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
>p:first-child {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
|
||||
span {
|
||||
color: var(--footer-selected-link);
|
||||
}
|
||||
}
|
||||
|
||||
.trading__currency__rate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
.trading__currency__rate_secondCurrency {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.trading__currency__rate_usd {
|
||||
color: var(--footer-selected-link);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
>div:first-child {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
>img {
|
||||
scale: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
>div:last-child {
|
||||
>p:first-child {
|
||||
font-size: 24px;
|
||||
|
||||
>span {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trading__top__wrapper {
|
||||
&__top {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
|
@ -241,412 +21,19 @@
|
|||
min-height: 380px;
|
||||
max-height: 500px;
|
||||
|
||||
.trading__orders_panel {
|
||||
max-width: 415px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
@media screen and (max-width: 1480px) {
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
.tooltip__arrow {
|
||||
border-top: 1px solid var(--dex-tooltip-border-color);
|
||||
background-color: var(--dex-tooltip-bg);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
border: 1px solid var(--dex-tooltip-border-color);
|
||||
width: 140px;
|
||||
padding: 10px;
|
||||
transform: translateX(-50%);
|
||||
background-color: var(--dex-tooltip-bg);
|
||||
|
||||
h6 {
|
||||
color: var(--table-th-color);
|
||||
margin-top: 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
color: #8d95ae;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
|
||||
&_title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&_type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
transition: 0.3s opacity ease;
|
||||
color: #ffffff;
|
||||
|
||||
&.selected,
|
||||
&:hover {
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
&.all {
|
||||
background: linear-gradient(to left, #ff6767 50%, #16d1d6 50%);
|
||||
}
|
||||
|
||||
&.buy {
|
||||
background-color: #16d1d6;
|
||||
}
|
||||
|
||||
&.sell {
|
||||
background-color: #ff6767;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orders__panel_content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-inline: 10px;
|
||||
margin-bottom: 9px;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
th {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: start;
|
||||
color: var(--table-th-color);
|
||||
min-width: 80px;
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
height: 29dvh;
|
||||
min-height: 265px;
|
||||
max-height: 380px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding-bottom: 20px;
|
||||
padding: 10px;
|
||||
|
||||
tr {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: var(--table-even-bg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: var(--line-width, 0%);
|
||||
height: 100%;
|
||||
background: #16d1d61a;
|
||||
}
|
||||
|
||||
&.sell_section {
|
||||
&::after {
|
||||
background: #ff67671a;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
>p {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
>p {
|
||||
min-width: 80px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orders__message {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
|
||||
svg {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
&.all__orders__msg {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
&.user__orders__msg {
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
&.all__orders__msg {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
&.user__orders__msg {
|
||||
padding-right: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
>h6 {
|
||||
color: var(--font-dimmed-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.allTrades {
|
||||
max-width: 415px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
@media screen and (max-width: 1480px) {
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22px;
|
||||
padding: 10px;
|
||||
padding-bottom: 0;
|
||||
|
||||
.navItem {
|
||||
padding-bottom: 7px;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
font-weight: 600;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-color: #1f8feb;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1f8feb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orders__panel_content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-inline: 10px;
|
||||
margin-bottom: 9px;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
th {
|
||||
min-width: 80px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: start;
|
||||
color: var(--table-th-color);
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
height: 29dvh;
|
||||
min-height: 265px;
|
||||
max-height: 380px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
tr {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: var(--table-even-bg);
|
||||
}
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
>p {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
>p {
|
||||
min-width: 80px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orders__message {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
|
||||
svg {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
&.all__orders__msg {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
&.user__orders__msg {
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
&.all__orders__msg {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
&.user__orders__msg {
|
||||
padding-right: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
>h6 {
|
||||
color: var(--font-dimmed-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trading__chart__wrapper {
|
||||
&_chart {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.trading__chart__preloader {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.trading__chart__settings {
|
||||
.settings {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.trading__chart__dropdown {
|
||||
width: 254px;
|
||||
&__dropdown {
|
||||
width: 250px;
|
||||
height: 48px;
|
||||
|
||||
@media screen and (max-width: 1360px) {
|
||||
|
|
@ -657,7 +44,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.trading__info {
|
||||
&__info {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
|
|
@ -668,327 +55,5 @@
|
|||
gap: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&_createOrder {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.trading__user__orders {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background: var(--window-bg-color);
|
||||
border: 1px solid var(--delimiter-color);
|
||||
border-radius: 10px;
|
||||
|
||||
@media screen and (max-width: 1440px) {
|
||||
width: 520px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
padding-bottom: 0;
|
||||
|
||||
&_nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22px;
|
||||
|
||||
.navItem {
|
||||
padding-bottom: 7px;
|
||||
position: relative;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
font-weight: 600;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
top: -10px;
|
||||
right: -15px;
|
||||
position: absolute;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
color: #fff;
|
||||
background-color: #ff6767;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #1f8feb;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1f8feb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&_btn {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
background-color: #1f8feb33;
|
||||
color: #1f8feb;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: #1f8feb43;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
br {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-inline: 10px;
|
||||
padding-bottom: 5px;
|
||||
margin-top: 10px;
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
th {
|
||||
min-width: 100px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-align: start;
|
||||
color: var(--table-th-color);
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
min-width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
a {
|
||||
display: block;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trading__right__tables {
|
||||
padding: 10px;
|
||||
padding-bottom: 20px;
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
&.trading__apply__table {
|
||||
border-top: 1px solid #1f8feb40;
|
||||
border-bottom: 1px solid #1f8feb40;
|
||||
background-color: var(--blur-color) !important;
|
||||
|
||||
tr {
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--delimiter-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.stats__table__incoming {
|
||||
tr {
|
||||
&:nth-child(even) {
|
||||
background-color: var(--table-even-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
min-width: 100px;
|
||||
|
||||
&:last-child {
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1440px) {
|
||||
min-width: 0;
|
||||
|
||||
&:last-child {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
>p {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
|
||||
>span {
|
||||
line-height: 1;
|
||||
color: var(--font-dimmed-color);
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orders__message {
|
||||
width: 100%;
|
||||
margin-top: 40px !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
|
||||
svg {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
&.all__orders__msg {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
&.user__orders__msg {
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
&.all__orders__msg {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
&.user__orders__msg {
|
||||
padding-right: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
>h6 {
|
||||
color: var(--font-dimmed-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table__tooltip {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 20%;
|
||||
transform: translateX(-50%);
|
||||
background-color: var(--trade-table-tooltip);
|
||||
|
||||
>.table__tooltip_arrow {
|
||||
background-color: var(--trade-table-tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
.table__tooltip_right {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 2%;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
|
||||
>.table__tooltip_arrow {
|
||||
left: 40px;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
.table__tooltip_end {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: -50%;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
|
||||
>.table__tooltip_arrow {
|
||||
border-radius: 2px;
|
||||
left: 10%;
|
||||
background-color: var(--trade-table-tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
width: fit-content;
|
||||
padding: 1px 4px;
|
||||
padding-right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: 100px;
|
||||
background: radial-gradient(100% 246.57% at 0% 0%, #a366ff 0%, #601fff 100%);
|
||||
|
||||
&.icon {
|
||||
min-width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
|
||||
>img {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
>img {
|
||||
height: 13px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
>span {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.high {
|
||||
padding: 2px 4px;
|
||||
background: radial-gradient(100% 188.88% at 0% 0%, #16d1d6 0%, #274cff 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
53
src/styles/themes/dark.scss
Normal file
53
src/styles/themes/dark.scss
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
[data-theme='dark'] {
|
||||
--main-bg-color: #0c0c3a;
|
||||
--table-header-font-color: rgba(213, 213, 226, 1);
|
||||
--table-header-bg: rgba(36, 36, 78, 1);
|
||||
--table-button-bg-hover: rgba(40, 40, 83, 1);
|
||||
--font-main-color: rgba(255, 255, 255, 1);
|
||||
--font-dimmed-color: rgba(141, 149, 174, 1);
|
||||
--delimiter-color: rgba(255, 255, 255, 0.1);
|
||||
--window-bg-color: rgba(15, 32, 85, 1);
|
||||
--dropdown-bg-color: rgba(17, 49, 107, 1);
|
||||
--dropdown-bg-hover: rgba(29, 59, 114, 1);
|
||||
--profile-widget-avatar: rgba(17, 49, 107, 1);
|
||||
--window-border-color: rgba(255, 255, 255, 0.3);
|
||||
--bordered-input-bg: rgba(255, 255, 255, 0.1);
|
||||
--row-header-bg: #154d91;
|
||||
--button-bordered-hover: rgba(255, 255, 255, 0.1);
|
||||
--alert-bg: rgba(35, 52, 103, 1);
|
||||
--switch-bg-color: rgba(15, 32, 85, 1);
|
||||
--switch-bg-hover: rgba(39, 54, 102, 1);
|
||||
--switch-disabled-bg-color: rgba(255, 255, 255, 0.1);
|
||||
--dimmed-btn-bg: rgba(255, 255, 255, 0.1);
|
||||
--dimmed-btn-hover: rgba(255, 255, 255, 0.3);
|
||||
--font-faded-color: rgba(255, 255, 255, 0.5);
|
||||
--slider-bg-color: #1d3b72;
|
||||
--blur-color: rgba(255, 255, 255, 0.05);
|
||||
--icon-bg-color: #0f1f54;
|
||||
--swap-btn-bg: rgba(31, 143, 235, 0.1);
|
||||
--table-bg-color: rgba(16, 16, 64, 1);
|
||||
--advices-bg-color: rgba(255, 255, 255, 0.1);
|
||||
--messenger-top-bg: #0f2055;
|
||||
--messenger-bottom-bg: #273666;
|
||||
--messenger-bg: #0f2055;
|
||||
--messenger-border: rgba(255, 255, 255, 0.1);
|
||||
--message-bg: rgba(255, 255, 255, 0.1);
|
||||
--custom-message-bg: transparent;
|
||||
--trade-table-tooltip: #1d3b72;
|
||||
--dex-offer-notification: #0f2055;
|
||||
--dex-panel-bg: #0f2055;
|
||||
--dex-panel-tooltip: #273666;
|
||||
--dex-buy-sell-border: #ffffff1a;
|
||||
--dex-input-currency: #39497c;
|
||||
--footer-selected-link: #ffffffcc;
|
||||
--table-th-color: #ffffffb2;
|
||||
--table-thead-bg: #24244e;
|
||||
--table-tbody-bg: #101040;
|
||||
--admin-table-border-color: #596f98;
|
||||
--alert-btn-bg: rgba(31, 143, 235, 0.2);
|
||||
--alert-btn-hover: rgba(31, 143, 235, 0.3);
|
||||
--table-even-bg: #0c1d4f;
|
||||
--table-tr-hover-color: #172a66;
|
||||
--dex-tooltip-bg: #11316b;
|
||||
--dex-tooltip-border-color: #1f8feb26;
|
||||
}
|
||||
|
|
@ -51,57 +51,3 @@
|
|||
--dex-tooltip-bg: #eff8ff;
|
||||
--dex-tooltip-border-color: #1f8feb33;
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--main-bg-color: #0c0c3a;
|
||||
--table-header-font-color: rgba(213, 213, 226, 1);
|
||||
--table-header-bg: rgba(36, 36, 78, 1);
|
||||
--table-button-bg-hover: rgba(40, 40, 83, 1);
|
||||
--font-main-color: rgba(255, 255, 255, 1);
|
||||
--font-dimmed-color: rgba(141, 149, 174, 1);
|
||||
--delimiter-color: rgba(255, 255, 255, 0.1);
|
||||
--window-bg-color: rgba(15, 32, 85, 1);
|
||||
--dropdown-bg-color: rgba(17, 49, 107, 1);
|
||||
--dropdown-bg-hover: rgba(29, 59, 114, 1);
|
||||
--profile-widget-avatar: rgba(17, 49, 107, 1);
|
||||
--window-border-color: rgba(255, 255, 255, 0.3);
|
||||
--bordered-input-bg: rgba(255, 255, 255, 0.1);
|
||||
--row-header-bg: #154d91;
|
||||
--button-bordered-hover: rgba(255, 255, 255, 0.1);
|
||||
--alert-bg: rgba(35, 52, 103, 1);
|
||||
--switch-bg-color: rgba(15, 32, 85, 1);
|
||||
--switch-bg-hover: rgba(39, 54, 102, 1);
|
||||
--switch-disabled-bg-color: rgba(255, 255, 255, 0.1);
|
||||
--dimmed-btn-bg: rgba(255, 255, 255, 0.1);
|
||||
--dimmed-btn-hover: rgba(255, 255, 255, 0.3);
|
||||
--font-faded-color: rgba(255, 255, 255, 0.5);
|
||||
--slider-bg-color: #1d3b72;
|
||||
--blur-color: rgba(255, 255, 255, 0.05);
|
||||
--icon-bg-color: #0f1f54;
|
||||
--swap-btn-bg: rgba(31, 143, 235, 0.1);
|
||||
--table-bg-color: rgba(16, 16, 64, 1);
|
||||
--advices-bg-color: rgba(255, 255, 255, 0.1);
|
||||
--messenger-top-bg: #0f2055;
|
||||
--messenger-bottom-bg: #273666;
|
||||
--messenger-bg: #0f2055;
|
||||
--messenger-border: rgba(255, 255, 255, 0.1);
|
||||
--message-bg: rgba(255, 255, 255, 0.1);
|
||||
--custom-message-bg: transparent;
|
||||
--trade-table-tooltip: #1d3b72;
|
||||
--dex-offer-notification: #0f2055;
|
||||
--dex-panel-bg: #0f2055;
|
||||
--dex-panel-tooltip: #273666;
|
||||
--dex-buy-sell-border: #ffffff1a;
|
||||
--dex-input-currency: #39497c;
|
||||
--footer-selected-link: #ffffffcc;
|
||||
--table-th-color: #ffffffb2;
|
||||
--table-thead-bg: #24244e;
|
||||
--table-tbody-bg: #101040;
|
||||
--admin-table-border-color: #596f98;
|
||||
--alert-btn-bg: rgba(31, 143, 235, 0.2);
|
||||
--alert-btn-hover: rgba(31, 143, 235, 0.3);
|
||||
--table-even-bg: #0c1d4f;
|
||||
--table-tr-hover-color: #172a66;
|
||||
--dex-tooltip-bg: #11316b;
|
||||
--dex-tooltip-border-color: #1f8feb26;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -335,3 +335,15 @@ export async function getTrades(pairId: string) {
|
|||
})
|
||||
.then((res) => res.data);
|
||||
}
|
||||
|
||||
export async function getMatrixAddresses(addresses: string[]) {
|
||||
try {
|
||||
const { data } = await axios.post('https://messenger.zano.org/api/get-addresses', {
|
||||
addresses,
|
||||
});
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,9 +143,9 @@ export function formatTime(ts: string | number) {
|
|||
return date.toLocaleTimeString('ru-RU', { hour12: false });
|
||||
}
|
||||
|
||||
export function classes(...items: (string | boolean | undefined)[]): string {
|
||||
export function classes(...classes: (string | boolean | undefined)[]): string {
|
||||
// boolean for constructions like [predicate] && [className]
|
||||
return items.filter((className) => className).join(' ');
|
||||
return classes.filter((className) => className).join(' ');
|
||||
}
|
||||
|
||||
export const ZANO_ASSET_ID = 'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue