From 4021d45af3f93fe966abf4eefa0786879248f903 Mon Sep 17 00:00:00 2001 From: UncleSamtoshi <88598461+UncleSamtoshi@users.noreply.github.com> Date: Thu, 16 May 2024 16:49:47 -0500 Subject: [PATCH] feat: reactive price screen (#3271) --- .../price-history/price-history.tsx | 223 +++++++++++------- app/graphql/generated.gql | 1 + app/graphql/generated.ts | 3 +- app/screens/home-screen/home-screen.tsx | 1 + app/screens/price/price-history-screen.tsx | 1 + e2e/06-other-tests.e2e.spec.ts | 7 - 6 files changed, 144 insertions(+), 92 deletions(-) diff --git a/app/components/price-history/price-history.tsx b/app/components/price-history/price-history.tsx index 321f7cfdb0..1515f76b3e 100644 --- a/app/components/price-history/price-history.tsx +++ b/app/components/price-history/price-history.tsx @@ -1,25 +1,28 @@ import { TextStyle, ViewStyle } from "node_modules/@types/react-native/index" import * as React from "react" -import { ActivityIndicator, StyleProp, View } from "react-native" +import { + ActivityIndicator, + StyleProp, + View, + TextInput, + type TextInputProps, +} from "react-native" import { CartesianChart, Line, useChartPressState } from "victory-native" -import type { SharedValue } from "react-native-reanimated" +import Reanimated, { + useAnimatedProps, + useDerivedValue, + type SharedValue, +} from "react-native-reanimated" import { gql } from "@apollo/client" -import { PricePoint, useBtcPriceListQuery } from "@app/graphql/generated" +import { PricePoint, WalletCurrency, useBtcPriceListQuery } from "@app/graphql/generated" import { useI18nContext } from "@app/i18n/i18n-react" import { testProps } from "@app/utils/testProps" import { Button } from "@rneui/base" import { Text, makeStyles, useTheme } from "@rneui/themed" import { Circle } from "@shopify/react-native-skia" - -const multiple = (currentUnit: string) => { - switch (currentUnit) { - case "USDCENT": - return 10 ** -5 - default: - return 1 - } -} +import { GaloyErrorBox } from "../atomic/galoy-error-box" +import { useDisplayCurrency } from "@app/hooks/use-display-currency" const GraphRange = { ONE_DAY: "ONE_DAY", @@ -49,7 +52,6 @@ export const PriceHistory = () => { const { theme: { colors }, } = useTheme() - const { LL } = useI18nContext() const [graphRange, setGraphRange] = React.useState(GraphRange.ONE_DAY) @@ -61,55 +63,59 @@ export const PriceHistory = () => { const { state, isActive } = useChartPressState({ x: 0, y: { y: 0 } }) - function ToolTip({ x, y }: { x: SharedValue; y: SharedValue }) { - return - } - - if (error) { - return {`${error}`} - } + const { formatMoneyAmount } = useDisplayCurrency() - if (loading || data === null || data?.btcPriceList === null) { + function ToolTip({ x, y }: { x: SharedValue; y: SharedValue }) { return ( - - - + <> + + ) } - const ranges = GraphRange[graphRange] - const rangeTimestamps = { - ONE_DAY: 300, - ONE_WEEK: 1800, - ONE_MONTH: 86400, - ONE_YEAR: 86400, - FIVE_YEARS: 86400, - } - - const lastPrice = priceList && priceList[priceList.length - 1] - if (!loading && lastPrice) { - const timeDiff = Date.now() / 1000 - lastPrice.timestamp - if (timeDiff > rangeTimestamps[ranges]) { - setGraphRange(ranges) - } - } - const prices = priceList .filter((price) => price !== null) .map((price) => price as PricePoint) - // FIXME: backend should be updated so that PricePoint is non-nullable + .map((index) => { + const amount = Math.floor(index.price.base / 10 ** index.price.offset) - const currentPriceData = prices[prices.length - 1].price - const startPriceData = prices[0].price - const price = - (currentPriceData.base / 10 ** currentPriceData.offset) * - multiple(currentPriceData.currencyUnit) - const delta = currentPriceData.base / startPriceData.base - 1 + return { + y: amount, + formattedAmount: formatMoneyAmount({ + moneyAmount: { + amount, + currency: WalletCurrency.Usd, + currencyCode: "USDCENT", + }, + }), + timestamp: index.timestamp, + currencyUnit: index.price.currencyUnit, + } + }) + + const currentPriceData = prices[prices.length - 1]?.y + const startPriceData = prices[0]?.y + const delta = + currentPriceData && startPriceData + ? (currentPriceData - startPriceData) / startPriceData + : 0 const color = delta > 0 ? { color: colors._green } : { color: colors.red } - const prices2 = prices.map((index) => ({ - y: (index.price.base / 10 ** index.price.offset) * multiple(index.price.currencyUnit), - })) + const activePrice = useDerivedValue(() => { + const price = isActive + ? prices.find((price) => price.y === state.y.y.value.value) + : prices[prices.length - 1] + + return price?.formattedAmount ?? "" + }) + + const activeTimestamp = useDerivedValue(() => { + const timestamp = isActive + ? prices.find((price) => price.y === state.y.y.value.value)?.timestamp + : undefined + + return `${timestamp ? new Date(timestamp * 1000).toLocaleString(undefined, { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }) : ""}` + }) const label = () => { switch (graphRange) { @@ -136,36 +142,38 @@ export const PriceHistory = () => { const titleStyleForRange = (titleGraphRange: GraphRangeType): StyleProp => { return graphRange === titleGraphRange ? null : styles.titleStyleTime } - return ( - - - - {LL.PriceHistoryScreen.satPrice()} - - ${price.toFixed(2)} - - - - - {(delta * 100).toFixed(2)}%{" "} - - - {label()} - + + + + + {!isActive && !loading ? ( + + + {(delta * 100).toFixed(2)}%{" "} + + {label()} + + ) : ( + + )} - { + {!loading && data ? ( /* eslint @typescript-eslint/ban-ts-comment: "off" */ // @ts-ignore-next-line no-implicit-any error - + {({ points }) => ( <> { strokeWidth={2} curveType="natural" /> - {isActive && } + + {isActive && ( + <> + + + )} )} - } + ) : ( + + + + )}