From 7eda434a22e3afaf2ae3d725942c40fe7f810a76 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Fri, 16 Feb 2024 15:39:04 +0300 Subject: [PATCH] feature(mobile): New chart data provider (#716) --- .../@core-js/src/TonAPI/TonAPIGenerated.ts | 223 +++++++++++++++--- packages/mobile/src/config/index.ts | 2 + .../mobile/src/core/Jetton/Jetton.style.ts | 9 +- packages/mobile/src/core/Jetton/Jetton.tsx | 20 +- .../mobile/src/core/Wallet/ToncoinScreen.tsx | 2 +- packages/mobile/src/hooks/usePreloadChart.ts | 6 +- .../shared/components/Chart/new/Chart.api.tsx | 36 ++- .../src/shared/components/Chart/new/Chart.tsx | 95 ++++---- .../Chart/new/Fallback/Fallback.tsx | 25 +- .../Chart/new/PercentDiff/PercentDiff.tsx | 8 +- .../Chart/new/PriceLabel/PriceLabel.tsx | 2 +- .../shared/components/Chart/new/Rate/Rate.tsx | 7 +- packages/mobile/src/utils/currency.ts | 3 +- 13 files changed, 319 insertions(+), 119 deletions(-) diff --git a/packages/@core-js/src/TonAPI/TonAPIGenerated.ts b/packages/@core-js/src/TonAPI/TonAPIGenerated.ts index ea60b3957..a73a3cd2a 100644 --- a/packages/@core-js/src/TonAPI/TonAPIGenerated.ts +++ b/packages/@core-js/src/TonAPI/TonAPIGenerated.ts @@ -337,6 +337,11 @@ export interface CreditPhase { export interface ActionPhase { /** @example true */ success: boolean; + /** + * @format int32 + * @example 5 + */ + result_code: number; /** * @format int32 * @example 5 @@ -667,8 +672,8 @@ export interface ValidatorsSet { utime_until: number; total: number; main: number; - /** @format int64 */ - total_weight?: number; + /** @example "1152921504606846800" */ + total_weight?: string; list: { public_key: string; /** @format int64 */ @@ -804,8 +809,9 @@ export interface BlockchainRawAccount { * @example 123456789 */ last_transaction_lt: number; - /** @example "active" */ - status: string; + /** @example "088b436a846d92281734236967970612f87fbd64a2cd3573107948379e8e4161" */ + last_transaction_hash?: string; + status: AccountStatus; storage: AccountStorageInfo; } @@ -823,8 +829,7 @@ export interface Account { * @example 123456789 */ last_activity: number; - /** @example "active" */ - status: string; + status: AccountStatus; interfaces?: string[]; /** @example "Ton foundation" */ name?: string; @@ -1357,6 +1362,8 @@ export interface Action { JettonSwap?: JettonSwapAction; SmartContractExec?: SmartContractAction; DomainRenew?: DomainRenewAction; + InscriptionTransfer?: InscriptionTransferAction; + InscriptionMint?: InscriptionMintAction; /** shortly describes what this action is about. */ simple_preview: ActionSimplePreview; } @@ -1402,6 +1409,42 @@ export interface DomainRenewAction { renewer: AccountAddress; } +export interface InscriptionMintAction { + recipient: AccountAddress; + /** + * amount in minimal particles + * @example "123456789" + */ + amount: string; + /** @example "ton20" */ + type: InscriptionMintActionTypeEnum; + /** @example "nano" */ + ticker: string; + /** @example 9 */ + decimals: number; +} + +export interface InscriptionTransferAction { + sender: AccountAddress; + recipient: AccountAddress; + /** + * amount in minimal particles + * @example "123456789" + */ + amount: string; + /** + * @example "Hi! This is your salary. + * From accounting with love." + */ + comment?: string; + /** @example "ton20" */ + type: InscriptionTransferActionTypeEnum; + /** @example "nano" */ + ticker: string; + /** @example 9 */ + decimals: number; +} + export interface NftItemTransferAction { sender?: AccountAddress; recipient?: AccountAddress; @@ -2292,6 +2335,8 @@ export enum ActionTypeEnum { ElectionsRecoverStake = 'ElectionsRecoverStake', ElectionsDepositStake = 'ElectionsDepositStake', DomainRenew = 'DomainRenew', + InscriptionTransfer = 'InscriptionTransfer', + InscriptionMint = 'InscriptionMint', Unknown = 'Unknown', } @@ -2301,6 +2346,18 @@ export enum ActionStatusEnum { Failed = 'failed', } +/** @example "ton20" */ +export enum InscriptionMintActionTypeEnum { + Ton20 = 'ton20', + Gram20 = 'gram20', +} + +/** @example "ton20" */ +export enum InscriptionTransferActionTypeEnum { + Ton20 = 'ton20', + Gram20 = 'gram20', +} + export enum AuctionBidActionAuctionTypeEnum { DNSTon = 'DNS.ton', DNSTg = 'DNS.tg', @@ -2345,6 +2402,7 @@ export interface GetBlockchainAccountTransactionsParams { before_lt?: number; /** * @format int32 + * @min 1 * @max 1000 * @default 100 * @example 100 @@ -2359,7 +2417,14 @@ export interface GetBlockchainAccountTransactionsParams { export interface ExecGetMethodForBlockchainAccountParams { /** - * Supported values: NaN, Null, 10-base digits for tiny int, 0x-prefixed hex digits for int257, all forms of addresses for slice, single-root base64-encoded BOC for cell + * Supported values: + * "NaN" for NaN type, + * "Null" for Null type, + * 10-base digits for tiny int type (Example: 100500), + * 0x-prefixed hex digits for int257 (Example: 0xfa01d78381ae32), + * all forms of addresses for slice type (Example: 0:6e731f2e28b73539a7f85ac47ca104d5840b229351189977bb6151d36b5e3f5e), + * single-root base64-encoded BOC for cell (Example: "te6ccgEBAQEAAgAAAA=="), + * single-root hex-encoded BOC for slice (Example: b5ee9c72010101010002000000) * @example ["0:9a33970f617bcd71acf2cd28357c067aa31859c02820d8f01d74c88063a8f4d8"] */ args?: string[]; @@ -2404,6 +2469,7 @@ export interface GetAccountJettonsHistoryParams { */ before_lt?: number; /** + * @min 1 * @max 1000 * @example 100 */ @@ -2433,6 +2499,7 @@ export interface GetAccountJettonHistoryByIdParams { */ before_lt?: number; /** + * @min 1 * @max 1000 * @example 100 */ @@ -2466,11 +2533,15 @@ export interface GetAccountNftItemsParams { */ collection?: string; /** + * @min 1 * @max 1000 * @default 1000 */ limit?: number; - /** @default 0 */ + /** + * @min 0 + * @default 0 + */ offset?: number; /** * Selling nft items in ton implemented usually via transfer items to special selling account. This option enables including items which owned not directly. @@ -2492,6 +2563,7 @@ export interface GetAccountNftHistoryParams { */ before_lt?: number; /** + * @min 1 * @max 1000 * @example 100 */ @@ -2531,6 +2603,7 @@ export interface GetAccountEventsParams { */ before_lt?: number; /** + * @min 1 * @max 1000 * @example 100 */ @@ -2572,6 +2645,7 @@ export interface GetAccountEventParams { export interface GetAccountTracesParams { /** + * @min 1 * @max 1000 * @default 100 * @example 100 @@ -2635,6 +2709,7 @@ export interface GetAllAuctionsParams { export interface GetNftCollectionsParams { /** * @format int32 + * @min 1 * @max 1000 * @default 100 * @example 15 @@ -2642,6 +2717,7 @@ export interface GetNftCollectionsParams { limit?: number; /** * @format int32 + * @min 0 * @default 0 * @example 10 */ @@ -2650,11 +2726,15 @@ export interface GetNftCollectionsParams { export interface GetItemsFromCollectionParams { /** + * @min 1 * @max 1000 * @default 1000 */ limit?: number; - /** @default 0 */ + /** + * @min 0 + * @default 0 + */ offset?: number; /** * account ID @@ -2671,6 +2751,7 @@ export interface GetNftHistoryByIdParams { */ before_lt?: number; /** + * @min 1 * @max 1000 * @example 100 */ @@ -2694,11 +2775,15 @@ export interface GetNftHistoryByIdParams { export interface GetAccountInscriptionsParams { /** + * @min 1 * @max 1000 * @default 1000 */ limit?: number; - /** @default 0 */ + /** + * @min 0 + * @default 0 + */ offset?: number; /** * account ID @@ -2707,6 +2792,50 @@ export interface GetAccountInscriptionsParams { accountId: string; } +export interface GetAccountInscriptionsHistoryParams { + /** + * omit this parameter to get last events + * @format int64 + * @example 25758317000002 + */ + before_lt?: number; + /** + * @min 1 + * @max 1000 + * @default 100 + * @example 100 + */ + limit?: number; + /** + * account ID + * @example "0:97264395BD65A255A429B11326C84128B7D70FFED7949ABAE3036D506BA38621" + */ + accountId: string; +} + +export interface GetAccountInscriptionsHistoryByTickerParams { + /** + * omit this parameter to get last events + * @format int64 + * @example 25758317000002 + */ + before_lt?: number; + /** + * @min 1 + * @max 1000 + * @default 100 + * @example 100 + */ + limit?: number; + /** + * account ID + * @example "0:97264395BD65A255A429B11326C84128B7D70FFED7949ABAE3036D506BA38621" + */ + accountId: string; + /** @example "nano" */ + ticker: string; +} + export interface GetInscriptionOpTemplateParams { /** @example "ton20" */ type: TypeEnum; @@ -2747,6 +2876,7 @@ export enum GetInscriptionOpTemplateParams1OperationEnum { export interface GetJettonsParams { /** * @format int32 + * @min 1 * @max 1000 * @default 100 * @example 15 @@ -2754,6 +2884,7 @@ export interface GetJettonsParams { limit?: number; /** * @format int32 + * @min 0 * @default 0 * @example 10 */ @@ -2762,11 +2893,15 @@ export interface GetJettonsParams { export interface GetJettonHoldersParams { /** + * @min 1 * @max 1000 * @default 1000 */ limit?: number; - /** @default 0 */ + /** + * @min 0 + * @default 0 + */ offset?: number; /** * account ID @@ -2816,6 +2951,13 @@ export interface GetChartRatesParams { * @example 1668436763 */ end_date?: number; + /** + * @format int + * @min 0 + * @max 200 + * @default 200 + */ + points_count?: number; } export interface GetRawMasterchainInfoExtParams { @@ -4133,25 +4275,6 @@ export class TonAPIGenerated { format: 'json', ...params, }), - - /** - * @description Get all inscriptions by owner address - * - * @tags Inscriptions - * @name GetAccountInscriptions - * @request GET:/v2/accounts/{account_id}/inscriptions - */ - getAccountInscriptions: ( - { accountId, ...query }: GetAccountInscriptionsParams, - params: RequestParams = {}, - ) => - this.http.request({ - path: `/v2/accounts/${accountId}/inscriptions`, - method: 'GET', - query: query, - format: 'json', - ...params, - }), }; dns = { /** @@ -4342,7 +4465,45 @@ export class TonAPIGenerated { }), /** - * @description return comment for making operation with instrospection. please don't use it if you don't know what you are doing + * @description Get the transfer inscriptions history for account. It's experimental API and can be dropped in the future. + * + * @tags Inscriptions + * @name GetAccountInscriptionsHistory + * @request GET:/v2/experimental/accounts/{account_id}/inscriptions/history + */ + getAccountInscriptionsHistory: ( + { accountId, ...query }: GetAccountInscriptionsHistoryParams, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/v2/experimental/accounts/${accountId}/inscriptions/history`, + method: 'GET', + query: query, + format: 'json', + ...params, + }), + + /** + * @description Get the transfer inscriptions history for account. It's experimental API and can be dropped in the future. + * + * @tags Inscriptions + * @name GetAccountInscriptionsHistoryByTicker + * @request GET:/v2/experimental/accounts/{account_id}/inscriptions/{ticker}/history + */ + getAccountInscriptionsHistoryByTicker: ( + { accountId, ticker, ...query }: GetAccountInscriptionsHistoryByTickerParams, + params: RequestParams = {}, + ) => + this.http.request({ + path: `/v2/experimental/accounts/${accountId}/inscriptions/${ticker}/history`, + method: 'GET', + query: query, + format: 'json', + ...params, + }), + + /** + * @description return comment for making operation with inscription. please don't use it if you don't know what you are doing * * @tags Inscriptions * @name GetInscriptionOpTemplate diff --git a/packages/mobile/src/config/index.ts b/packages/mobile/src/config/index.ts index 019083c81..4c0bf8ddf 100644 --- a/packages/mobile/src/config/index.ts +++ b/packages/mobile/src/config/index.ts @@ -49,6 +49,7 @@ export type AppConfigVars = { holdersAppEndpoint: string; holdersService: string; disable_battery: boolean; + disable_jetton_charts: boolean; disable_battery_iap_module: boolean; disable_battery_send: boolean; disable_show_unverified_token: boolean; @@ -85,6 +86,7 @@ const defaultConfig: Partial = { disable_show_unverified_token: false, disable_tonstakers: false, disable_holders_cards: true, + disable_jetton_charts: false, }; export const config = new AppConfig({ diff --git a/packages/mobile/src/core/Jetton/Jetton.style.ts b/packages/mobile/src/core/Jetton/Jetton.style.ts index 3c5797a09..e244d6eb5 100644 --- a/packages/mobile/src/core/Jetton/Jetton.style.ts +++ b/packages/mobile/src/core/Jetton/Jetton.style.ts @@ -9,6 +9,11 @@ export const Wrap = styled.View` flex: 1; `; +export const ChartWrap = styled.View` + margin-bottom: ${ns(24.5)}px; + margin-top: 18px; +`; + export const HeaderWrap = styled.View` align-items: center; padding-horizontal: ${ns(16)}px; @@ -101,15 +106,13 @@ export const ActionLabelWrapper = styled.View` margin-top: ${ns(2)}px; `; - export const ActionsContainer = styled.View` justify-content: center; flex-direction: row; margin-bottom: ${ns(12)}px; `; -export const IconWrap = styled.View` -`; +export const IconWrap = styled.View``; export const HeaderViewDetailsButton = styled(TouchableOpacity).attrs({ activeOpacity: Opacity.ForSmall, diff --git a/packages/mobile/src/core/Jetton/Jetton.tsx b/packages/mobile/src/core/Jetton/Jetton.tsx index 8e7876764..e1933b080 100644 --- a/packages/mobile/src/core/Jetton/Jetton.tsx +++ b/packages/mobile/src/core/Jetton/Jetton.tsx @@ -25,8 +25,9 @@ import { openReceiveJettonModal } from '@tonkeeper/shared/modals/ReceiveJettonMo import { TokenType } from '$core/Send/Send.interface'; import { config } from '$config'; import { openUnverifiedTokenDetailsModal } from '@tonkeeper/shared/modals/UnverifiedTokenDetailsModal'; -import { useWallet } from '@tonkeeper/shared/hooks'; +import { useWallet, useWalletCurrency } from '@tonkeeper/shared/hooks'; import { tk } from '$wallet'; +import { Chart } from '$shared/components/Chart/new/Chart'; const unverifiedTokenHitSlop = { top: 4, left: 4, bottom: 4, right: 4 }; @@ -38,6 +39,8 @@ export const Jetton: React.FC = ({ route }) => { const wallet = useWallet(); const isWatchOnly = wallet && wallet.isWatchOnly; + const fiatCurrency = useWalletCurrency(); + const shouldShowChart = !config.get('disable_jetton_charts') && jettonPrice.fiat !== 0; const nav = useNavigation(); @@ -123,6 +126,14 @@ export const Jetton: React.FC = ({ route }) => { ) : null} + {shouldShowChart && ( + <> + + + + + + )} ); }, [ @@ -134,6 +145,9 @@ export const Jetton: React.FC = ({ route }) => { showSwap, flags.disable_swap, handlePressSwap, + shouldShowChart, + fiatCurrency, + route.params.jettonAddress, ]); if (!jetton) { @@ -170,12 +184,12 @@ export const Jetton: React.FC = ({ route }) => { shouldCloseMenu onPress={handleOpenExplorer} text={t('jetton_open_explorer')} - icon={} + icon={} />, ]} > null}> - + } diff --git a/packages/mobile/src/core/Wallet/ToncoinScreen.tsx b/packages/mobile/src/core/Wallet/ToncoinScreen.tsx index e1a4bec58..1f7aec007 100644 --- a/packages/mobile/src/core/Wallet/ToncoinScreen.tsx +++ b/packages/mobile/src/core/Wallet/ToncoinScreen.tsx @@ -232,7 +232,7 @@ const HeaderList = memo(() => { {shouldShowChart && ( <> - + diff --git a/packages/mobile/src/hooks/usePreloadChart.ts b/packages/mobile/src/hooks/usePreloadChart.ts index 88a7fe543..e1324ab5b 100644 --- a/packages/mobile/src/hooks/usePreloadChart.ts +++ b/packages/mobile/src/hooks/usePreloadChart.ts @@ -2,8 +2,10 @@ import { useChartStore } from '$store/zustand/chart'; import { loadChartData } from '$shared/components/Chart/new/Chart.api'; import { useEffect } from 'react'; import { useQueryClient } from 'react-query'; +import { useWalletCurrency } from '@tonkeeper/shared/hooks'; export function usePreloadChart() { + const fiatCurrency = useWalletCurrency(); const selectedPeriod = useChartStore( (state) => state.selectedPeriod, () => true, @@ -11,8 +13,8 @@ export function usePreloadChart() { const queryClient = useQueryClient(); useEffect(() => { - queryClient.prefetchQuery(['chartFetch', selectedPeriod], () => - loadChartData(selectedPeriod), + queryClient.prefetchQuery(['chartFetch', 'ton', fiatCurrency, selectedPeriod], () => + loadChartData(selectedPeriod, 'ton', fiatCurrency), ); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/packages/mobile/src/shared/components/Chart/new/Chart.api.tsx b/packages/mobile/src/shared/components/Chart/new/Chart.api.tsx index dd64b97c9..9ebf2dbae 100644 --- a/packages/mobile/src/shared/components/Chart/new/Chart.api.tsx +++ b/packages/mobile/src/shared/components/Chart/new/Chart.api.tsx @@ -1,6 +1,32 @@ -import { config } from '$config'; +import { ChartPeriod } from '$store/zustand/chart'; +import { tk } from '$wallet'; -export const loadChartData = (period) => - fetch(`${config.get('tonkeeperEndpoint')}/stock/chart-new?period=${period}`).then( - (res) => res.json(), - ); +const ONE_HOUR = 60 * 60; +const ONE_DAY = 24 * ONE_HOUR; + +function getPeriodFromTimestamp(period) { + const dateNowSec = Math.round(Date.now() / 1000); + switch (period) { + case ChartPeriod.ONE_HOUR: + return dateNowSec - ONE_HOUR; + case ChartPeriod.ONE_DAY: + return dateNowSec - ONE_DAY; + case ChartPeriod.SEVEN_DAYS: + return dateNowSec - ONE_DAY * 7; + case ChartPeriod.ONE_MONTH: + return dateNowSec - ONE_DAY * 31; + case ChartPeriod.SIX_MONTHS: + return dateNowSec - ONE_DAY * 183; + case ChartPeriod.ONE_YEAR: + return dateNowSec - ONE_DAY * 366; + } +} + +export function loadChartData(period: ChartPeriod, token: string, currency: string) { + return tk.wallet.tonapi.rates.getChartRates({ + token, + currency, + end_date: Math.round(Date.now() / 1000), + start_date: getPeriodFromTimestamp(period), + }); +} diff --git a/packages/mobile/src/shared/components/Chart/new/Chart.tsx b/packages/mobile/src/shared/components/Chart/new/Chart.tsx index a4e26a491..d43d63473 100644 --- a/packages/mobile/src/shared/components/Chart/new/Chart.tsx +++ b/packages/mobile/src/shared/components/Chart/new/Chart.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { ChartDot, ChartPath, @@ -8,8 +8,6 @@ import { } from '@rainbow-me/animated-charts'; import { Dimensions, View } from 'react-native'; import { useTheme } from '$hooks/useTheme'; -import { useTokenPrice } from '$hooks/useTokenPrice'; -import { CryptoCurrencies } from '$shared/constants'; import { formatFiatCurrencyAmount } from '$utils/currency'; import { PriceLabel } from './PriceLabel/PriceLabel'; import { PercentDiff } from './PercentDiff/PercentDiff'; @@ -22,71 +20,56 @@ import { changeAlphaValue, convertHexToRGBA, ns } from '$utils'; import { ChartXLabels } from './ChartXLabels/ChartXLabels'; import { ChartPeriod, useChartStore } from '$store/zustand/chart'; import { Fallback } from './Fallback/Fallback'; -import BigNumber from 'bignumber.js'; import { isIOS } from '@tonkeeper/uikit'; -import { useWalletCurrency } from '@tonkeeper/shared/hooks'; -import { WalletCurrency } from '@tonkeeper/core'; +import { WalletCurrency } from '$shared/constants'; export const { width: SIZE } = Dimensions.get('window'); -const ChartComponent: React.FC = () => { +export interface ChartProps { + token: string; + currency: WalletCurrency; +} + +const ChartComponent: React.FC = (props) => { const theme = useTheme(); const selectedPeriod = useChartStore((state) => state.selectedPeriod); const setChartPeriod = useChartStore((state) => state.actions.setChartPeriod); const { isLoading, isFetching, data, isError } = useQuery({ - queryKey: ['chartFetch', selectedPeriod], - queryFn: () => loadChartData(selectedPeriod), + queryKey: ['chartFetch', props.token, props.currency, selectedPeriod], + queryFn: () => loadChartData(selectedPeriod, props.token, props.currency), refetchInterval: 60 * 1000, + keepPreviousData: true, staleTime: 120 * 1000, }); - const [cachedData, setCachedData] = useState(data?.data ?? []); - - useEffect(() => { - if ((data && !isFetching && !isLoading) || !cachedData) { - setCachedData( - selectedPeriod === ChartPeriod.ONE_HOUR - ? stepInterpolation(data.data) - : data.data, - ); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [data, selectedPeriod, isFetching, isLoading]); - - const fiatCurrency = useWalletCurrency(); - const shouldRenderChart = !!cachedData.length; - - const tonPrice = useTokenPrice(CryptoCurrencies.Ton); - - const fiatRate = useMemo(() => { - if (fiatCurrency === WalletCurrency.USD) { - return 1; - } + const points = useMemo( + () => (data?.points ? data.points.map(([x, y]) => ({ x, y })).reverse() : []), + [data], + ); - return new BigNumber(tonPrice.fiat).dividedBy(tonPrice.usd).toNumber(); - }, [fiatCurrency, tonPrice.fiat, tonPrice.usd]); + const shouldRenderChart = !!points.length; const [maxPrice, minPrice] = React.useMemo(() => { - if (!cachedData.length) { + if (!points.length) { return ['0', '0']; } - const mappedPoints = cachedData.map((o) => o.y); + const mappedPoints = points.map((o) => o.y); return [Math.max(...mappedPoints), Math.min(...mappedPoints)].map((value) => - formatFiatCurrencyAmount((value * fiatRate).toFixed(2), fiatCurrency, true), + formatFiatCurrencyAmount(value.toFixed(2), props.currency, true), ); - }, [cachedData, fiatRate, fiatCurrency]); + }, [points, props.currency]); const [firstPoint, latestPoint] = React.useMemo(() => { - if (!cachedData.length) { + if (!points.length) { return [0, 0]; } - const latest = cachedData[cachedData.length - 1].y; - const first = cachedData[0].y; + const latest = points[points.length - 1].y; + const first = points[0].y; return [first, latest]; - }, [cachedData]); + }, [points]); - if (isLoading && !cachedData) { + if (isLoading && !points) { return null; } @@ -95,22 +78,24 @@ const ChartComponent: React.FC = () => { - - - + {latestPoint ? ( + <> + + + + + ) : null} diff --git a/packages/mobile/src/shared/components/Chart/new/Fallback/Fallback.tsx b/packages/mobile/src/shared/components/Chart/new/Fallback/Fallback.tsx index 7eaf6b193..c7b7adff7 100644 --- a/packages/mobile/src/shared/components/Chart/new/Fallback/Fallback.tsx +++ b/packages/mobile/src/shared/components/Chart/new/Fallback/Fallback.tsx @@ -1,23 +1,23 @@ import { useTheme } from '$hooks/useTheme'; import { t } from '@tonkeeper/shared/i18n'; -import { Skeleton, Text } from '$uikit'; -import { deviceWidth, ns } from '$utils'; +import { Skeleton } from '$uikit'; +import { deviceWidth } from '$utils'; import { useNetInfo } from '@react-native-community/netinfo'; import React from 'react'; -import { View } from 'react-native'; +import { Steezy, View, Text } from '@tonkeeper/uikit'; export const Fallback: React.FC<{ isError?: boolean }> = (props) => { const theme = useTheme(); const netInfo = useNetInfo(); return ( - - {props.isError || !netInfo.isConnected ? ( + + {props.isError || netInfo.isConnected === false ? ( <> - + {t('chart.no_internet')} - + {t('chart.check_connection')} @@ -33,3 +33,14 @@ export const Fallback: React.FC<{ isError?: boolean }> = (props) => { ); }; + +const styles = Steezy.create({ + wrap: { + height: 248, + alignItems: 'center', + justifyContent: 'center', + }, + label: { + marginBottom: 2, + }, +}); diff --git a/packages/mobile/src/shared/components/Chart/new/PercentDiff/PercentDiff.tsx b/packages/mobile/src/shared/components/Chart/new/PercentDiff/PercentDiff.tsx index 6af12dd7c..cbf1e5441 100644 --- a/packages/mobile/src/shared/components/Chart/new/PercentDiff/PercentDiff.tsx +++ b/packages/mobile/src/shared/components/Chart/new/PercentDiff/PercentDiff.tsx @@ -11,7 +11,6 @@ import { Platform } from 'react-native'; export interface PercentDiffProps { latestPoint: number; firstPoint: number; - fiatRate: number; fiatCurrency: WalletCurrency; } @@ -41,12 +40,9 @@ const PercentDiffComponent: React.FC = (props) => { const fiatInfo = React.useMemo(() => { let percent = '0 %'; let color: TonThemeColor = 'foregroundSecondary'; - let amountResult: string; const diffInFiat = formatFiatCurrencyAmount( - Math.abs( - ((props.firstPoint * parseFloat(priceDiff)) / 100) * props.fiatRate, - ).toFixed(2), + Math.abs((props.firstPoint * parseFloat(priceDiff)) / 100).toFixed(2), props.fiatCurrency, true, ); @@ -74,7 +70,7 @@ const PercentDiffComponent: React.FC = (props) => { diffInFiat, color, }; - }, [activePoint, priceDiff, props.fiatRate, props.fiatCurrency, priceDiffNumber]); + }, [props.firstPoint, props.fiatCurrency, priceDiff, priceDiffNumber]); const formatPriceWrapper = (point: number) => { if (!point) { diff --git a/packages/mobile/src/shared/components/Chart/new/PriceLabel/PriceLabel.tsx b/packages/mobile/src/shared/components/Chart/new/PriceLabel/PriceLabel.tsx index 99894c94d..4ea564cbd 100644 --- a/packages/mobile/src/shared/components/Chart/new/PriceLabel/PriceLabel.tsx +++ b/packages/mobile/src/shared/components/Chart/new/PriceLabel/PriceLabel.tsx @@ -22,7 +22,7 @@ export const PriceLabel: React.FC<{ selectedPeriod: ChartPeriod }> = (props) => [props.selectedPeriod], ); const chartData = useChartData(); - const [state, setState] = useState(''); + const [state, setState] = useState(t('chart.price')); const formatDateWrapper = useCallback( (text: string) => { diff --git a/packages/mobile/src/shared/components/Chart/new/Rate/Rate.tsx b/packages/mobile/src/shared/components/Chart/new/Rate/Rate.tsx index b6cf45b78..57348f74f 100644 --- a/packages/mobile/src/shared/components/Chart/new/Rate/Rate.tsx +++ b/packages/mobile/src/shared/components/Chart/new/Rate/Rate.tsx @@ -13,7 +13,6 @@ const fontFamily = Platform.select({ const RateComponent: React.FC<{ latestPoint: number; - fiatRate: number; fiatCurrency: WalletCurrency; }> = (props) => { const chartData = useChartData(); @@ -21,11 +20,11 @@ const RateComponent: React.FC<{ const formattedLatestPrice = useMemo( () => formatFiatCurrencyAmount( - (activePoint * props.fiatRate).toFixed(4), + activePoint.toFixed(activePoint > 0.0001 ? 4 : 8), props.fiatCurrency, true, ), - [props.fiatCurrency, props.fiatRate, activePoint], + [props.fiatCurrency, activePoint], ); const formatPriceWrapper = useCallback( @@ -49,7 +48,7 @@ const RateComponent: React.FC<{ }, (result, previous) => { if (result !== previous) { - runOnJS(formatPriceWrapper)(result); + runOnJS(formatPriceWrapper)(parseFloat(result)); } }, [formatPriceWrapper], diff --git a/packages/mobile/src/utils/currency.ts b/packages/mobile/src/utils/currency.ts index ad7021ca3..6123ba3c1 100644 --- a/packages/mobile/src/utils/currency.ts +++ b/packages/mobile/src/utils/currency.ts @@ -8,10 +8,11 @@ import { truncateDecimal, } from './number'; import { getNumberFormatSettings } from 'react-native-localize'; +import { WalletCurrency } from '$shared/constants'; export function formatFiatCurrencyAmount( amount: any, - currency: FiatCurrency, + currency: FiatCurrency | WalletCurrency, hasThinSpace?: boolean, hasNegative?: boolean, ): string {