From 610a8f3212668d520a5a046350101da48bcc778e Mon Sep 17 00:00:00 2001 From: Hossein Date: Fri, 21 Feb 2025 16:21:38 +0330 Subject: [PATCH 01/16] #85 feat: fetch and store currencies, pairs and fees as a Map in Redux state --- src/store/actions/actionTypes.js | 4 +- src/store/actions/exchange.js | 18 +++++ src/store/actions/index.js | 6 +- src/store/reducers/exchangeReducer.js | 31 +++++++-- src/store/sagas/global.js | 98 ++++++++++++++++++++++++--- 5 files changed, 141 insertions(+), 16 deletions(-) diff --git a/src/store/actions/actionTypes.js b/src/store/actions/actionTypes.js index 69b605d..94c2588 100644 --- a/src/store/actions/actionTypes.js +++ b/src/store/actions/actionTypes.js @@ -61,4 +61,6 @@ export const ACTIVE_ORDER_LAYOUT = "ACTIVE_ORDER_LAYOUT"; export const ACTIVE_ACTION_SHEET = "ACTIVE_ACTION_SHEET"; - +export const GET_CURRENCIES = "GET_CURRENCIES"; +export const GET_PAIRS = "GET_PAIRS"; +export const GET_FEES = "GET_FEES"; diff --git a/src/store/actions/exchange.js b/src/store/actions/exchange.js index 04e0f64..7701e6d 100644 --- a/src/store/actions/exchange.js +++ b/src/store/actions/exchange.js @@ -103,4 +103,22 @@ export const setExchangeConfigs = configs => { type: actionTypes.SET_EXCHANGE_CONFIG, configs: configs }; +}; +export const getCurrencies = currencies => { + return { + type: actionTypes.GET_CURRENCIES, + currencies, + }; +}; +export const getPairs = pairs => { + return { + type: actionTypes.GET_PAIRS, + pairs, + }; +}; +export const getFees = fees => { + return { + type: actionTypes.GET_FEES, + fees, + }; }; \ No newline at end of file diff --git a/src/store/actions/index.js b/src/store/actions/index.js index 0acfe70..36947b0 100644 --- a/src/store/actions/index.js +++ b/src/store/actions/index.js @@ -22,8 +22,10 @@ export { setIPGInitiate, setVerifyEmailLock, setVerifyEmailLockInitiate, - setExchangeConfigs - + setExchangeConfigs, + getCurrencies, + getPairs, + getFees } from "./exchange"; export { diff --git a/src/store/reducers/exchangeReducer.js b/src/store/reducers/exchangeReducer.js index 8a5923c..dee93da 100644 --- a/src/store/reducers/exchangeReducer.js +++ b/src/store/reducers/exchangeReducer.js @@ -5,7 +5,6 @@ const initialState = { pairs: [], symbols: [], activePair: {}, - //lastPrice:{}, activePairOrders: { bestBuyPrice: 0, bestSellPrice: 0, @@ -29,7 +28,10 @@ const initialState = { defaultTheme: "", supportEmail: "", baseCurrency: "", - dateType: "" + dateType: "", + currencies: [], + pairsList: [], + fees: [], }; const exchangeReducer = (state = initialState, action) => { @@ -45,12 +47,14 @@ const exchangeReducer = (state = initialState, action) => { verifyEmailLock: action.verifyEmailLockTime, }; case actionTypes.SET_ACTIVE_PAIR: + const [baseAsset, quoteAsset] = action.pair.split('_'); return { ...state, activePair: { - ...state.activePair, - ...action.pair, - name: action.pair.baseAsset + "/" + action.pair.quoteAsset + symbol: `${baseAsset}${quoteAsset}`, + pair: action.pair, + baseAsset: baseAsset, + quoteAsset: quoteAsset, }, activePairOrders: { ...state.activePairOrders, @@ -107,8 +111,25 @@ const exchangeReducer = (state = initialState, action) => { ...state, ...action.configs }; + + case actionTypes.GET_CURRENCIES: + return { + ...state, + currencies: action.currencies, + }; + case actionTypes.GET_PAIRS: + return { + ...state, + pairsList: action.pairs, + }; + case actionTypes.GET_FEES: + return { + ...state, + fees: action.fees, + }; default: return state; + } }; diff --git a/src/store/sagas/global.js b/src/store/sagas/global.js index 8c1fccb..d096410 100644 --- a/src/store/sagas/global.js +++ b/src/store/sagas/global.js @@ -5,6 +5,7 @@ import axios from "axios"; import i18n from "i18next"; import {defaultConfigs} from "../../setup/configs/configs"; + export function* setThemeSaga(action) { try { yield put(actions.setTheme(action.theme)); @@ -20,7 +21,7 @@ export function* setThemeSaga(action) { } export function* setActivePair(action) { - yield call([localStorage, 'setItem'], "activePair", action.pair.symbol) + yield call([localStorage, 'setItem'], "activePair", action.pair) yield call([localStorage, 'setItem'], "activeMarketTab", action.activeTab) yield put(actions.setActivePair(action.pair)); } @@ -52,8 +53,9 @@ function* getExchangeInfo() { for (let i = 0; i < 10; i++) { try { - const {data: {symbols}} = yield call(axios.get, '/api/v3/exchangeInfo') - return symbols + const {data} = yield call(axios.get, '/api/v3/exchangeInfo') + + return data } catch (err) { if (i < 2) { yield delay(1000) @@ -65,6 +67,29 @@ function* getExchangeInfo() { } } +function* fetchCurrencies() { + const params = { + includeManualGateways: false, + includeOffChainGateways: true, + includeOnChainGateways: true + }; + + for (let i = 0; i < 10; i++) { + try { + const response = yield call(axios.get, '/wallet/currency', { params }); + + const { currencies } = response.data; + return currencies; + } catch (err) { + if (i < 9) { + yield delay(1000); + } else { + throw new Error('Failed to fetch currencies after 10 attempts.'); + } + } + } +} + export function* loadConfig(action) { yield put(actions.setLoading(true)) @@ -91,7 +116,6 @@ export function* loadConfig(action) { i18n.changeLanguage(language) appTheme = defaultTheme; - } catch (e) { i18n.changeLanguage(defaultConfigs?.defaultLanguage) appTheme = defaultConfigs?.defaultTheme; @@ -99,11 +123,59 @@ export function* loadConfig(action) { } try { + const exchangeInfo = yield call(getExchangeInfo); + const currencies = yield call(fetchCurrencies); + + const currenciesMap = currencies.reduce((acc, currency) => { + const precisionValue = currency.precision.toString(); + const decimalPlaces = precisionValue.includes('.') + ? precisionValue.split('.')[1].length + : 0; + + const decimalFactor = Number((Math.pow(10, -decimalPlaces)).toFixed(decimalPlaces)); + + acc[currency.symbol] = { + ...currency, + precision: decimalPlaces, + minOrder: decimalFactor, + step: decimalFactor, + }; + return acc; + }, {}); + + yield put(actions.getCurrencies(currenciesMap)); + + const pairsList = exchangeInfo.symbols; + const pairsListMap = pairsList.reduce((acc, pair) => { + /*acc[pair.symbol] = pair;*/ + const key = `${pair.baseAsset}_${pair.quoteAsset}`; + acc[key] = { + symbol: pair.symbol, + baseAsset: pair.baseAsset, + quoteAsset: pair.quoteAsset, + orderTypes: pair.orderTypes, + }; + return acc; + }, {}); + yield put(actions.getPairs(pairsListMap)); + + const fees = exchangeInfo.fees; + const feesMap = fees.reduce((acc, fee) => { + acc[fee.pair] = fee; + /* acc[pair.symbol] = { + symbol: pair.symbol, + baseAsset: pair.baseAsset, + quoteAsset: pair.quoteAsset, + orderTypes: pair.orderTypes, + };*/ + return acc; + }, {}); + yield put(actions.getFees(feesMap)); const localTheme = yield call([localStorage, 'getItem'], 'theme') if (localTheme) appTheme = localTheme; - const symbols = yield call(getExchangeInfo) + const symbols = exchangeInfo.symbols for (const symbol of symbols) { if (symbol.symbol.toUpperCase().includes("NLN")) continue if (!assets.includes(symbol.baseAsset)) { @@ -125,11 +197,21 @@ export function* loadConfig(action) { lastPrice[symbol.symbol] = 0 } yield put(actions.setExchange({pairs, assets, symbols, lastPrice})); + /*yield put(actions.getCurrencies({currencies}));*/ yield put(actions.setUserAccountInfo({wallets, tradeFee})); const activePair = yield call([localStorage, 'getItem'], 'activePair') - const lastActivePair = symbols.find(symbol => symbol.symbol === activePair) - yield put(actions.setActivePair(lastActivePair || symbols[0])); + const lastActivePair = Object.keys(pairsListMap).includes(activePair) ? activePair : null; + + /*const lastActivePair = lastActivePairKey ? pairsListMap[lastActivePairKey] : null;*/ + + + + /*const lastActivePair = symbols.find(symbol => symbol.symbol === activePair)*/ + + + + yield put(actions.setActivePair(lastActivePair || Object.keys(pairsListMap)[0])); } catch (e) { yield put(actions.setError(true)) @@ -197,4 +279,4 @@ const assetsScope = { SOL: {min: 0.00001, step: 0.00001}, TON: {min: 0.0001, step: 0.0001}, DOGE: {min: 0.0001, step: 0.0001}, -} +} \ No newline at end of file From 4d1a1860024ff483d2141de1091b9870f9a5bf39 Mon Sep 17 00:00:00 2001 From: Hossein Date: Fri, 21 Feb 2025 17:15:56 +0330 Subject: [PATCH 02/16] #85 feat: Integrated currency data from service into MarketDisplay component --- .../MarketViewCard/MarketViewCard.js | 16 ++++---- src/utils/utils.js | 39 ++++++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js b/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js index c054942..cb786fd 100644 --- a/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js +++ b/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js @@ -1,17 +1,18 @@ import React from 'react'; import {useTranslation} from "react-i18next"; import {images} from "../../../../../../../../assets/images"; -import {BN} from "../../../../../../../../utils/utils"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; import i18n from "i18next"; import Loading from "../../../../../../../../components/Loading/Loading"; import Error from "../../../../../../../../components/Error/Error"; +import {useSelector} from "react-redux"; const MarketViewCard = ({title, data, error, isLoading, volume}) => { - console.log("title", title) - console.log("data", data) const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) const content = () => { if (isLoading) return @@ -19,18 +20,17 @@ const MarketViewCard = ({title, data, error, isLoading, volume}) => { else return <>
{data.pairInfo.baseAsset} - {t("currency." + data.pairInfo.baseAsset)} + {getCurrencyNameOrAlias(currencies[data.pairInfo.baseAsset], language)}
- {data.pairInfo.quoteAsset} - {new BN(volume ? data?.volume : data?.lastPrice).toFormat()} + {data.pairInfo.quoteAsset} + {new BN(volume ? data?.volume : data?.lastPrice).decimalPlaces(currencies[data.pairInfo.quoteAsset]?.precision ?? 0).toFormat()}
{data?.priceChangePercent && 0 ? "text-green" : "text-red"} direction-ltr`}> {new BN(data?.priceChangePercent).toFormat(2)} % diff --git a/src/utils/utils.js b/src/utils/utils.js index 7c36f68..1292114 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -72,4 +72,41 @@ export const shortenHash = (hash) => { return hash; } return `${hash.substring(0, 6)} - - - ${hash.substring(hash.length - 4)}`; -} \ No newline at end of file +} + +export function getCurrencyNameOrAlias(currency, lang) { + + const languagesConfig = { fa: "alias", en: "name", ar: "alias" }; + const langOption = languagesConfig[lang] || 'name'; + + if (!currency || typeof currency !== 'object') { + return '-'; + } + + const name = currency.name || '-'; + const alias = currency.alias || '-'; + + return langOption === 'alias' ? alias : name; +} + +export const formatWithPrecision = (value, precision, maxAttempts = 2) => { + + if (!value || isNaN(value) || value === Infinity || value === -Infinity) { + return "0"; + } + + let bnValue = new BN(value); + if (bnValue.isNaN()) return "0"; + + let currentPrecision = precision; + let formatted = bnValue.decimalPlaces(currentPrecision).toNumber(); + + let attempts = 0; + while (formatted === 0 && attempts < maxAttempts) { + currentPrecision += 1; + formatted = bnValue.decimalPlaces(currentPrecision).toNumber(); + attempts++; + } + + return bnValue.decimalPlaces(currentPrecision).toFormat(); +}; \ No newline at end of file From 8eeace6985ddcf5c29eac86e1caa6483bdd79be7 Mon Sep 17 00:00:00 2001 From: Hossein Date: Fri, 21 Feb 2025 18:03:37 +0330 Subject: [PATCH 03/16] =?UTF-8?q?#85=20feat:=20=C3=99Udate=20components=20?= =?UTF-8?q?to=20use=20currency=20service=20and=20pair=20managementt=20in?= =?UTF-8?q?=20landing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/MarketInfo/MarketInfo.js | 31 +++++++++--- .../MarketInfoTable/MarketInfoTable.js | 49 ++++++++++++++----- .../MarketInfoTable.module.css | 11 ++++- .../MarketViewCard/MarketViewCard.js | 2 +- src/queries/hooks/useGetChartData.js | 45 +++++++++++++++++ src/queries/index.js | 3 +- yarn.lock | 4 +- 7 files changed, 118 insertions(+), 27 deletions(-) create mode 100644 src/queries/hooks/useGetChartData.js diff --git a/src/main/Mobile/Pages/Landing/components/MarketInfo/MarketInfo.js b/src/main/Mobile/Pages/Landing/components/MarketInfo/MarketInfo.js index 5c22232..d763569 100644 --- a/src/main/Mobile/Pages/Landing/components/MarketInfo/MarketInfo.js +++ b/src/main/Mobile/Pages/Landing/components/MarketInfo/MarketInfo.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import classes from './MarketInfo.module.css' import Title from "../../../../../../components/Title/Title"; import {Link} from "react-router-dom"; @@ -11,6 +11,8 @@ import Error from "../../../../../../components/Error/Error"; import MarketInfoCard from "./components/MarketInfoCard/MarketInfoCard"; import {useTranslation} from "react-i18next"; import i18n from "i18next"; +import {useSelector} from "react-redux"; +import {getCurrencyNameOrAlias} from "../../../../../../utils/utils"; const MarketInfo = () => { const {t} = useTranslation(); @@ -20,17 +22,28 @@ const MarketInfo = () => { const interval = "24h" const quote = activeCurrency === "" ? null : activeCurrency + const currencies = useSelector((state) => state.exchange.currencies) + + const language = i18n.language + const {data: overview, isLoading, error} = useOverview(null, interval, quote) - const {data: currencies} = useGetQuoteCurrencies() + const {data: quoteCurrencies, isLoading:quoteCurrenciesIsLoading, error:quoteCurrenciesError} = useGetQuoteCurrencies() + + useEffect(() => { + if (quoteCurrencies?.length > 0) { + setActiveCurrency(quoteCurrencies[0]); + } + }, [quoteCurrencies]); + const content = () => { - if (isLoading) return
- if (error) return
+ if (isLoading || quoteCurrenciesIsLoading) return
+ if (error || quoteCurrenciesError) return
else return <> {card ? - + : - + } } @@ -61,8 +74,10 @@ const MarketInfo = () => {
- {currencies?.map((currency) => - setActiveCurrency(currency)} key={currency}>{t("currency." + currency)} + {quoteCurrencies?.map((currency) => + setActiveCurrency(currency)} key={currency}> + {getCurrencyNameOrAlias(currencies[currency], language)} + )}
diff --git a/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js b/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js index a05e1b2..080af5d 100644 --- a/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js +++ b/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js @@ -6,18 +6,28 @@ import {Order} from "../../../../../../Routes/routes"; import {useDispatch, useSelector} from "react-redux"; import {useNavigate} from "react-router-dom"; import {setActivePairInitiate} from "../../../../../../../../store/actions"; -import {BN} from "../../../../../../../../utils/utils"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; import {LeadingActions, SwipeableList, SwipeableListItem, SwipeAction, TrailingActions} from "react-swipeable-list"; import Button from "../../../../../../../../components/Button/Button"; import i18n from "i18next"; +import {useGetChartData} from "../../../../../../../../queries"; -const MarketInfoTable = ({data, activeCurrency}) => { +const MarketInfoTable = ({data, activeCurrency, interval}) => { const {t} = useTranslation(); const navigate = useNavigate(); const dispatch = useDispatch(); + + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) const allExchangeSymbols = useSelector((state) => state.exchange.symbols) + const pairsList = useSelector((state) => state.exchange.pairsList) + const symbols = Object.keys(pairsList); + + const { data: ChartData, isLoading: ChartDataIsLoading, error: ChartDataError } = useGetChartData(symbols, interval); + + const [swipRight, setSwipRight] = useState(null); const [swipLeft, setSwipLeft] = useState(null); @@ -80,9 +90,20 @@ const MarketInfoTable = ({data, activeCurrency}) => { ); + const chartView = (chartInfo) => { + if (ChartDataIsLoading) { + return ----- + } + if (ChartDataError || !(chartInfo?.svgData)) { + return + } + return {chartInfo?.symbol} + } + let body = ( <> {data.map((tr, index) => { + const chartInfo = ChartData?.find(chart => chart.symbol.replace("_", "") === tr.symbol); return ( { title={tr?.baseAsset} className={`img-md ml-1`} /> - {activeCurrency ? t("currency." + tr?.base) : tr?.base + " / " + tr?.quote} + {activeCurrency ? getCurrencyNameOrAlias(currencies[tr?.base], language) : tr?.base + " / " + tr?.quote}
- 0 ? "text-green" : "text-red"}`}>{tr.priceChange} % - 0 ? "text-green" : "text-red"}`}>{new BN(tr.lastPrice).toFormat()} - {t("currency." + tr?.quote)} + + 0 ? "text-green" : tr.priceChangePercent < 0 ? "text-red" : ""} direction-ltr}`}> + + {tr.priceChangePercent === 0 ? "0 %" : `${new BN(tr.priceChangePercent).toFormat(2)} %`} + + + + + 0 ? "text-green" : tr.priceChangePercent < 0 ? "text-red" : ""}`}>{new BN(tr.lastPrice).decimalPlaces(currencies[tr?.quote]?.precision ?? 0).toFormat()} + {getCurrencyNameOrAlias(currencies[tr?.quote], language)}
- {new BN(tr.volume).toFormat()} + {new BN(tr.volume).decimalPlaces(currencies[tr?.base]?.precision ?? 0).toFormat()}
@@ -121,12 +149,7 @@ const MarketInfoTable = ({data, activeCurrency}) => {
- {""} + {chartView(chartInfo)}
diff --git a/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css b/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css index 24a3c92..d8cc017 100644 --- a/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css +++ b/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css @@ -62,6 +62,13 @@ bottom: 0; } -.filter { - filter: blur(4px); +.chart { + width: 75%; +} +.filterUp { + filter: invert(51%) sepia(94%) saturate(370%) hue-rotate(109deg) brightness(87%) contrast(92%); + +} +.filterDown { + filter: invert(50%) sepia(53%) saturate(2527%) hue-rotate(337deg) brightness(68%) contrast(175%); } diff --git a/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js b/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js index cb786fd..8e27e3d 100644 --- a/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js +++ b/src/main/Mobile/Pages/Landing/components/MarketView/components/MarketViewCard/MarketViewCard.js @@ -32,7 +32,7 @@ const MarketViewCard = ({title, data, error, isLoading, volume}) => { {data.pairInfo.quoteAsset} {new BN(volume ? data?.volume : data?.lastPrice).decimalPlaces(currencies[data.pairInfo.quoteAsset]?.precision ?? 0).toFormat()} - {data?.priceChangePercent && 0 ? "text-green" : "text-red"} direction-ltr`}> + {data?.priceChangePercent && 0 ? "text-green" : data.priceChangePercent < 0 ? "text-red" : ""} direction-ltr`}> {new BN(data?.priceChangePercent).toFormat(2)} % } diff --git a/src/queries/hooks/useGetChartData.js b/src/queries/hooks/useGetChartData.js new file mode 100644 index 0000000..99879fb --- /dev/null +++ b/src/queries/hooks/useGetChartData.js @@ -0,0 +1,45 @@ +import { useQuery } from "@tanstack/react-query"; +import { fetchChartData } from "js-api-client"; +import axios from "axios"; + +const apiBaseUrl = window.env.REACT_APP_API_BASE_URL + + +/*export const useGetChartData = (symbols = [], period = "WEEKLY", config = {}) => { + return useQuery({ + queryKey: ["chartData", symbols, period], + queryFn: async () => { + const { data } = await fetchChartData({ symbols, period }); + return data; + }, + enabled: symbols.length > 0, + staleTime: 1000 * 60 * 5, + retry: 1, + ...config, + }); +};*/ + +export const useGetChartData = (symbols = [], period = "WEEKLY", config = {}) => { + return useQuery({ + queryKey: ["chartData", symbols, period], + + queryFn: async () => { + const params = new URLSearchParams(); + params.append("symbols", symbols.join(",")); + params.append("period", period); + + const { data } = await axios.get(`${apiBaseUrl}/market/v1/chart/spark-line?${params.toString()}`, { + headers: { + "Content-Type": "application/json", + }, + }); + + return data; + }, + enabled: symbols.length > 0, + staleTime: 1000 * 60 * 5, + retry: 1, + ...config, + }); +}; + diff --git a/src/queries/index.js b/src/queries/index.js index 3c57c4f..6bc3d1b 100644 --- a/src/queries/index.js +++ b/src/queries/index.js @@ -25,4 +25,5 @@ export {useGetUserAssetsEstimatedValue} from "./hooks/useGetUserAssetsEstimatedV export {useTransactionHistory} from "./hooks/useTransactionHistory"; export {useGetTransactionsHistory} from "./hooks/useGetTransactionsHistory"; export {useGetDepositHistory} from "./hooks/useGetDepositHistory.js"; -export {useGetWithdrawHistory} from "./hooks/useGetWithdrawHistory.js"; \ No newline at end of file +export {useGetWithdrawHistory} from "./hooks/useGetWithdrawHistory.js"; +export {useGetChartData} from "./hooks/useGetChartData.js"; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d2a596d..a2a1324 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8930,8 +8930,8 @@ __metadata: "js-api-client@https://github.com/opexdev/js-api-client.git#develop": version: 1.0.0-beta2 - resolution: "js-api-client@https://github.com/opexdev/js-api-client.git#commit=c352c8f2dc8f7d225090061d519ae579278ae960" - checksum: 61b0c4519dfd1dd0092d417d6cc28e7b97c0c425da37506c2a7c35d94db72e57bf1956787614cbc7059111f8438675a3b3745e50e5dd0b68b027ff8032d07069 + resolution: "js-api-client@https://github.com/opexdev/js-api-client.git#commit=80c1affbcf83d7da6962e541c5cbb5cdcf0933a1" + checksum: 5e5aca483a1c18eca58fb229d318de06f1200323ce122d18f42704b154a53592af1cf32f9a7e74dcb28375e8a29d644cc69ccc28d733dfeba302096f0bdcd297 languageName: node linkType: hard From dc641b100f6bdf86b0a6e5b2dda3417cf9c93396 Mon Sep 17 00:00:00 2001 From: Hossein Date: Fri, 21 Feb 2025 18:43:23 +0330 Subject: [PATCH 04/16] #85 feat: Integrated currency data from service in market page --- .../components/AllMarketInfo/AllMarketInfo.js | 29 ++++++++--- .../AllMarketInfoTable/AllMarketInfoTable.js | 50 ++++++++++++------- .../AllMarketInfoTable.module.css | 10 +++- .../MostDecreasedPrice/MostDecreasedPrice.js | 30 ++++------- .../MostIncreasedPrice/MostIncreasedPrice.js | 18 +++---- .../components/MostTrades/MostTrades.js | 30 ++++------- .../components/MostVolume/MostVolume.js | 37 ++++---------- 7 files changed, 100 insertions(+), 104 deletions(-) diff --git a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js index 0a07baa..2dbc005 100644 --- a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js +++ b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {useTranslation} from "react-i18next"; import classes from "./AllMarketInfo.module.css" import {useSelector} from "react-redux"; @@ -6,6 +6,8 @@ import {useGetQuoteCurrencies, useOverview} from "../../../../../../queries"; import AllMarketInfoTable from "./components/AllMarketInfoTable/AllMarketInfoTable"; import Loading from "../../../../../../components/Loading/Loading"; import Error from "../../../../../../components/Error/Error"; +import {getCurrencyNameOrAlias} from "../../../../../../utils/utils"; +import i18n from "i18next"; const AllMarketInfo = () => { @@ -17,17 +19,26 @@ const AllMarketInfo = () => { const interval = useSelector((state) => state.global.marketInterval) const quote = activeCurrency === "" ? null : activeCurrency + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const {data: overview, isLoading, error} = useOverview(null, interval, quote) - const {data: currencies} = useGetQuoteCurrencies() + const {data: quoteCurrencies, isLoading:quoteCurrenciesIsLoading, error:quoteCurrenciesError} = useGetQuoteCurrencies() + + useEffect(() => { + if (quoteCurrencies?.length > 0) { + setActiveCurrency(quoteCurrencies[0]); + } + }, [quoteCurrencies]); const content = () => { - if (isLoading) return
- if (error) return
+ if (isLoading || quoteCurrenciesIsLoading) return
+ if (error || quoteCurrenciesError) return
else return <> {card ? - + : - + } } @@ -42,8 +53,10 @@ const AllMarketInfo = () => {
- {currencies?.map((currency) => - setActiveCurrency(currency)} key={currency}>{t("currency." + currency)} + {quoteCurrencies?.map((currency) => + setActiveCurrency(currency)} key={currency}> + {getCurrencyNameOrAlias(currencies[currency], language)} + )}
diff --git a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js index 6576007..d053f0b 100644 --- a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js +++ b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js @@ -4,15 +4,15 @@ import {useNavigate} from "react-router-dom"; import {useTranslation} from "react-i18next"; import {useDispatch, useSelector} from "react-redux"; import {setActivePairInitiate} from "../../../../../../../../store/actions"; -import {images} from "../../../../../../../../assets/images"; import i18n from "i18next"; -import {BN} from "../../../../../../../../utils/utils"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; import {Order} from "../../../../../../Routes/routes"; import Button from "../../../../../../../../components/Button/Button"; import Icon from "../../../../../../../../components/Icon/Icon"; import {LeadingActions, SwipeableList, SwipeableListItem, SwipeAction, TrailingActions} from "react-swipeable-list"; +import {useGetChartData} from "../../../../../../../../queries"; -const AllMarketInfoTable = ({data, activeCurrency}) => { +const AllMarketInfoTable = ({data, activeCurrency, interval}) => { const {t} = useTranslation(); @@ -20,6 +20,14 @@ const AllMarketInfoTable = ({data, activeCurrency}) => { const dispatch = useDispatch(); const allExchangeSymbols = useSelector((state) => state.exchange.symbols) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + + const pairsList = useSelector((state) => state.exchange.pairsList) + const symbols = Object.keys(pairsList); + + const { data: ChartData, isLoading: ChartDataIsLoading, error: ChartDataError } = useGetChartData(symbols, interval); + const [swipeRight, setSwipeRight] = useState(null); const [swipeLeft, setSwipeLeft] = useState(null); @@ -81,9 +89,20 @@ const AllMarketInfoTable = ({data, activeCurrency}) => { ); + const chartView = (chartInfo) => { + if (ChartDataIsLoading) { + return ----- + } + if (ChartDataError || !(chartInfo?.svgData)) { + return + } + return {chartInfo?.symbol} + } + let body = ( <> {data.map((tr, index) => { + const chartInfo = ChartData?.find(chart => chart.symbol.replace("_", "") === tr.symbol); return ( {
{tr?.base} - {activeCurrency ? t("currency." + tr?.base) : tr?.base + " / " + tr?.quote} + {activeCurrency ? getCurrencyNameOrAlias(currencies[tr?.base], language) : tr?.base + " / " + tr?.quote}
- 0 ? "text-green" : "text-red"}`}>{tr.priceChange} % - 0 ? "text-green" : "text-red"}`}>{new BN(tr.lastPrice).toFormat()} {t("currency." + tr?.quote)} + 0 ? "text-green" : tr.priceChangePercent < 0 ? "text-red" : ""}`}>{tr.priceChangePercent === 0 ? "0 %" : `${new BN(tr.priceChangePercent).toFormat(2)} %`} + 0 ? "text-green" : tr.priceChangePercent < 0 ? "text-red" : ""}`}>{new BN(tr.lastPrice).decimalPlaces(currencies[tr?.quote]?.precision ?? 0).toFormat()} {tr?.quote}
- {new BN(tr.volume).toFormat()} + {new BN(tr?.volume).decimalPlaces(currencies[tr?.base]?.precision ?? 0).toFormat()}
- - {new BN(tr?.highPrice).toFormat()} + + {new BN(tr?.highPrice).decimalPlaces(currencies[tr?.quote]?.precision ?? 0).toFormat()}
- - {new BN(tr?.lowPrice).toFormat()} + + {new BN(tr?.lowPrice).decimalPlaces(currencies[tr?.quote]?.precision ?? 0).toFormat()}
@@ -140,12 +159,7 @@ const AllMarketInfoTable = ({data, activeCurrency}) => { />
- {""} + {chartView(chartInfo)}
diff --git a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css index 98fb9b6..fd3dc4d 100644 --- a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css +++ b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css @@ -59,6 +59,12 @@ bottom: 0; } -.filter { - filter: blur(4px); +.chart { + width: 75%; +} +.filterUp { + filter: invert(51%) sepia(94%) saturate(370%) hue-rotate(109deg) brightness(87%) contrast(92%); +} +.filterDown { + filter: invert(50%) sepia(53%) saturate(2527%) hue-rotate(337deg) brightness(68%) contrast(175%); } \ No newline at end of file diff --git a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostDecreasedPrice/MostDecreasedPrice.js b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostDecreasedPrice/MostDecreasedPrice.js index fe52187..56679f5 100644 --- a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostDecreasedPrice/MostDecreasedPrice.js +++ b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostDecreasedPrice/MostDecreasedPrice.js @@ -1,41 +1,29 @@ import React from 'react'; -import {images} from "../../../../../../../../assets/images"; import i18n from "i18next"; -import {BN} from "../../../../../../../../utils/utils"; -import {useTranslation} from "react-i18next"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; +import {useSelector} from "react-redux"; const MostDecreasedPrice = ({mostDecreasedPrice}) => { - const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) return ( <> - {/*{mostDecreasedPrice?.pairInfo?.baseAsset} - {t("currency." + mostDecreasedPrice?.pairInfo?.baseAsset)} -
- {mostDecreasedPrice?.pairInfo?.quoteAsset} - {new BN(mostDecreasedPrice?.lastPrice).toFormat()} -
-
- {new BN(mostDecreasedPrice?.priceChangePercent).toFormat(2)} % -
*/}
- {mostDecreasedPrice?.pairInfo?.baseAsset} - {t("currency." + mostDecreasedPrice?.pairInfo?.baseAsset)} + {getCurrencyNameOrAlias(currencies[mostDecreasedPrice?.pairInfo?.baseAsset], language)}
{mostDecreasedPrice?.pairInfo?.quoteAsset} - {new BN(mostDecreasedPrice?.lastPrice).toFormat()} + {new BN(mostDecreasedPrice?.lastPrice).decimalPlaces(currencies[mostDecreasedPrice.pairInfo.quoteAsset]?.precision ?? 0).toFormat()}
-
0 ? "text-green" : "text-red"} ${i18n.language !== "fa" ? 'jc-end' : 'jc-start'} direction-ltr width-100 row ai-center mt-05`}> - {new BN(mostDecreasedPrice?.priceChangePercent).toFormat(2)} % +
0 ? "text-green" : mostDecreasedPrice?.priceChangePercent < 0 ? "text-red" : ""} ${i18n.language !== "fa" ? 'jc-end' : 'jc-start'} direction-ltr width-100 row ai-center mt-05`}> + {mostDecreasedPrice?.priceChangePercent === 0 ? "0 %" : `${new BN(mostDecreasedPrice?.priceChangePercent).toFormat(2)} %`}
diff --git a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostIncreasedPrice/MostIncreasedPrice.js b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostIncreasedPrice/MostIncreasedPrice.js index 7c4b290..4b03578 100644 --- a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostIncreasedPrice/MostIncreasedPrice.js +++ b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostIncreasedPrice/MostIncreasedPrice.js @@ -1,29 +1,29 @@ import React from 'react'; -import {images} from "../../../../../../../../assets/images"; import i18n from "i18next"; -import {BN} from "../../../../../../../../utils/utils"; -import {useTranslation} from "react-i18next"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; +import {useSelector} from "react-redux"; const MostIncreasedPrice = ({mostIncreasedPrice}) => { - const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) return ( <>
- {mostIncreasedPrice?.pairInfo?.baseAsset} - {t("currency." + mostIncreasedPrice?.pairInfo?.baseAsset)} + {getCurrencyNameOrAlias(currencies[mostIncreasedPrice?.pairInfo?.baseAsset], language)}
{mostIncreasedPrice?.pairInfo?.quoteAsset} - {new BN(mostIncreasedPrice?.lastPrice).toFormat()} + {new BN(mostIncreasedPrice?.lastPrice).decimalPlaces(currencies[mostIncreasedPrice.pairInfo.quoteAsset]?.precision ?? 0).toFormat()}
-
0 ? "text-green" : "text-red"} ${i18n.language !== "fa" ? 'jc-end' : 'jc-start'} direction-ltr width-100 row ai-center mt-05`}> - {new BN(mostIncreasedPrice?.priceChangePercent).toFormat(2)} % +
0 ? "text-green" : mostIncreasedPrice?.priceChangePercent < 0 ? "text-red" : ""} ${i18n.language !== "fa" ? 'jc-end' : 'jc-start'} direction-ltr width-100 row ai-center mt-05`}> + {mostIncreasedPrice?.priceChangePercent === 0 ? "0 %" : `${new BN(mostIncreasedPrice?.priceChangePercent).toFormat(2)} %`}
diff --git a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostTrades/MostTrades.js b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostTrades/MostTrades.js index 706ce43..ff1ebc0 100644 --- a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostTrades/MostTrades.js +++ b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostTrades/MostTrades.js @@ -1,34 +1,24 @@ import React from 'react'; -import {images} from "../../../../../../../../assets/images"; import i18n from "i18next"; -import {BN} from "../../../../../../../../utils/utils"; -import {useTranslation} from "react-i18next"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; +import {useSelector} from "react-redux"; const MostTrades = ({mostTrades}) => { - const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) return ( <> - {/*{mostTrades?.pairInfo?.baseAsset} - {t("currency." + mostTrades?.pairInfo?.baseAsset)} -
- {new BN(mostTrades?.tradeCount).toFormat()} -
*/} - -
- {mostTrades?.pairInfo?.baseAsset} - {t("currency." + mostTrades?.pairInfo?.baseAsset)} + {mostTrades?.pairInfo?.baseAsset} + {getCurrencyNameOrAlias(currencies[mostTrades?.pairInfo?.baseAsset], language)}
-
+
{new BN(mostTrades?.tradeCount).toFormat()}
diff --git a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostVolume/MostVolume.js b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostVolume/MostVolume.js index e30bc62..ccafea2 100644 --- a/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostVolume/MostVolume.js +++ b/src/main/Mobile/Pages/AllMarket/components/MarketStats/components/MostVolume/MostVolume.js @@ -1,41 +1,26 @@ import React from 'react'; -import {images} from "../../../../../../../../assets/images"; import i18n from "i18next"; -import {BN} from "../../../../../../../../utils/utils"; -import {useTranslation} from "react-i18next"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; +import {useSelector} from "react-redux"; const MostVolume = ({mostVolume}) => { - const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) return ( <> - {/*{mostVolume?.pairInfo?.baseAsset} - {t("currency." + mostVolume?.pairInfo?.baseAsset)} -
- {mostVolume?.pairInfo?.quoteAsset} - {new BN(mostVolume?.volume).toFormat()} -
-
- % {new BN(mostVolume?.change).toFormat(2)}+ -
*/} - - -
- {mostVolume?.pairInfo?.baseAsset} - {t("currency." + mostVolume?.pairInfo?.baseAsset)} + {mostVolume?.pairInfo?.baseAsset} + {getCurrencyNameOrAlias(currencies[mostVolume?.pairInfo?.baseAsset], language)}
- {mostVolume?.pairInfo?.quoteAsset} - {new BN(mostVolume?.volume).toFormat()} + {mostVolume?.pairInfo?.baseAsset} + {new BN(mostVolume?.volume).decimalPlaces(currencies[mostVolume?.pairInfo?.baseAsset]?.precision ?? 0).toFormat()}
From b5df989a6260b8fe923442aa466e6346d0b9104a Mon Sep 17 00:00:00 2001 From: Hossein Date: Thu, 17 Apr 2025 16:39:16 +0330 Subject: [PATCH 05/16] #85 feat: Integrated currency service for buy and sell order --- .../Order/components/BuyOrder/BuyOrder.js | 88 +++++++++++-------- .../Order/components/SellOrder/SellOrder.js | 77 +++++++++------- .../Market/components/OrderBook/OrderBook.js | 9 +- .../OrderBookTable/OrderBookTable.js | 14 +-- .../InformationBlock/InformationBlock.js | 22 +++-- 5 files changed, 121 insertions(+), 89 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/Order/components/BuyOrder/BuyOrder.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/Order/components/BuyOrder/BuyOrder.js index 5abea37..015fd21 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/Order/components/BuyOrder/BuyOrder.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/Order/components/BuyOrder/BuyOrder.js @@ -3,7 +3,12 @@ import classes from "../../Order.module.css"; import {Trans, useTranslation} from "react-i18next"; import {setLastTransaction} from "../../../../../../../../../../../../store/actions/auth"; import {useDispatch, useSelector} from "react-redux"; -import {BN, parsePriceString} from "../../../../../../../../../../../../utils/utils"; +import { + BN, + formatWithPrecision, + getCurrencyNameOrAlias, + parsePriceString +} from "../../../../../../../../../../../../utils/utils"; import {useNavigate} from "react-router-dom"; import Button from "../../../../../../../../../../../../components/Button/Button"; import {Login as LoginRoute} from "../../../../../../../../../../Routes/routes"; @@ -12,6 +17,7 @@ import {images} from "../../../../../../../../../../../../assets/images"; import {createOrder} from "../../api/order"; import {useGetUserAccount} from "../../../../../../../../../../../../queries/hooks/useGetUserAccount"; import NumberInput from "../../../../../../../../../../../../components/NumberInput/NumberInput"; +import i18n from "i18next"; const BuyOrder = () => { @@ -31,6 +37,9 @@ const BuyOrder = () => { const tradeFee = useSelector((state) => state.auth.tradeFee) const isLogin = useSelector((state) => state.auth.isLogin) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const quote = userAccount?.wallets[activePair.quoteAsset]?.free || 0; const [alert, setAlert] = useState({ @@ -75,26 +84,27 @@ const BuyOrder = () => { }, [activePair]) const currencyValidator = (key, val, rule) => { - if (!val.isZero() && val.isLessThan(rule.min)) { + + if (!val.isZero() && val.isLessThan(currencies[rule].minOrder)) { return setAlert({ ...alert, [key]: ( ), }); } - if (!val.mod(rule.step).isZero()) { + if (!val.mod(currencies?.[rule]?.step).isZero()) { return setAlert({ ...alert, [key]: () }) } @@ -106,12 +116,12 @@ const BuyOrder = () => { switch (key) { case "reqAmount": const reqAmount = new BN(value); - currencyValidator("reqAmount", reqAmount, activePair.baseRange); + currencyValidator("reqAmount", reqAmount, activePair.baseAsset); setOrder({ ...order, reqAmount, - totalPrice: reqAmount.multipliedBy(order.pricePerUnit).decimalPlaces(activePair.quoteAssetPrecision), - tradeFee: reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(activePair.baseAssetPrecision), + totalPrice: reqAmount.multipliedBy(order.pricePerUnit).decimalPlaces(currencies[activePair.quoteAsset].precision), + tradeFee: reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(currencies[activePair.baseAsset].precision), }); break; case "pricePerUnit": @@ -119,20 +129,20 @@ const BuyOrder = () => { setOrder({ ...order, pricePerUnit: pricePerUnit, - totalPrice: pricePerUnit.multipliedBy(order.reqAmount).decimalPlaces(activePair.quoteAssetPrecision), - tradeFee: order.reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(activePair.baseAssetPrecision), + totalPrice: pricePerUnit.multipliedBy(order.reqAmount).decimalPlaces(currencies[activePair.quoteAsset].precision), + tradeFee: order.reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(currencies[activePair.baseAsset].precision), }); break; case "totalPrice": const totalPrice = new BN(value); - const req = totalPrice.dividedBy(order.pricePerUnit).decimalPlaces(activePair.baseAssetPrecision); + const req = totalPrice.dividedBy(order.pricePerUnit).decimalPlaces(currencies[activePair.baseAsset].precision); setOrder({ ...order, reqAmount: req.isFinite() ? req : new BN(0), totalPrice, - tradeFee: req.isFinite() ? req.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(activePair.baseAssetPrecision) : new BN(0), + tradeFee: req.isFinite() ? req.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(currencies[activePair.baseAsset].precision) : new BN(0), }); - currencyValidator("reqAmount", req, activePair.baseRange); + currencyValidator("reqAmount", req, activePair.baseAsset); break; default: } @@ -154,7 +164,7 @@ const BuyOrder = () => { useEffect(() => { setOrder((prevState) => ({ ...order, - tradeFee: prevState.totalPrice.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(activePair.baseAssetPrecision), + tradeFee: prevState.totalPrice.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(currencies[activePair.baseAsset].precision), })); }, [tradeFee]); @@ -172,10 +182,10 @@ const BuyOrder = () => { ...order, reqAmount, pricePerUnit: pricePerUnit, - totalPrice: reqAmount.multipliedBy(pricePerUnit).decimalPlaces(activePair.quoteAssetPrecision), - tradeFee: reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(activePair.baseAssetPrecision), + totalPrice: reqAmount.multipliedBy(pricePerUnit).decimalPlaces(currencies[activePair.quoteAsset].precision), + tradeFee: reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(currencies[activePair.baseAsset].precision), }); - currencyValidator("reqAmount", reqAmount, activePair.baseRange); + currencyValidator("reqAmount", reqAmount, activePair.baseAsset); }, [selectedBuyOrder]); @@ -184,9 +194,9 @@ const BuyOrder = () => { if (order.pricePerUnit.isEqualTo(0)) { const pricePerUnit = new BN(bestBuyPrice) let totalPrice = new BN(quote); - let reqAmount = totalPrice.dividedBy(pricePerUnit).decimalPlaces(activePair.baseAssetPrecision) - if (!reqAmount.mod(activePair.baseRange.step).isZero()) { - reqAmount = reqAmount.minus(reqAmount.mod(activePair.baseRange.step)); + let reqAmount = totalPrice.dividedBy(pricePerUnit).decimalPlaces(currencies[activePair.baseAsset].precision) + if (!reqAmount.mod(currencies[activePair.baseAsset].step).isZero()) { + reqAmount = reqAmount.minus(reqAmount.mod(currencies[activePair.baseAsset].step)); totalPrice = reqAmount.multipliedBy(pricePerUnit); } setOrder({ @@ -194,13 +204,13 @@ const BuyOrder = () => { reqAmount, pricePerUnit, totalPrice, - tradeFee: reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(activePair.baseAssetPrecision), + tradeFee: reqAmount.multipliedBy(tradeFee[activePair.quoteAsset]).decimalPlaces(currencies[activePair.baseAsset].precision), }); } else { let totalPrice = new BN(quote); - let reqAmount = totalPrice.dividedBy(order.pricePerUnit).decimalPlaces(activePair.baseAssetPrecision) - if (!reqAmount.mod(activePair.baseRange.step).isZero()) { - reqAmount = reqAmount.minus(reqAmount.mod(activePair.baseRange.step)); + let reqAmount = totalPrice.dividedBy(order.pricePerUnit).decimalPlaces(currencies[activePair.baseAsset].precision) + if (!reqAmount.mod(currencies[activePair.baseAsset].step).isZero()) { + reqAmount = reqAmount.minus(reqAmount.mod(currencies[activePair.baseAsset].step)); } buyPriceHandler( reqAmount.toFormat(), @@ -239,8 +249,8 @@ const BuyOrder = () => { toast.success( {
{fillBuyByWallet()}}> {t("orders.availableAmount")}: - {new BN(quote).toFormat()}{" "}{t("currency." + activePair.quoteAsset)} + {new BN(quote).toFormat()}{" "}{getCurrencyNameOrAlias(currencies[activePair.quoteAsset], language)}
fillBuyByBestPrice()}> {t("orders.bestOffer")}: - {new BN(bestBuyPrice).toFormat()}{" "}{t("currency." + activePair.quoteAsset)} + {formatWithPrecision(bestBuyPrice, currencies[activePair.quoteAsset]?.precision ?? 0)}{" "}{getCurrencyNameOrAlias(currencies[activePair.quoteAsset], language)}
buyPriceHandler(e.target.value, "reqAmount")} alert={alert.reqAmount} customClass={`${classes.smallInput} fs-0-8`} @@ -294,9 +304,9 @@ const BuyOrder = () => { /> buyPriceHandler(e.target.value, "pricePerUnit")} customClass={`${classes.smallInput} fs-0-8 my-05`} isAllowed={isAllowed} @@ -304,8 +314,8 @@ const BuyOrder = () => { buyPriceHandler(e.target.value, "totalPrice")} customClass={`${classes.smallInput} fs-0-8`} alert={alert.totalPrice} @@ -315,13 +325,13 @@ const BuyOrder = () => {

{t("orders.tradeFee")}:{" "} - {order.tradeFee.toFormat()}{" "} - {t("currency." + activePair.baseAsset)} + {formatWithPrecision(order.tradeFee, currencies[activePair.baseAsset]?.precision ?? 0)}{" "} + {getCurrencyNameOrAlias(currencies[activePair.baseAsset], language)}

{t("orders.getAmount")}:{" "} - {order.reqAmount.minus(order.tradeFee).decimalPlaces(activePair.baseAssetPrecision).toFormat()}{" "} - {t("currency." + activePair.baseAsset)} + {order.reqAmount.minus(order.tradeFee).decimalPlaces(currencies[activePair.baseAsset].precision).toFormat()}{" "} + {getCurrencyNameOrAlias(currencies[activePair.baseAsset], language)}

@@ -78,8 +82,8 @@ const OpenOrders = () => {
{t("totalPrice")}:
- {totalPrice.decimalPlaces(activePair.quoteAssetPrecision).toFormat()} - {t("currency." + activePair.quoteAsset.toUpperCase())} + {totalPrice.decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.quoteAsset.toUpperCase()], language)}
@@ -89,8 +93,8 @@ const OpenOrders = () => {
{t("pricePerUnit")}:
- {pricePerUnit.decimalPlaces(activePair.quoteAssetPrecision).toFormat()} - {t("currency." + activePair.quoteAsset.toUpperCase())} + {pricePerUnit.decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.quoteAsset.toUpperCase()], language)}
@@ -105,7 +109,7 @@ const OpenOrders = () => {
{t("myOrders.tradedAmount")}:
- {executedQty.decimalPlaces(activePair.baseAssetPrecision).toFormat()} + {executedQty.decimalPlaces(currencies[activePair.baseAsset].precision).toFormat()}
@@ -120,7 +124,7 @@ const OpenOrders = () => {
{t("myOrders.tradedPrice")}:
- {executedQty.multipliedBy(pricePerUnit).decimalPlaces(activePair.baseAssetPrecision).toFormat()} + {executedQty.multipliedBy(pricePerUnit).decimalPlaces(currencies[activePair.baseAsset].precision).toFormat()}
diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/OrdersHistory/OrdersHistory.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/OrdersHistory/OrdersHistory.js index 0267921..9db3185 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/OrdersHistory/OrdersHistory.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/OrdersHistory/OrdersHistory.js @@ -8,6 +8,8 @@ import ScrollBar from "../../../../../../../../../../../../components/ScrollBar" import {useMyOrderHistory} from "../../../../../../../../../../../../queries"; import Error from "../../../../../../../../../../../../components/Error/Error"; import Date from "../../../../../../../../../../../../components/Date/Date"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../../../../../utils/utils"; +import i18n from "i18next"; const OrdersHistory = () => { const {t} = useTranslation(); @@ -16,6 +18,9 @@ const OrdersHistory = () => { const activePair = useSelector((state) => state.exchange.activePair) const lastTransaction = useSelector((state) => state.auth.lastTransaction); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const {data, isLoading, error, refetch} = useMyOrderHistory(activePair.symbol) useEffect(() => { @@ -46,8 +51,8 @@ const OrdersHistory = () => {
{t("volume")}:
- {tr.origQty} - {t("currency." + activePair.baseAsset.toUpperCase())} + {new BN(tr.origQty).decimalPlaces(currencies[activePair.baseAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.baseAsset.toUpperCase()], language)}
@@ -56,22 +61,18 @@ const OrdersHistory = () => {
{t("totalPrice")}:
- {(tr.origQty * tr.price).toLocaleString()} - {t("currency." + activePair.quoteAsset.toUpperCase())} + {new BN(tr.origQty).multipliedBy(tr.price).decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.quoteAsset.toUpperCase()], language)}
- - - - {t("pricePerUnit")}: -
- {tr.price.toLocaleString()} - {t("currency." + activePair.quoteAsset.toUpperCase())} -
- + {t("pricePerUnit")}: +
+ {new BN(tr.price).decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.quoteAsset.toUpperCase()], language)} +
diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/Trades/Trades.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/Trades/Trades.js index 4ed062d..cb8b902 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/Trades/Trades.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/MyOrder/components/Trades/Trades.js @@ -8,7 +8,8 @@ import ScrollBar from "../../../../../../../../../../../../components/ScrollBar" import Error from "../../../../../../../../../../../../components/Error/Error"; import {useMyTrades} from "../../../../../../../../../../../../queries"; import Date from "../../../../../../../../../../../../components/Date/Date"; -import {BN} from "../../../../../../../../../../../../utils/utils"; +import {BN, formatWithPrecision, getCurrencyNameOrAlias} from "../../../../../../../../../../../../utils/utils"; +import i18n from "i18next"; const Trades = () => { @@ -21,6 +22,9 @@ const Trades = () => { const {data, isLoading, error, refetch} = useMyTrades(activePair.symbol) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + useEffect(() => { refetch() }, [lastTransaction]) @@ -48,8 +52,8 @@ const Trades = () => {
{t("volume")}:
- {tr.qty} - {t("currency." + activePair.baseAsset.toUpperCase())} + {new BN(tr.qty).decimalPlaces(currencies[activePair.baseAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.baseAsset.toUpperCase()], language)}
@@ -57,19 +61,17 @@ const Trades = () => {
{t("totalPrice")}:
- {(tr.qty * tr.price).toLocaleString()} - {t("currency." + activePair.quoteAsset.toUpperCase())} + {new BN(tr.qty).multipliedBy(tr.price).decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.quoteAsset.toUpperCase()], language)}
-
-
{t("pricePerUnit")}:
- {(tr.price)} - {t("currency." + activePair.quoteAsset.toUpperCase())} + {new BN(tr.price).decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair.quoteAsset.toUpperCase()], language)}
@@ -82,8 +84,8 @@ const Trades = () => {
{t("commission")}:
- {new BN(tr.commission).toFormat()} - {t("currency." + tr.commissionAsset.toUpperCase())} + {formatWithPrecision(tr.commission, currencies[tr.commissionAsset.toUpperCase()].precision)} + {getCurrencyNameOrAlias(currencies[tr.commissionAsset.toUpperCase()], language)}
From 1a13a84129ce344adf3cd35010fa0ea60ef6afb0 Mon Sep 17 00:00:00 2001 From: Hossein Date: Mon, 21 Apr 2025 18:52:53 +0330 Subject: [PATCH 07/16] #85 feat: Use new currency data in last trades section --- .../components/LastTradesTable/LastTradesTable.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/LastTrades/components/LastTradesTable/LastTradesTable.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/LastTrades/components/LastTradesTable/LastTradesTable.js index f5d4c71..6362f17 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/LastTrades/components/LastTradesTable/LastTradesTable.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/Market/components/LastTrades/components/LastTradesTable/LastTradesTable.js @@ -6,10 +6,15 @@ import {useSelector} from "react-redux"; import ScrollBar from "../../../../../../../../../../../../components/ScrollBar"; import {BN} from "../../../../../../../../../../../../utils/utils"; import Date from "../../../../../../../../../../../../components/Date/Date"; +import i18n from "i18next"; const LastTradesTable = ({data}) => { const {t} = useTranslation(); const activePair = useSelector((state) => state.exchange.activePair) + + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + return (
@@ -37,9 +42,9 @@ const LastTradesTable = ({data}) => { {moment(tr.time).format("HH:mm:ss")} - {amount.decimalPlaces(activePair.baseAssetPrecision).toFormat()} - {pricePerUnit.decimalPlaces(activePair.quoteAssetPrecision).toFormat()} - {totalPrice.decimalPlaces(activePair.quoteAssetPrecision).toFormat()} + {amount.decimalPlaces(currencies[activePair.baseAsset].precision).toFormat()} + {pricePerUnit.decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} + {totalPrice.decimalPlaces(currencies[activePair.quoteAsset].precision).toFormat()} ); })} From f51b9523263e0a47316e3a9954504ca74d79a054 Mon Sep 17 00:00:00 2001 From: Hossein Date: Mon, 21 Apr 2025 19:12:53 +0330 Subject: [PATCH 08/16] #85 feat: Integrated sub-header of Wallet and Market with Currency Service --- .../MarketSubHeader/MarketSubHeader.js | 17 ++++++++++------ .../WalletSubHeader/WalletSubHeader.js | 20 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/MarketSubHeader/MarketSubHeader.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/MarketSubHeader/MarketSubHeader.js index e877279..a3c19e5 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/MarketSubHeader/MarketSubHeader.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/MarketSubHeader/MarketSubHeader.js @@ -3,14 +3,19 @@ import {useSelector} from "react-redux"; import {useTranslation} from "react-i18next"; import {useGetLastPrices} from "../../../../../../../../queries/hooks/useGetLastPrices"; import {useGetUserAccount} from "../../../../../../../../queries/hooks/useGetUserAccount"; -import {BN} from "../../../../../../../../utils/utils"; +import {BN, getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; import classes from "./MarketSubHeader.module.css" +import i18n from "i18next"; const MarketSubHeader = () => { const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const activePair = useSelector((state) => state.exchange.activePair) + const {data: lastPrices} = useGetLastPrices() const {data: userAccount} = useGetUserAccount() @@ -23,20 +28,20 @@ const MarketSubHeader = () => { {t("header.lastPrice")}
- {new BN(lastPrices[activePair.symbol] || 0).toFormat()+" "+t("currency." + activePair.quoteAsset)} + {" "}{new BN(lastPrices[activePair.symbol] || 0).decimalPlaces(currencies[activePair?.quoteAsset]?.precision ?? 0).toFormat() +" "+ getCurrencyNameOrAlias(currencies[activePair?.quoteAsset], language)}
{t("header.availableBalance")}
- { new BN (base).toFormat()} - {t("currency." + activePair.baseAsset)} + {new BN (base).decimalPlaces(currencies[activePair?.baseAsset]?.precision ?? 0).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair?.baseAsset], language)}
|
- { new BN(quote).toFormat()} - {t("currency." + activePair.quoteAsset)} + {new BN(quote).decimalPlaces(currencies[activePair?.quoteAsset]?.precision ?? 0).toFormat()} + {getCurrencyNameOrAlias(currencies[activePair?.quoteAsset], language)}
diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js index ebaeaaa..56083a2 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js @@ -4,13 +4,19 @@ import {useParams} from "react-router-dom"; import {useGetUserAssets} from "../../../../../../../../queries"; import {useTranslation} from "react-i18next"; import {useGetUserAccount} from "../../../../../../../../queries/hooks/useGetUserAccount"; -import {BN} from "../../../../../../../../utils/utils"; +import {BN, formatWithPrecision} from "../../../../../../../../utils/utils"; +import i18n from "i18next"; +import {useSelector} from "react-redux"; const WalletSubHeader = () => { const {id} = useParams() const {t} = useTranslation() const refCurrency = window.env.REACT_APP_REFERENCE_FIAT_CURRENCY + + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const {data: userAccount} = useGetUserAccount() const {data: estimateValue , isLoading, error} = useGetUserAssets(refCurrency) @@ -21,18 +27,18 @@ const WalletSubHeader = () => {
{t("header.free")} - {new BN(userAccount?.wallets[id]?.free || 0).toFormat()} - ( {refCurrency === id ? new BN(userAccount?.wallets[id]?.free || 0).toFormat() : new BN(allEstimateValue?.free || 0).toFormat()} {t("currency."+refCurrency)} ) + {new BN(userAccount?.wallets[id]?.free || 0).decimalPlaces(currencies[id]?.precision ?? 0).toFormat()} + ({ refCurrency === id ? formatWithPrecision(userAccount?.wallets[id]?.free || 0, currencies[id]?.precision ?? 0) : formatWithPrecision(allEstimateValue?.free || 0, currencies[refCurrency]?.precision ?? 0)} {refCurrency} )
{t("header.locked")} - {new BN(userAccount?.wallets[id]?.locked || 0).toFormat()} - ( {refCurrency === id ? new BN(userAccount?.wallets[id]?.locked || 0).toFormat() : new BN(allEstimateValue?.locked || 0).toFormat()} {t("currency."+refCurrency)} ) + {new BN(userAccount?.wallets[id]?.locked || 0).decimalPlaces(currencies[id]?.precision ?? 0).toFormat()} + ({ refCurrency === id ? formatWithPrecision(userAccount?.wallets[id]?.locked || 0, currencies[id]?.precision ?? 0) : formatWithPrecision(allEstimateValue?.locked || 0, currencies[refCurrency]?.precision ?? 0)} {refCurrency} )
{t("header.inWithdrawalProcess")} - {new BN(userAccount?.wallets[id]?.withdraw || 0).toFormat()} - ( {refCurrency === id ? new BN(userAccount?.wallets[id]?.withdraw || 0).toFormat() : new BN(allEstimateValue?.withdrawing || 0).toFormat()} {t("currency."+refCurrency)} ) + {new BN(userAccount?.wallets[id]?.withdraw || 0).decimalPlaces(currencies[id]?.precision ?? 0).toFormat()} + ({ refCurrency === id ? formatWithPrecision(userAccount?.wallets[id]?.withdraw || 0, currencies[id]?.precision ?? 0) : formatWithPrecision(allEstimateValue?.withdrawing || 0, currencies[refCurrency]?.precision ?? 0)} {refCurrency} )
); From 5f1175132315a419766648a6a228f2c0cead8c90 Mon Sep 17 00:00:00 2001 From: Hossein Date: Mon, 21 Apr 2025 19:25:48 +0330 Subject: [PATCH 09/16] #85 feat: Updated Market sub-menu using Currency Service --- .../components/MarketSubMenu/MarketSubMenu.js | 11 +++++--- .../components/MarketCard/MarketCard.js | 7 +++-- .../MarketPairCard/MarketPairCard.js | 26 ++++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/MarketSubMenu.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/MarketSubMenu.js index 25b7173..2bf84e4 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/MarketSubMenu.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/MarketSubMenu.js @@ -12,6 +12,9 @@ const MarketSubMenu = () => { const {t} = useTranslation(); const [activeTab] = useState(JSON.parse(localStorage.getItem("activeMarketTab")) || 1); const symbols = useSelector((state) => state.exchange.symbols) + + const pairsList = useSelector((state) => state.exchange.pairsList) + const fav = useSelector((state) => state.auth.favoritePairs) const dispatch = useDispatch(); @@ -36,7 +39,7 @@ const MarketSubMenu = () => { fav.includes(p.symbol))} + pairs={Object.values(pairsList).filter(pair => fav.includes(pair.symbol))} favPair={fav} addFav={(selected) => addToFav(selected)} /> @@ -48,7 +51,7 @@ const MarketSubMenu = () => { addToFav(selected)} /> @@ -62,7 +65,7 @@ const MarketSubMenu = () => { id="2" type="BTC" favPair={fav} - pairs={symbols.filter(p => (p.baseAsset === "BTC" || p.quoteAsset === "BTC"))} + pairs={Object.values(pairsList).filter(pair => pair.baseAsset === "BTC" || pair.quoteAsset === "BTC")} addFav={(selected) => addToFav(selected)} /> ), @@ -75,7 +78,7 @@ const MarketSubMenu = () => { id="4" type="USDT" favPair={fav} - pairs={symbols.filter(p => (p.baseAsset === "USDT" || p.quoteAsset === "USDT"))} + pairs={Object.values(pairsList).filter(pair => pair.baseAsset === "USDT" || pair.quoteAsset === "USDT")} addFav={(selected) => addToFav(selected)} /> ), diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketCard/MarketCard.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketCard/MarketCard.js index 672adca..f2a51fb 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketCard/MarketCard.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketCard/MarketCard.js @@ -1,5 +1,4 @@ import React from "react"; -import {useSelector} from "react-redux"; import ScrollBar from "../../../../../../../../../../components/ScrollBar"; import MarketPairCard from "../MarketPairCard/MarketPairCard"; @@ -8,7 +7,11 @@ const MarketCard = ({type, ...props}) => { return (
- {props.pairs.map((pair) => )} + { + Object + .keys(props?.pairs) + .map((pair) => ) + }
); diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js index f6ffeed..c0a7d88 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js @@ -3,10 +3,11 @@ import classes from "../MarketCard/MarketCard.module.css"; import {useDispatch, useSelector} from "react-redux"; import {images} from "../../../../../../../../../../assets/images"; import Icon from "../../../../../../../../../../components/Icon/Icon"; -import {BN} from "../../../../../../../../../../utils/utils"; +import {BN, formatWithPrecision} from "../../../../../../../../../../utils/utils"; import {setActivePairInitiate} from "../../../../../../../../../../store/actions"; import {activeActionSheet} from "../../../../../../../../../../store/actions/global"; import {useGetLastPrices} from "../../../../../../../../../../queries/hooks/useGetLastPrices"; +import i18n from "i18next"; const MarketPairCard = ({id, pair,favPair,addFav}) => { @@ -14,9 +15,14 @@ const MarketPairCard = ({id, pair,favPair,addFav}) => { const activePair = useSelector((state) => state.exchange.activePair.symbol) const {data: prices} = useGetLastPrices() const dispatch = useDispatch(); + + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const changeActivePair = () =>{ - if (activePair !== pair.symbol) { - dispatch(setActivePairInitiate(pair, id)) + const pairSymbolFormatted = `${pair.baseAsset}_${pair.quoteAsset}`; + if (activePair !== pairSymbolFormatted) { + dispatch(setActivePairInitiate(`${pair.baseAsset}_${pair.quoteAsset}`, id)); dispatch(activeActionSheet({ menu: false, subMenu: false, @@ -29,27 +35,27 @@ const MarketPairCard = ({id, pair,favPair,addFav}) => {
{pair.symbol}
{ e.stopPropagation(); - addFav(pair.symbol); - }} data-name={pair.symbol}> + addFav(pair?.symbol); + }} data-name={pair?.symbol}>
- {pair.baseAsset +"/"+pair.quoteAsset} + {pair?.baseAsset + " / " + pair?.quoteAsset}
- {new BN(prices[pair.symbol] || 0).toFormat()} + {formatWithPrecision(prices[pair?.symbol] || 0, currencies[pair?.quoteAsset]?.precision ?? 0)}
From c6a9fda05860d03e2c26af6808124aa8a544c9cf Mon Sep 17 00:00:00 2001 From: Hossein Date: Mon, 21 Apr 2025 20:21:12 +0330 Subject: [PATCH 10/16] #85 refactor: Integrated currency service into Wallet submenu components --- .../WalletSubHeader/WalletSubHeader.js | 2 +- .../components/WalletSubMenu/WalletSubMenu.js | 35 +++++++++++++++++-- .../WalletSubMenu/WalletSubMenu.module.css | 5 +++ .../components/WalletBalance/WalletBalance.js | 11 ++++-- .../WalletListItem/WalletListItem.js | 23 +++++++----- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js index 56083a2..1383bef 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubHeader/components/WalletSubHeader/WalletSubHeader.js @@ -12,7 +12,7 @@ const WalletSubHeader = () => { const {id} = useParams() const {t} = useTranslation() - const refCurrency = window.env.REACT_APP_REFERENCE_FIAT_CURRENCY + const refCurrency = useSelector((state) => state.exchange.baseCurrency) const language = i18n.language const currencies = useSelector((state) => state.exchange.currencies) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/WalletSubMenu.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/WalletSubMenu.js index 0e8c4d6..9962ef8 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/WalletSubMenu.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/WalletSubMenu.js @@ -8,15 +8,35 @@ import WalletListItem from "./components/WalletListItem/WalletListItem"; import WalletBalance from "./components/WalletBalance/WalletBalance"; import {useGetUserAccount} from "../../../../../../../../queries/hooks/useGetUserAccount"; import Loading from "../../../../../../../../components/Loading/Loading"; +import i18n from "i18next"; const WalletSubMenu = () => { const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) const [showZero, setShowZero] = useState(false); const assets = useSelector((state) => state.exchange.assets) const {data: data, isLoading} = useGetUserAccount() + const wallets = Object.keys(currencies) + .map(symbol => ({ + symbol, + name: currencies[symbol].name, + alias: currencies[symbol].alias, + icon: currencies[symbol].icon, + order: currencies[symbol].order, + isActive: currencies[symbol].isActive, + free: data?.wallets?.[symbol]?.free || 0, + })) + .filter(wallet => wallet.isActive || wallet.free > 0); + + wallets.sort((a, b) => { + if (b.free !== a.free) return b.free - a.free; + return a.order - b.order; + }); + return (
@@ -32,9 +52,18 @@ const WalletSubMenu = () => {
- {assets.filter(asset => data?.wallets?.[asset]?.free > 0) - .concat(assets.filter(asset => data?.wallets?.[asset]?.free === 0)) - .map((name) => )} + { + wallets.map((wallet) => ( + + )) + }
{ const {t} = useTranslation(); - const refCurrency = window.env.REACT_APP_REFERENCE_FIAT_CURRENCY + const refCurrency = useSelector((state) => state.exchange.baseCurrency) const {data , isLoading, error} = useGetUserAssetsEstimatedValue(refCurrency) const totalValue = (isLoading || error) ? 0 : data.value + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + return (
{ {t("WalletSubMenu.approximate")}
- {new BN(totalValue).toFormat()}{" "}{t("currency."+refCurrency)} + {formatWithPrecision(totalValue, currencies[refCurrency]?.precision ?? 0)}{" "}{getCurrencyNameOrAlias(currencies[refCurrency], language)}
diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/components/WalletListItem/WalletListItem.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/components/WalletListItem/WalletListItem.js index 29342e1..ee6eb26 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/components/WalletListItem/WalletListItem.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/WalletSubMenu/components/WalletListItem/WalletListItem.js @@ -4,20 +4,25 @@ import {NavLink, useParams} from "react-router-dom"; import {useTranslation} from "react-i18next"; import {images} from "../../../../../../../../../../assets/images"; import * as Routes from "../../../../../../../../Routes/routes"; -import {BN} from "../../../../../../../../../../utils/utils"; +import {BN, formatWithPrecision, getCurrencyNameOrAlias} from "../../../../../../../../../../utils/utils"; import {useGetUserAccount} from "../../../../../../../../../../queries/hooks/useGetUserAccount"; import {useGetUserAssets} from "../../../../../../../../../../queries"; +import i18n from "i18next"; +import {useSelector} from "react-redux"; -const WalletListItem = ({assetName, showZero}) => { +const WalletListItem = ({symbol, data, assetName, freeWallet, showZero}) => { const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) const {path} = useParams() - const refCurrency = window.env.REACT_APP_REFERENCE_FIAT_CURRENCY + const refCurrency = useSelector((state) => state.exchange.baseCurrency) const {data: userAccount} = useGetUserAccount() const free = userAccount?.wallets[assetName]?.free || 0 const {data: estimateValue , isLoading, error} = useGetUserAssets(refCurrency) const freeEstimateValue = (isLoading || error) ? 0 : (estimateValue?.find( q => q.asset === assetName )?.free || 0) + const active = currencies[symbol]?.isActive if (showZero && free === 0) return <> @@ -31,21 +36,21 @@ const WalletListItem = ({assetName, showZero}) => {
{assetName}
- {assetName} - {t("currency." + assetName)} + {symbol} + {getCurrencyNameOrAlias(currencies[symbol], language)}
- {new BN(free).toFormat() + " "} {t("currency." + assetName)} + {new BN(freeWallet).decimalPlaces(currencies[symbol]?.precision ?? 0).toFormat() + " "}{getCurrencyNameOrAlias(currencies[symbol], language)} - ~ {refCurrency === assetName ? new BN(free).toFormat() : new BN(freeEstimateValue).toFormat()} {t("currency."+refCurrency)} + ~ {refCurrency === assetName ? formatWithPrecision(free, currencies[refCurrency]?.precision ?? 0) : formatWithPrecision(freeEstimateValue, currencies[refCurrency]?.precision ?? 0)} {getCurrencyNameOrAlias(currencies[refCurrency], language)}
From b6987b7b760e6c9e78754ed1b4a31ac2038aacfb Mon Sep 17 00:00:00 2001 From: Hossein Date: Mon, 21 Apr 2025 20:31:06 +0330 Subject: [PATCH 11/16] #85 refactor: Integrated currency service into Wallet and Market headers --- .../Header/components/MarketHeader/MarketHeader.js | 6 +++++- .../Header/components/WalletHeader/WalletHeader.js | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/MarketHeader/MarketHeader.js b/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/MarketHeader/MarketHeader.js index 1eae2a6..986ca43 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/MarketHeader/MarketHeader.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/MarketHeader/MarketHeader.js @@ -4,6 +4,7 @@ import {useDispatch, useSelector} from "react-redux"; import {useTranslation} from "react-i18next"; import {activeActionSheet} from "../../../../../../../../store/actions/global"; import i18n from "i18next"; +import {getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; const MarketHeader = () => { const {t} = useTranslation(); @@ -11,10 +12,13 @@ const MarketHeader = () => { const activePair = useSelector((state) => state.exchange.activePair) const active = useSelector((state) => state.global.activeActionSheet.subMenu) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + return ( <>
dispatch(activeActionSheet({subMenu: true}))}> - {t("currency." + activePair.baseAsset)}/{t("currency." + activePair.quoteAsset)} + {getCurrencyNameOrAlias(currencies[activePair?.baseAsset], language)}/{getCurrencyNameOrAlias(currencies[activePair?.quoteAsset], language)}
diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/WalletHeader/WalletHeader.js b/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/WalletHeader/WalletHeader.js index 4482e6d..60a64b3 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/WalletHeader/WalletHeader.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Header/components/WalletHeader/WalletHeader.js @@ -5,6 +5,7 @@ import {useTranslation} from "react-i18next"; import {activeActionSheet} from "../../../../../../../../store/actions/global"; import {useDispatch, useSelector} from "react-redux"; import i18n from "i18next"; +import {getCurrencyNameOrAlias} from "../../../../../../../../utils/utils"; const WalletHeader = () => { @@ -12,13 +13,16 @@ const WalletHeader = () => { const {t} = useTranslation() const dispatch = useDispatch(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const active = useSelector((state) => state.global.activeActionSheet.subMenu) return ( <>
dispatch(activeActionSheet({subMenu: true}))}> - {t("currency." + id)} - {i18n.language === "fa" && {id}} + {getCurrencyNameOrAlias(currencies[id], language)} + ({id})
Date: Tue, 22 Apr 2025 10:33:27 +0330 Subject: [PATCH 12/16] #85 feat: Update transaction history to use currency details from service --- .../History/components/Transactions/Transactions.js | 10 +++++++--- .../components/TransactionsTable/TransactionsTable.js | 11 ++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/Transactions/Transactions.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/Transactions/Transactions.js index 02c0b13..8f4022b 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/Transactions/Transactions.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/Transactions/Transactions.js @@ -13,12 +13,16 @@ import Loading from "../../../../../../../../../../components/Loading/Loading"; import Error from "../../../../../../../../../../components/Error/Error"; import Date from "../../../../../../../../../../components/Date/Date"; import Button from "../../../../../../../../../../components/Button/Button"; +import {getCurrencyNameOrAlias} from "../../../../../../../../../../utils/utils"; const Transactions = () => { const {t} = useTranslation(); const coins = useSelector((state) => state.exchange.assets) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const [query, setQuery] = useState({ "currency": null, // optional "category": null, // optional [DEPOSIT, FEE, TRADE, WITHDRAW, ORDER_CANCEL, ORDER_CREATE, ORDER_FINALIZED] @@ -54,8 +58,8 @@ const Transactions = () => { categoryOptions.push({value: o, label: t('TransactionCategory.' + o)}) }) - coins.forEach((o) => { - currenciesOptions.push({value: o, label: t('currency.' + o)}) + Object.keys(currencies).forEach((o) => { + currenciesOptions.push({value: o, label: getCurrencyNameOrAlias(currencies[o], language)}) }) const scrollRef = useRef(null); @@ -132,7 +136,7 @@ const Transactions = () => { type="select" value={{ value: query?.currency, - label: query?.currency ? t('currency.'+ query?.currency) : t('all'), + label: query?.currency ? getCurrencyNameOrAlias(currencies[query?.currency], language) : t('all'), }} onchange={(e) => setQuery({...query, currency: e.value, offset:0})} customClass={`width-100 my-1 ${classes.thisInput}`} diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/TransactionsTable/TransactionsTable.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/TransactionsTable/TransactionsTable.js index 9425830..16706aa 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/TransactionsTable/TransactionsTable.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/TransactionsTable/TransactionsTable.js @@ -2,8 +2,10 @@ import React, {useState} from 'react'; import classes from './TransactionsTable.module.css' import {useTranslation} from "react-i18next"; import Date from "../../../../../../../../../../components/Date/Date"; -import {BN} from "../../../../../../../../../../utils/utils"; +import {BN, formatWithPrecision} from "../../../../../../../../../../utils/utils"; import moment from "moment-jalaali"; +import i18n from "i18next"; +import {useSelector} from "react-redux"; const TransactionsTable = ({data}) => { @@ -11,6 +13,9 @@ const TransactionsTable = ({data}) => { const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + return (
data?.description && setIsOpen(prevState => !prevState)}> @@ -30,7 +35,7 @@ const TransactionsTable = ({data}) => {
{t("history.balanceChange")}
- {new BN(data?.balanceChange).isGreaterThan(0) && "+"}{new BN(data?.balanceChange).toFormat()} + {new BN(data?.balanceChange).isGreaterThan(0) && "+"}{formatWithPrecision(data?.balanceChange, currencies[data?.currency].precision)} {data?.currency} @@ -41,7 +46,7 @@ const TransactionsTable = ({data}) => {
{t("history.balance")}
- {new BN(data?.balance).toFormat()} + {formatWithPrecision(data?.balance, currencies[data?.currency].precision)} {data?.currency} From bd5b8810f57dcf87e94e5f88543e8649de3ccf5e Mon Sep 17 00:00:00 2001 From: Hossein Date: Tue, 22 Apr 2025 11:12:48 +0330 Subject: [PATCH 13/16] #85 feat: Update deposit history to fetch and display currency details from service --- .../components/DepositHistory/DepositHistory.js | 11 ++++++++--- .../DepositHistoryTable/DepositHistoryTable.js | 15 +++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistory/DepositHistory.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistory/DepositHistory.js index ad5e8d7..3ccb142 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistory/DepositHistory.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistory/DepositHistory.js @@ -13,12 +13,17 @@ import TextInput from "../../../../../../../../../../components/TextInput/TextIn import DatePanel from "react-multi-date-picker/plugins/date_panel"; import ToggleSwitch from "../../../../../../../../../../components/ToggleSwitch/ToggleSwitch"; import Button from "../../../../../../../../../../components/Button/Button"; +import {getCurrencyNameOrAlias} from "../../../../../../../../../../utils/utils"; +import i18n from "i18next"; const DepositHistory = () => { const {t} = useTranslation(); const coins = useSelector((state) => state.exchange.assets) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const [query, setQuery] = useState({ "currency": null, // optional "category": null, // optional [DEPOSIT, FEE, TRADE, WITHDRAW, ORDER_CANCEL, ORDER_CREATE, ORDER_FINALIZED] @@ -52,8 +57,8 @@ const DepositHistory = () => { categoryOptions.push({value: o, label: t('TransactionCategory.' + o)}) }) - coins.forEach((o) => { - currenciesOptions.push({value: o, label: t('currency.' + o)}) + Object.keys(currencies).forEach((o) => { + currenciesOptions.push({value: o, label: getCurrencyNameOrAlias(currencies[o], language)}) }) @@ -134,7 +139,7 @@ const DepositHistory = () => { type="select" value={{ value: query?.currency, - label: query?.currency ? t('currency.'+ query?.currency) : t('all'), + label: query?.currency ? getCurrencyNameOrAlias(currencies[query?.currency], language) : t('all'), }} onchange={(e) => setQuery({...query, currency: e.value, offset:0})} customClass={`width-100 my-1 ${classes.thisInput}`} diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistoryTable/DepositHistoryTable.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistoryTable/DepositHistoryTable.js index 833f07e..05d4fc9 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistoryTable/DepositHistoryTable.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/DepositHistoryTable/DepositHistoryTable.js @@ -4,14 +4,17 @@ import {useTranslation} from "react-i18next"; import {toast} from "react-hot-toast"; import Date from "../../../../../../../../../../components/Date/Date"; import moment from "moment-jalaali"; -import {BN, shortenHash} from "../../../../../../../../../../utils/utils"; +import {BN, formatWithPrecision, shortenHash} from "../../../../../../../../../../utils/utils"; import Icon from "../../../../../../../../../../components/Icon/Icon"; +import i18n from "i18next"; +import {useSelector} from "react-redux"; const DepositHistoryTable = ({data}) => { const [isOpen, setIsOpen] = useState(false); const {t} = useTranslation(); - + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) const copyToClipboard = (e, value) => { e.stopPropagation(); @@ -30,15 +33,15 @@ const DepositHistoryTable = ({data}) => { {moment.utc(data?.createDate).local().format("HH:mm:ss")}
- {t("HistoryStatus."+ data?.status)} + {t("depositStatus."+ data?.status)} {/*{moment(data?.date).format("HH:mm:ss")}*/}
- {data?.network} + {data.network ?? "- - -"}
- {new BN(data?.amount).toFormat()} + {formatWithPrecision(data?.amount, currencies[data?.currency].precision)} {data?.currency} @@ -47,7 +50,7 @@ const DepositHistoryTable = ({data}) => {
{t("history.type")} - {t("HistoryType."+ data?.type)} + {data?.type}
From a2319c94d097f298a7bbb75d5e040a203ed99e68 Mon Sep 17 00:00:00 2001 From: Hossein Date: Tue, 22 Apr 2025 11:32:46 +0330 Subject: [PATCH 14/16] #85 feat: Update withdrawal history to utilize currency details from service --- public/assets/locales/en/translation.json | 7 +++++++ public/assets/locales/fa/translation.json | 7 +++++++ .../components/WithdrawHistory/WithdrawHistory.js | 11 ++++++++--- .../WithdrawHistoryTable/WithdrawHistoryTable.js | 14 +++++++++----- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/public/assets/locales/en/translation.json b/public/assets/locales/en/translation.json index 5bc59f0..2e3c08b 100644 --- a/public/assets/locales/en/translation.json +++ b/public/assets/locales/en/translation.json @@ -286,6 +286,13 @@ "depositStatus": { "DONE": "Done" }, + "withdrawStatus": { + "CREATED": "Created", + "PROCESSING": "Processing", + "CANCELED": "Canceled", + "REJECTED": "Rejected", + "DONE": "Done" + }, "HistoryStatus": { "DONE": "Done", "CREATED": "Created", diff --git a/public/assets/locales/fa/translation.json b/public/assets/locales/fa/translation.json index dac45c2..6643f48 100644 --- a/public/assets/locales/fa/translation.json +++ b/public/assets/locales/fa/translation.json @@ -286,6 +286,13 @@ "depositStatus": { "DONE": "انجام شده" }, + "withdrawStatus": { + "CREATED": "جاری شده", + "PROCESSING": "در حال انجام", + "CANCELED": "لغو شده", + "REJECTED": "رد شده", + "DONE": "انجام شده" + }, "HistoryStatus": { "DONE" : "انجام شده", "CREATED": "جاری شده", diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistory/WithdrawHistory.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistory/WithdrawHistory.js index 0f18996..f6065a8 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistory/WithdrawHistory.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistory/WithdrawHistory.js @@ -13,11 +13,16 @@ import TextInput from "../../../../../../../../../../components/TextInput/TextIn import DatePanel from "react-multi-date-picker/plugins/date_panel"; import ToggleSwitch from "../../../../../../../../../../components/ToggleSwitch/ToggleSwitch"; import Button from "../../../../../../../../../../components/Button/Button"; +import i18n from "i18next"; +import {getCurrencyNameOrAlias} from "../../../../../../../../../../utils/utils"; const WithdrawHistory = () => { const {t} = useTranslation(); const coins = useSelector((state) => state.exchange.assets) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const [query, setQuery] = useState({ "currency": null, // optional "category": null, // optional [DEPOSIT, FEE, TRADE, WITHDRAW, ORDER_CANCEL, ORDER_CREATE, ORDER_FINALIZED] @@ -51,8 +56,8 @@ const WithdrawHistory = () => { categoryOptions.push({value: o, label: t('TransactionCategory.' + o)}) }) - coins.forEach((o) => { - currenciesOptions.push({value: o, label: t('currency.' + o)}) + Object.keys(currencies).forEach((o) => { + currenciesOptions.push({value: o, label: getCurrencyNameOrAlias(currencies[o], language)}) }) @@ -131,7 +136,7 @@ const WithdrawHistory = () => { type="select" value={{ value: query?.currency, - label: query?.currency ? t('currency.'+ query?.currency) : t('all'), + label: query?.currency ? getCurrencyNameOrAlias(currencies[query?.currency], language) : t('all'), }} onchange={(e) => setQuery({...query, currency: e.value, offset:0})} customClass={`width-100 my-1 ${classes.thisInput}`} diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistoryTable/WithdrawHistoryTable.js b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistoryTable/WithdrawHistoryTable.js index 5088b5e..9cc53b7 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistoryTable/WithdrawHistoryTable.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/Content/components/History/components/WithdrawHistoryTable/WithdrawHistoryTable.js @@ -4,16 +4,20 @@ import {useTranslation} from "react-i18next"; import {toast} from "react-hot-toast"; import Date from "../../../../../../../../../../components/Date/Date"; import moment from "moment-jalaali"; -import {BN, shortenHash} from "../../../../../../../../../../utils/utils"; +import {BN, formatWithPrecision, shortenHash} from "../../../../../../../../../../utils/utils"; import Button from "../../../../../../../../../../components/Button/Button"; import {useGetWithdrawHistory} from "../../../../../../../../../../queries"; import {cancelWithdrawReq} from "js-api-client"; import Loading from "../../../../../../../../../../components/Loading/Loading"; import Icon from "../../../../../../../../../../components/Icon/Icon"; +import i18n from "i18next"; +import {useSelector} from "react-redux"; const WithdrawHistoryTable = ({data, query}) => { const {t} = useTranslation(); + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false) @@ -59,15 +63,15 @@ const WithdrawHistoryTable = ({data, query}) => { {moment.utc(data?.createDate).local().format("HH:mm:ss")}
- {t("HistoryStatus."+ data?.status)} + {t("withdrawStatus."+ data?.status)} {/*{moment(data?.date).format("HH:mm:ss")}*/}
- {data?.network} + {data?.destNetwork}
- {new BN(data?.amount).toFormat()} + {formatWithPrecision(data?.amount, currencies[data?.currency].precision)} {data?.currency} @@ -99,7 +103,7 @@ const WithdrawHistoryTable = ({data, query}) => { {t("history.appliedFee")} { data?.appliedFee ?
- {new BN(data?.amount).toFormat()} + {formatWithPrecision(data?.appliedFee, currencies[data.currency].precision)} {data?.currency} From 4e30ba636ade6a5bf3fa6522ddab7ec99b308c7b Mon Sep 17 00:00:00 2001 From: Hossein Date: Tue, 22 Apr 2025 12:02:53 +0330 Subject: [PATCH 15/16] #85 feat: Integrate currency service in Easy Trade component --- .../components/EasyOrder/EasyOrder.js | 200 ++++++++++-------- 1 file changed, 110 insertions(+), 90 deletions(-) diff --git a/src/main/Mobile/Pages/EasyTrading/components/EasyOrder/EasyOrder.js b/src/main/Mobile/Pages/EasyTrading/components/EasyOrder/EasyOrder.js index dbeb0f0..6679823 100644 --- a/src/main/Mobile/Pages/EasyTrading/components/EasyOrder/EasyOrder.js +++ b/src/main/Mobile/Pages/EasyTrading/components/EasyOrder/EasyOrder.js @@ -4,7 +4,7 @@ import {Trans, useTranslation} from "react-i18next"; import {useDispatch, useSelector} from "react-redux"; import {useGetUserAccount} from "../../../../../../queries/hooks/useGetUserAccount"; import {useOrderBook} from "../../../../../../queries"; -import {BN, parsePriceString} from "../../../../../../utils/utils"; +import {BN, getCurrencyNameOrAlias, parsePriceString} from "../../../../../../utils/utils"; import {toast} from "react-hot-toast"; import {setLastTransaction} from "../../../../../../store/actions/auth"; import {images} from "../../../../../../assets/images"; @@ -26,6 +26,10 @@ const EasyOrder = () => { const [isLoading, setIsLoading] = useState(false) const isLogin = useSelector((state) => state.auth.isLogin) + const language = i18n.language + const currencies = useSelector((state) => state.exchange.currencies) + const pairsList = useSelector((state) => state.exchange.pairsList) + const symbols = useSelector((state) => state.exchange.symbols) const [alert, setAlert] = useState({ @@ -45,31 +49,37 @@ const EasyOrder = () => { }); const handleAvailableAssets = () => { - const availableAssets = []; - for (const symbol of symbols) { - if (!availableAssets.includes(symbol.baseAsset)) availableAssets.push(symbol.baseAsset) - if (!availableAssets.includes(symbol.quoteAsset)) availableAssets.push(symbol.quoteAsset) - } - return availableAssets; - } + const availableAssets = new Set(); + Object.values(pairsList).forEach(pair => { + availableAssets.add(pair.baseAsset); + availableAssets.add(pair.quoteAsset); + }); + return Array.from(availableAssets); + }; const handleAvailableDest = (buy) => { - const dest = [] - for (const symbol of symbols) { - if (symbol.baseAsset === buy) dest.push(symbol.quoteAsset) - if (symbol.quoteAsset === buy) dest.push(symbol.baseAsset) - } - return dest - } + const dest = new Set(); + Object.values(pairsList).forEach(pair => { + if (pair.baseAsset === buy) dest.add(pair.quoteAsset); + if (pair.quoteAsset === buy) dest.add(pair.baseAsset); + }); + return Array.from(dest); + }; + + const findPair = (buy, sell) => + Object.values(pairsList)?.find(pair => + (pair.baseAsset === buy && pair.quoteAsset === sell) || + (pair.baseAsset === sell && pair.quoteAsset === buy) + ); - const findPair = (buy, sell) => symbols?.find(s => ((s?.baseAsset === buy) && (s?.quoteAsset === sell)) || ((s?.baseAsset === sell) && (s?.quoteAsset === buy))) + const pairsArray = Object.values(pairsList); const [selected, setSelected] = useState({ - buy: symbols[0].baseAsset, - sell: symbols[0].quoteAsset, - pair: findPair(symbols[0].baseAsset, symbols[0].quoteAsset), + buy: pairsArray[0]?.baseAsset, + sell: pairsArray[0]?.quoteAsset, + pair: findPair(pairsArray[0]?.baseAsset, pairsArray[0]?.quoteAsset), type: "ask" - }) + }); const reversePair = () => { @@ -80,6 +90,12 @@ const EasyOrder = () => { type: selected?.type ==="ask" ? "bid" : "ask" }) + setAlert({ + submit: false, + reqAmount: null, + totalPrice: null, + }) + } const [options, setOptions] = useState({ @@ -107,36 +123,28 @@ const EasyOrder = () => { bestPriceHandler() }, [orderBook, selected]) - const buyPriceHandler = (value) => { let newAlert = null value = parsePriceString(value); const reqAmount = new BN(value); - let range = "baseRange" - if (selected.type === "bid") range = "quoteRange" - if (reqAmount.isZero() && reqAmount.isLessThan(selected.pair[range].min)) { + + let selectedCurrency = "baseAsset" + if (selected.type === "bid") selectedCurrency = "quoteAsset" + + if (reqAmount.isZero() && reqAmount.isLessThan(currencies[selected.pair[selectedCurrency]].minOrder)) { newAlert = - } - /* if (reqAmount.isGreaterThan(selected.pair[range].max)) { - newAlert = - }*/ - if (!reqAmount.mod(selected.pair[range].step).isZero()) { + + if (!reqAmount.mod(currencies[selected.pair[selectedCurrency]].step).isZero()) { newAlert = } setAlert({...alert, reqAmount: newAlert}); @@ -149,36 +157,30 @@ const EasyOrder = () => { }); }; + const totalPriceHandler = (value) => { let newAlert = null value = parsePriceString(value); const totalPrice = new BN(value); - let range = "quoteRange" - if (selected.type === "bid") range = "baseRange" - if (totalPrice.isZero() && totalPrice.isLessThan(selected.pair[range].min)) { + let selectedCurrency = "quoteAsset" + if (selected.type === "bid") selectedCurrency = "baseAsset" + + if (totalPrice.isZero() && totalPrice.isLessThan(currencies[selected.pair[selectedCurrency]].minOrder)) { newAlert = } - /*if (totalPrice.isGreaterThan(selected.pair[range].max)) { - newAlert = - }*/ - if (!totalPrice.mod(selected.pair[range].step).isZero()) { + + if (!totalPrice.mod(currencies[selected.pair[selectedCurrency]].step).isZero()) { newAlert = } setAlert({...alert, totalPrice: newAlert}); @@ -195,8 +197,8 @@ const EasyOrder = () => { if (order.pricePerUnit.isEqualTo(0)) return toast.error(t("orders.hasNoOffer")); let totalPrice = new BN(userAccount?.wallets[selected?.sell]?.free); let reqAmount = totalPrice.dividedBy(order.pricePerUnit) - if (!reqAmount.mod(selected.pair?.[selected.type === "ask" ? "baseRange" : "quoteRange"].step).isZero()) { - reqAmount = reqAmount.minus(reqAmount.mod(selected.pair?.[selected.type === "ask" ? "baseRange" : "quoteRange"].step)); + if (!reqAmount.mod(currencies[selected.pair?.[selected.type === "ask" ? "baseAsset" : "quoteAsset"]].step).isZero()) { + reqAmount = reqAmount.minus(reqAmount.mod(currencies[selected.pair?.[selected.type === "ask" ? "baseAsset" : "quoteAsset"]].step)); } buyPriceHandler( reqAmount.toFormat(), @@ -212,7 +214,7 @@ const EasyOrder = () => { setIsLoading(true) const newOrder = {...order} if (selected.type === "bid") { - newOrder.reqAmount = order.totalPrice.decimalPlaces(selected.pair?.baseAssetPrecision) + newOrder.reqAmount = order.totalPrice.decimalPlaces(currencies[selected?.pair?.baseAsset].precision) } createOrder(selected.pair?.symbol, selected.type === "ask" ? "BUY" : "SELL", newOrder) .then((res) => { @@ -228,8 +230,8 @@ const EasyOrder = () => { toast.success( { const buyOnChangeHandler = (e) => { const newBuy = e.value; - const sellOptions = handleAvailableDest(newBuy) + const sellOptions = handleAvailableDest(newBuy); + + setOptions(prevOptions => ({ + ...prevOptions, + sell: sellOptions, + })); - setOptions({ - ...options, - "sell": sellOptions, - }) const sell = sellOptions.includes(selected.sell) ? selected.sell : sellOptions[0]; - const pair = findPair(newBuy, sell) + const pair = findPair(newBuy, sell) || {}; setSelected({ buy: newBuy, sell, pair, - type: newBuy === pair.baseAsset ? "ask" : "bid" - }) + type: pair.baseAsset === newBuy ? "ask" : "bid" + }); + setOrder({ tradeFee: new BN(0), stopLimit: false, @@ -281,17 +285,26 @@ const EasyOrder = () => { reqAmount: new BN(0), pricePerUnit: new BN(0), totalPrice: new BN(0), - }) - } + }); + + setAlert({ + submit: false, + reqAmount: null, + totalPrice: null, + }); + }; + const sellOnChangeHandler = (e) => { const newSell = e.value; - const pair = findPair(selected.buy, newSell) - setSelected({ - ...selected, + const pair = findPair(selected.buy, newSell) || {}; + + setSelected(prevSelected => ({ + ...prevSelected, sell: newSell, pair, type: selected.buy === pair.baseAsset ? "ask" : "bid" - }) + })); + setOrder({ tradeFee: new BN(0), stopLimit: false, @@ -300,12 +313,19 @@ const EasyOrder = () => { reqAmount: new BN(0), pricePerUnit: new BN(0), totalPrice: new BN(0), - }) - } + }); + + setAlert({ + submit: false, + reqAmount: null, + totalPrice: null, + }); + }; + const showBestPrice = () => { if (order.pricePerUnit.isZero()) return 0 if (selected.type === "ask") return order.pricePerUnit.toFormat() - return new BN(1).dividedBy(order.pricePerUnit).decimalPlaces(selected.pair?.baseAssetPrecision).toFormat() + return new BN(1).dividedBy(order.pricePerUnit).decimalPlaces(currencies[selected?.pair?.baseAsset].precision).toFormat() } useEffect(() => { @@ -328,7 +348,7 @@ const EasyOrder = () => { {t("MarketTitle.easyTrading")}
- {t("txHistory.title")} + {t("txHistory.title")} @@ -345,9 +365,9 @@ const EasyOrder = () => { value: o, label:
- {t('currency.' + o)} + {getCurrencyNameOrAlias(currencies[o], language)}
} } @@ -356,15 +376,15 @@ const EasyOrder = () => { type="select" value={{ value: selected?.buy, - label: t('currency.' + selected?.buy), + label: getCurrencyNameOrAlias(currencies[selected?.buy], language), }} onchange={buyOnChangeHandler} customClass={`width-90 ${classes.thisInput} mb-1`} />
-

{t("MarketInfo.lastPrice")}{" "} {t("currency." + selected?.buy)}:

- {showBestPrice()}{" "}{t("currency." + selected?.sell)} +

{t("MarketInfo.lastPrice")}{" "} {getCurrencyNameOrAlias(currencies[selected?.buy], language)}:

+ {showBestPrice()}{" "}{getCurrencyNameOrAlias(currencies[selected?.sell], language)}
@@ -384,9 +404,9 @@ const EasyOrder = () => { label:
- {t('currency.' + o)} + {getCurrencyNameOrAlias(currencies[o], language)}
} } @@ -395,7 +415,7 @@ const EasyOrder = () => { type="select" value={{ value: selected?.sell, - label: selected?.sell ? t('currency.' + selected?.sell) : t("PersonalizationForm.placeholder"), + label: selected?.sell ? getCurrencyNameOrAlias(currencies[selected?.sell], language) : t("PersonalizationForm.placeholder"), }} onchange={sellOnChangeHandler} customClass={`width-90 ${classes.thisInput} my-1`} @@ -404,14 +424,14 @@ const EasyOrder = () => {

{t("orders.availableAmount")}:{" "}

{new BN(userAccount?.wallets[selected?.sell]?.free || 0).toFormat()}{" "}{t("currency." + selected?.sell)} + onClick={fillBuyByWallet}>{new BN(userAccount?.wallets[selected?.sell]?.free || 0).decimalPlaces(currencies[selected?.sell]?.precision ?? 0).toFormat()}{" "}{getCurrencyNameOrAlias(currencies[selected?.sell], language)}
buyPriceHandler(e.target.value)} alert={alert.reqAmount} customClass={`width-90 mb-1 mt-5`} @@ -420,8 +440,8 @@ const EasyOrder = () => { totalPriceHandler(e.target.value)} alert={alert.totalPrice} customClass={`width-90 my-1`} From 1a9d7f75e752823b1d2199c6b2fedc067ffaa2c4 Mon Sep 17 00:00:00 2001 From: Hossein Date: Tue, 22 Apr 2025 14:08:46 +0330 Subject: [PATCH 16/16] #85 refactor: Update navigateToPanel to align with new pair service and active pair handling --- .../AllMarketInfoTable/AllMarketInfoTable.js | 8 ++++---- .../components/MarketInfoTable/MarketInfoTable.js | 10 +++++----- .../components/MarketPairCard/MarketPairCard.js | 2 ++ src/store/reducers/exchangeReducer.js | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js index d053f0b..5e82ab3 100644 --- a/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js +++ b/src/main/Mobile/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js @@ -31,9 +31,9 @@ const AllMarketInfoTable = ({data, activeCurrency, interval}) => { const [swipeRight, setSwipeRight] = useState(null); const [swipeLeft, setSwipeLeft] = useState(null); - const navigateToPanel = (symbol) => { - const selectedPair = allExchangeSymbols.find( s => s.symbol === symbol) - dispatch(setActivePairInitiate(selectedPair, 0)) + const navigateToPanel = (baseAsset, quoteAsset) => { + const pairSymbolFormatted = `${baseAsset}_${quoteAsset}`; + dispatch(setActivePairInitiate(`${baseAsset}_${quoteAsset}`, 0)); navigate(Order) } @@ -154,7 +154,7 @@ const AllMarketInfoTable = ({data, activeCurrency, interval}) => {
diff --git a/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js b/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js index 080af5d..2fd5609 100644 --- a/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js +++ b/src/main/Mobile/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js @@ -32,9 +32,9 @@ const MarketInfoTable = ({data, activeCurrency, interval}) => { const [swipLeft, setSwipLeft] = useState(null); - const navigateToPanel = (symbol) => { - const selectedPair = allExchangeSymbols.find(s => s.symbol === symbol) - dispatch(setActivePairInitiate(selectedPair, 0)) + const navigateToPanel = (baseAsset, quoteAsset) => { + const pairSymbolFormatted = `${baseAsset}_${quoteAsset}`; + dispatch(setActivePairInitiate(`${baseAsset}_${quoteAsset}`, 0)); navigate(Order) } @@ -116,7 +116,7 @@ const MarketInfoTable = ({data, activeCurrency, interval}) => { {tr?.base} {activeCurrency ? getCurrencyNameOrAlias(currencies[tr?.base], language) : tr?.base + " / " + tr?.quote} @@ -143,7 +143,7 @@ const MarketInfoTable = ({data, activeCurrency, interval}) => {
diff --git a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js index c0a7d88..1b8c56b 100644 --- a/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js +++ b/src/main/Mobile/Pages/UserPanel/Secttions/SubMenu/components/MarketSubMenu/components/MarketPairCard/MarketPairCard.js @@ -12,6 +12,8 @@ import i18n from "i18next"; const MarketPairCard = ({id, pair,favPair,addFav}) => { + console.log("id", id) + const activePair = useSelector((state) => state.exchange.activePair.symbol) const {data: prices} = useGetLastPrices() const dispatch = useDispatch(); diff --git a/src/store/reducers/exchangeReducer.js b/src/store/reducers/exchangeReducer.js index dee93da..2395f08 100644 --- a/src/store/reducers/exchangeReducer.js +++ b/src/store/reducers/exchangeReducer.js @@ -4,7 +4,7 @@ const initialState = { assets: [], pairs: [], symbols: [], - activePair: {}, + activePair: [], activePairOrders: { bestBuyPrice: 0, bestSellPrice: 0,