From 7bade3596b37b35c3e6e4540b369f4766eac175f Mon Sep 17 00:00:00 2001 From: Hossein Date: Thu, 20 Feb 2025 20:47:48 +0330 Subject: [PATCH] #249 feat: Implement chart service for currency trend visualization with 24h, 7d, and 30d intervals --- .../components/AllMarketInfo/AllMarketInfo.js | 4 +- .../AllMarketInfoCard/AllMarketInfoCard.js | 27 +++++++---- .../AllMarketInfoCard.module.css | 12 ++++- .../AllMarketInfoTable/AllMarketInfoTable.js | 28 ++++++++---- .../AllMarketInfoTable.module.css | 11 ++++- .../components/MarketInfo/MarketInfo.js | 10 ++--- .../MarketInfoCard/MarketInfoCard.js | 29 ++++++++---- .../MarketInfoCard/MarketInfoCard.module.css | 12 ++++- .../MarketInfoTable/MarketInfoTable.js | 25 ++++++++--- .../MarketInfoTable.module.css | 11 +++-- .../components/MarketView/MarketView.js | 2 + src/queries/hooks/useGetChartData.js | 45 +++++++++++++++++++ src/queries/index.js | 3 +- yarn.lock | 4 +- 14 files changed, 172 insertions(+), 51 deletions(-) create mode 100644 src/queries/hooks/useGetChartData.js diff --git a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js index e4797e16..d9e84f5f 100644 --- a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js +++ b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/AllMarketInfo.js @@ -41,9 +41,9 @@ const AllMarketInfo = () => { if (error || quoteCurrenciesError) return
else return <> {card ? - + : - + } } diff --git a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.js b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.js index ac5d2983..ee74e203 100644 --- a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.js +++ b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.js @@ -9,8 +9,9 @@ import {Panel} from "../../../../../../Routes/routes"; import {useNavigate} from "react-router-dom"; import {useDispatch, useSelector} from "react-redux"; import i18n from "i18next"; +import {useGetChartData} from "../../../../../../../../queries"; -const AllMarketInfoCard = ({data, activeCurrency}) => { +const AllMarketInfoCard = ({data, activeCurrency, interval}) => { const {t} = useTranslation(); @@ -22,6 +23,11 @@ const AllMarketInfoCard = ({data, activeCurrency}) => { const [showButton, setShowButton] = useState(null) + const pairsList = useSelector((state) => state.exchange.pairsList) + const symbols = Object.keys(pairsList); + + const { data: ChartData, isLoading: ChartDataIsLoading, error: ChartDataError } = useGetChartData(symbols, interval); + const backgroundBar = (percent) => { if (percent > 0) { return { @@ -46,11 +52,20 @@ const AllMarketInfoCard = ({data, activeCurrency}) => { navigate(Panel) } + const chartView = (chartInfo) => { + if (ChartDataIsLoading) { + return ----- + } + if (ChartDataError || !(chartInfo?.svgData)) { + return + } + return {chartInfo?.symbol} + } return (
- {data.map((tr, index) => { + const chartInfo = ChartData?.find(chart => chart.symbol.replace("_", "") === tr.symbol); return (
MouseEnterEventHandler(index)} onMouseLeave={MouseLeaveEventHandler}> @@ -108,13 +123,7 @@ const AllMarketInfoCard = ({data, activeCurrency}) => {
:
- {""} - {t("comingSoon")} + {chartView(chartInfo)}
}
diff --git a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.module.css b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.module.css index 0c436124..e8412cad 100644 --- a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.module.css +++ b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoCard/AllMarketInfoCard.module.css @@ -22,6 +22,14 @@ bottom: 0; } -.filter { - filter: blur(4px); +.chart { + width: 100%; +} + +.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/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js index 10369eb9..8824dfa1 100644 --- a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js +++ b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.js @@ -9,8 +9,9 @@ import {setActivePairInitiate} from "../../../../../../../../store/actions"; import {Panel} from "../../../../../../Routes/routes"; import {useNavigate} from "react-router-dom"; import {useDispatch, useSelector} from "react-redux"; +import {useGetChartData} from "../../../../../../../../queries"; -const AllMarketInfTable = ({data, activeCurrency}) => { +const AllMarketInfTable = ({data, activeCurrency, interval}) => { const {t} = useTranslation(); const navigate = useNavigate(); @@ -20,6 +21,12 @@ const AllMarketInfTable = ({data, activeCurrency}) => { 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 navigateToPanel = (symbol) => { const selectedPair = allExchangeSymbols.find( s => s.symbol === symbol) dispatch(setActivePairInitiate(selectedPair, 0)) @@ -42,9 +49,20 @@ const AllMarketInfTable = ({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 (
@@ -77,13 +95,7 @@ const AllMarketInfTable = ({data, activeCurrency}) => { {tr.highPrice}*/} - {""} - {t("comingSoon")} + {chartView(chartInfo)} diff --git a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css index e02aefbc..c0fae374 100644 --- a/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css +++ b/src/main/Browser/Pages/AllMarket/components/AllMarketInfo/components/AllMarketInfoTable/AllMarketInfoTable.module.css @@ -17,6 +17,13 @@ bottom: 0; } -.filter { - filter: blur(4px); +.chart { + width: 100%; +} +.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/Browser/Pages/Landing/components/MarketInfo/MarketInfo.js b/src/main/Browser/Pages/Landing/components/MarketInfo/MarketInfo.js index e89d2f38..328f487a 100644 --- a/src/main/Browser/Pages/Landing/components/MarketInfo/MarketInfo.js +++ b/src/main/Browser/Pages/Landing/components/MarketInfo/MarketInfo.js @@ -5,7 +5,7 @@ import MarketInfoTable from "./components/MarketInfoTable/MarketInfoTable"; import MarketInfoCard from "./components/MarketInfoCard/MarketInfoCard"; import * as Routes from "../../../../Routes/routes"; import {Link} from "react-router-dom"; -import {useGetQuoteCurrencies, useOverview} from "../../../../../../queries"; +import {useGetChartData, useGetQuoteCurrencies, useOverview} from "../../../../../../queries"; import Loading from "../../../../../../components/Loading/Loading"; import Error from "../../../../../../components/Error/Error"; import {useTranslation} from "react-i18next"; @@ -23,11 +23,10 @@ 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) @@ -46,14 +45,15 @@ const MarketInfo = () => { if (error || quoteCurrenciesError) return
else return <> {card ? - + : - + } } + return (
diff --git a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.js b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.js index 7f786523..bc5f5364 100644 --- a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.js +++ b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.js @@ -8,8 +8,9 @@ import {Panel} from "../../../../../../Routes/routes"; import {useNavigate} from "react-router-dom"; import {useDispatch, useSelector} from "react-redux"; import i18n from "i18next"; +import {useGetChartData} from "../../../../../../../../queries"; -const MarketInfoCard = ({data, activeCurrency}) => { +const MarketInfoCard = ({data, activeCurrency, interval}) => { const {t} = useTranslation(); const navigate = useNavigate(); @@ -18,6 +19,12 @@ const MarketInfoCard = ({data, activeCurrency}) => { 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 backgroundBar = (percent) => { if (percent > 0) { return { @@ -35,9 +42,20 @@ const MarketInfoCard = ({data, activeCurrency}) => { navigate(Panel) } + const chartView = (chartInfo) => { + if (ChartDataIsLoading) { + return ----- + } + if (ChartDataError || !(chartInfo?.svgData)) { + return + } + return {chartInfo?.symbol} + } + return (
{data.map((tr, index) => { + const chartInfo = ChartData?.find(chart => chart.symbol.replace("_", "") === tr.symbol); return (
navigateToPanel(tr.symbol)}>
@@ -60,14 +78,7 @@ const MarketInfoCard = ({data, activeCurrency}) => { {new BN(tr.volume).decimalPlaces(currencies[tr?.base]?.precision ?? 0).toFormat()} {tr?.base}
- {""} - {t("comingSoon")} + {chartView(chartInfo)}
) diff --git a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.module.css b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.module.css index 7fc04ed0..2a7b1429 100644 --- a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.module.css +++ b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoCard/MarketInfoCard.module.css @@ -14,6 +14,14 @@ background-color: var(--container) !important; } -.filter { - filter: blur(4px); +.chart { + width: 100%; +} + +.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/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js index eb93c071..42b2459f 100644 --- a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js +++ b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.js @@ -7,9 +7,10 @@ import {setActivePairInitiate} from "../../../../../../../../store/actions"; import {useDispatch, useSelector} from "react-redux"; import {Panel} from "../../../../../../Routes/routes"; import {useNavigate} from "react-router-dom"; +import {useGetChartData} from "../../../../../../../../queries"; -const MarketInfoTable = ({data, activeCurrency}) => { +const MarketInfoTable = ({data, activeCurrency, interval}) => { const {t} = useTranslation(); const navigate = useNavigate(); @@ -18,6 +19,12 @@ const MarketInfoTable = ({data, activeCurrency}) => { 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 navigateToPanel = (symbol) => { const selectedPair = allExchangeSymbols.find( s => s.symbol === symbol) dispatch(setActivePairInitiate(selectedPair, 0)) @@ -34,13 +41,20 @@ const MarketInfoTable = ({data, activeCurrency}) => {
); - const base64Data = "PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxuczpqZnJlZXN2Zz0naHR0cDovL3d3dy5qZnJlZS5vcmcvamZyZWVzdmcvc3ZnJyB3aWR0aD0nMTAwJyBoZWlnaHQ9JzM1JyB0ZXh0LXJlbmRlcmluZz0nYXV0bycgc2hhcGUtcmVuZGVyaW5nPSdhdXRvJz48ZGVmcz48Y2xpcFBhdGggaWQ9J18xMTIzOTc0MzA1MTMzY2xpcC0wJz48cGF0aCBkPSdNMTIsOEwxMiwyN0w4OCwyN0w4OCw4WicvPjwvY2xpcFBhdGg+PC9kZWZzPjxsaW5lIHgxPScxNS40NScgeTE9JzI0LjIzJyB4Mj0nMTcuMScgeTI9JzIyLjAyJyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzE3LjEnIHkxPScyMi4wMicgeDI9JzE4Ljc0JyB5Mj0nMjMuMjcnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nMTguNzQnIHkxPScyMy4yNycgeDI9JzIwLjM5JyB5Mj0nMjAuNTcnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nMjAuMzknIHkxPScyMC41NycgeDI9JzIyLjAzJyB5Mj0nMjYuMTYnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nMjIuMDMnIHkxPScyNi4xNicgeDI9JzIzLjY4JyB5Mj0nMTkuNDUnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nMjMuNjgnIHkxPScxOS40NScgeDI9JzI1LjMyJyB5Mj0nMjIuOCcgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPScyNS4zMicgeTE9JzIyLjgnIHgyPScyNi45NycgeTI9JzIzLjYzJyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzI2Ljk3JyB5MT0nMjMuNjMnIHgyPScyOC42MScgeTI9JzI0LjY4JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzI4LjYxJyB5MT0nMjQuNjgnIHgyPSczMC4yNicgeTI9JzE5LjY3JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzMwLjI2JyB5MT0nMTkuNjcnIHgyPSczMS45JyB5Mj0nMjMuMzQnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nMzEuOScgeTE9JzIzLjM0JyB4Mj0nMzMuNTUnIHkyPScyMi42Mycgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSczMy41NScgeTE9JzIyLjYzJyB4Mj0nMzUuMTknIHkyPScxOC4yOScgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSczNS4xOScgeTE9JzE4LjI5JyB4Mj0nMzYuODQnIHkyPScxNy42Micgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSczNi44NCcgeTE9JzE3LjYyJyB4Mj0nMzguNDgnIHkyPScxNi4xNicgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSczOC40OCcgeTE9JzE2LjE2JyB4Mj0nNDAuMTMnIHkyPScyMS44NCcgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc0MC4xMycgeTE9JzIxLjg0JyB4Mj0nNDEuNzcnIHkyPScxOS40Nycgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc0MS43NycgeTE9JzE5LjQ3JyB4Mj0nNDMuNDInIHkyPScyMi4zOScgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc0My40MicgeTE9JzIyLjM5JyB4Mj0nNDUuMDYnIHkyPScxNS45Micgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc0NS4wNicgeTE9JzE1LjkyJyB4Mj0nNDYuNzEnIHkyPScxNS42OScgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc0Ni43MScgeTE9JzE1LjY5JyB4Mj0nNDguMzUnIHkyPScyMC4wNCcgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc0OC4zNScgeTE9JzIwLjA0JyB4Mj0nNTAnIHkyPScxNS4zNCcgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc1MCcgeTE9JzE1LjM0JyB4Mj0nNTEuNjUnIHkyPScyMS4xNycgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc1MS42NScgeTE9JzIxLjE3JyB4Mj0nNTMuMjknIHkyPScyMC42JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzUzLjI5JyB5MT0nMjAuNicgeDI9JzU0Ljk0JyB5Mj0nMTUuNzQnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nNTQuOTQnIHkxPScxNS43NCcgeDI9JzU2LjU4JyB5Mj0nMTYuNzknIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nNTYuNTgnIHkxPScxNi43OScgeDI9JzU4LjIzJyB5Mj0nMTkuOCcgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc1OC4yMycgeTE9JzE5LjgnIHgyPSc1OS44NycgeTI9JzEzLjk2JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzU5Ljg3JyB5MT0nMTMuOTYnIHgyPSc2MS41MicgeTI9JzE4LjEnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nNjEuNTInIHkxPScxOC4xJyB4Mj0nNjMuMTYnIHkyPScxMScgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc2My4xNicgeTE9JzExJyB4Mj0nNjQuODEnIHkyPScxMS4xMicgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc2NC44MScgeTE9JzExLjEyJyB4Mj0nNjYuNDUnIHkyPScxNC43Nicgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc2Ni40NScgeTE9JzE0Ljc2JyB4Mj0nNjguMScgeTI9JzExLjg3JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9JzY4LjEnIHkxPScxMS44NycgeDI9JzY5Ljc0JyB5Mj0nMTYuMTcnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nNjkuNzQnIHkxPScxNi4xNycgeDI9JzcxLjM5JyB5Mj0nMTQuMzgnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nNzEuMzknIHkxPScxNC4zOCcgeDI9JzczLjAzJyB5Mj0nMTYuMScgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjxsaW5lIHgxPSc3My4wMycgeTE9JzE2LjEnIHgyPSc3NC42OCcgeTI9JzEwLjY2JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9Jzc0LjY4JyB5MT0nMTAuNjYnIHgyPSc3Ni4zMicgeTI9JzEyLjUxJyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9Jzc2LjMyJyB5MT0nMTIuNTEnIHgyPSc3Ny45NycgeTI9JzE1LjE2JyBzdHlsZT0nc3Ryb2tlLXdpZHRoOjEuMDtzdHJva2U6cmdiKDI1NSwyNTUsMjU1KTtzdHJva2Utb3BhY2l0eToxLjA7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpiZXZlbDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzaGFwZS1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uJyBjbGlwLXBhdGg9J3VybCgjXzExMjM5NzQzMDUxMzNjbGlwLTApJy8+PGxpbmUgeDE9Jzc3Ljk3JyB5MT0nMTUuMTYnIHgyPSc3OS42MScgeTI9JzgnIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nNzkuNjEnIHkxPSc4JyB4Mj0nODEuMjYnIHkyPSc5LjknIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nODEuMjYnIHkxPSc5LjknIHgyPSc4Mi45JyB5Mj0nMTAuNzknIHN0eWxlPSdzdHJva2Utd2lkdGg6MS4wO3N0cm9rZTpyZ2IoMjU1LDI1NSwyNTUpO3N0cm9rZS1vcGFjaXR5OjEuMDtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOmJldmVsO3N0cm9rZS1taXRlcmxpbWl0OjEwO3NoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb24nIGNsaXAtcGF0aD0ndXJsKCNfMTEyMzk3NDMwNTEzM2NsaXAtMCknLz48bGluZSB4MT0nODIuOScgeTE9JzEwLjc5JyB4Mj0nODQuNTUnIHkyPScxMS4wNycgc3R5bGU9J3N0cm9rZS13aWR0aDoxLjA7c3Ryb2tlOnJnYigyNTUsMjU1LDI1NSk7c3Ryb2tlLW9wYWNpdHk6MS4wO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46YmV2ZWw7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbicgY2xpcC1wYXRoPSd1cmwoI18xMTIzOTc0MzA1MTMzY2xpcC0wKScvPjwvc3ZnPg==" - const svgBase64 = `data:image/svg+xml;base64,${base64Data}`; - + 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 (
navigateToPanel(tr.symbol)}> @@ -61,7 +75,7 @@ const MarketInfoTable = ({data, activeCurrency}) => { {new BN(tr.volume).decimalPlaces(currencies[tr?.base]?.precision ?? 0).toFormat()} {tr?.base} - {svgBase64 && SVG Example} + {chartView(chartInfo)}
) @@ -69,7 +83,6 @@ const MarketInfoTable = ({data, activeCurrency}) => { ); - return ( <> {head} diff --git a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css index 8e700398..90ca61bc 100644 --- a/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css +++ b/src/main/Browser/Pages/Landing/components/MarketInfo/components/MarketInfoTable/MarketInfoTable.module.css @@ -9,8 +9,13 @@ .row:hover { background-color: var(--container); } +.chart { + width: 55%; +} +.filterUp { + filter: invert(51%) sepia(94%) saturate(370%) hue-rotate(109deg) brightness(87%) contrast(92%); -.filter { - /*filter: invert(51%) sepia(94%) saturate(370%) hue-rotate(109deg) brightness(87%) contrast(92%);*/ - filter: invert(38%) sepia(77%) saturate(5872%) hue-rotate(347deg) brightness(93%) contrast(79%); +} +.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/Browser/Pages/Landing/components/MarketView/MarketView.js b/src/main/Browser/Pages/Landing/components/MarketView/MarketView.js index 56f005c3..2e283eef 100644 --- a/src/main/Browser/Pages/Landing/components/MarketView/MarketView.js +++ b/src/main/Browser/Pages/Landing/components/MarketView/MarketView.js @@ -32,6 +32,8 @@ const MarketView = () => { const content = () => { + + if (isLoading) return if (error) return if ( mostVolume === null && mostTrades === null) return diff --git a/src/queries/hooks/useGetChartData.js b/src/queries/hooks/useGetChartData.js new file mode 100644 index 00000000..99879fb8 --- /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 1ae9b4db..14180b19 100644 --- a/src/queries/index.js +++ b/src/queries/index.js @@ -26,4 +26,5 @@ export {useGetBuyAndSellHistory} from "./hooks/useGetBuyAndSellHistory"; export {useGetWithdrawHistory} from "./hooks/useGetWithdrawHistory"; export {useGetDepositHistory} from "./hooks/useGetDepositHistory"; export {useGetTransactionsHistory} from "./hooks/useGetTransactionsHistory"; -export {useGetGatewaysByCurrency} from "./hooks/useGetGatewaysByCurrency"; \ No newline at end of file +export {useGetGatewaysByCurrency} from "./hooks/useGetGatewaysByCurrency"; +export {useGetChartData} from "./hooks/useGetChartData"; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d4504862..1cd7345a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9486,8 +9486,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=e3bb06671718e426e03d618c6a742e079c6a1449" - checksum: 6dd6286f1df7fa812ed56d5fe20f16dadc057cfda979d349379e90a2b76e189cfcda6aba48bdc974f7722bdb7f24233c4a631c89038d7b8192d7e90e3e3fc351 + resolution: "js-api-client@https://github.com/opexdev/js-api-client.git#commit=80c1affbcf83d7da6962e541c5cbb5cdcf0933a1" + checksum: 5e5aca483a1c18eca58fb229d318de06f1200323ce122d18f42704b154a53592af1cf32f9a7e74dcb28375e8a29d644cc69ccc28d733dfeba302096f0bdcd297 languageName: node linkType: hard