Skip to content

Commit

Permalink
Further reduce loading state jank
Browse files Browse the repository at this point in the history
  • Loading branch information
christianbaroni committed Jan 10, 2025
1 parent 48520fc commit 5506be4
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 72 deletions.
19 changes: 9 additions & 10 deletions src/components/expanded-state/chart/ChartExpandedStateHeader.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import lang from 'i18n-js';
import React, { useMemo } from 'react';
import { View } from 'react-native';
import Animated, { FadeIn, useAnimatedStyle, withTiming } from 'react-native-reanimated';
import { ColumnWithMargins, Row } from '../../layout';
import ChartContextButton from './ChartContextButton';
import { ChartDateLabel, ChartPercentChangeLabel, ChartPriceLabel } from './chart-data-labels';
import { useChartData } from '@/react-native-animated-charts/src';
import { TIMING_CONFIGS } from '@/components/animations/animationConfigs';
import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon';
import { Column, Columns, Text } from '@/design-system';
import ChartTypes from '@/helpers/chartTypes';
import { convertAmountToNativeDisplay } from '@/helpers/utilities';
import { useAccountSettings } from '@/hooks';
import { useChartData } from '@/react-native-animated-charts/src';
import styled from '@/styled-thing';
import { padding } from '@/styles';
import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon';
import { View } from 'react-native';
import { TIMING_CONFIGS } from '@/components/animations/animationConfigs';
import { ColumnWithMargins, Row } from '../../layout';
import ChartContextButton from './ChartContextButton';
import { ChartDateLabel, ChartPercentChangeLabel, ChartPriceLabel } from './chart-data-labels';

const noPriceData = lang.t('expanded_state.chart.no_price_data');

Expand All @@ -26,7 +26,6 @@ const Container = styled(ColumnWithMargins).attrs({
export default function ChartExpandedStateHeader({
asset,
color: givenColors,
dateRef,
isPool,
latestChange,
latestPrice = noPriceData,
Expand Down Expand Up @@ -84,7 +83,7 @@ export default function ChartExpandedStateHeader({
const firstValue = data?.points?.[0]?.y;
const lastValue = data?.points?.[data.points.length - 1]?.y;

return firstValue === Number(firstValue) ? lastValue / firstValue : 1;
return firstValue === Number(firstValue) ? lastValue / firstValue : undefined;
}, [data]);

const showPriceChangeStyle = useAnimatedStyle(() => {
Expand Down Expand Up @@ -135,7 +134,7 @@ export default function ChartExpandedStateHeader({
</Column>
<Column width="content">
<Animated.View entering={FadeIn.duration(140)} style={showPriceChangeStyle}>
<ChartDateLabel chartTimeDefaultValue={defaultTimeValue} dateRef={dateRef} ratio={ratio} />
<ChartDateLabel chartTimeDefaultValue={defaultTimeValue} ratio={ratio} showPriceChangeStyle={showPriceChangeStyle} />
</Animated.View>
</Column>
</Columns>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import lang from 'i18n-js';
import React from 'react';
import { useAnimatedStyle } from 'react-native-reanimated';
import React, { useCallback } from 'react';
import Animated, { AnimatedStyle, FadeIn, useAnimatedStyle } from 'react-native-reanimated';
import { useRatio } from './useRatio';
import { ChartXLabel, useChartData } from '@/react-native-animated-charts/src';
import { useTheme } from '@/theme';

const MONTHS = [
lang.t('expanded_state.chart.date.months.month_00'),
Expand All @@ -19,7 +20,7 @@ const MONTHS = [
lang.t('expanded_state.chart.date.months.month_11'),
];

function formatDatetime(value, chartTimeDefaultValue) {
function formatDatetime(value: string, chartTimeDefaultValue: string) {
'worklet';
// we have to do it manually due to limitations of reanimated
if (value === '') {
Expand Down Expand Up @@ -70,25 +71,37 @@ function formatDatetime(value, chartTimeDefaultValue) {
return res;
}

export default function ChartDateLabel({ chartTimeDefaultValue, ratio }) {
export default function ChartDateLabel({
chartTimeDefaultValue,
ratio,
showPriceChangeStyle,
}: {
chartTimeDefaultValue: string;
ratio: number | undefined;
showPriceChangeStyle: AnimatedStyle;
}) {
const { isActive } = useChartData();
const sharedRatio = useRatio('ChartDataLabel');
const sharedRatio = useRatio();
const { colors } = useTheme();

const textStyle = useAnimatedStyle(() => {
const realRatio = isActive.value ? sharedRatio.value : ratio;
return {
color: realRatio === 1 ? colors.blueGreyDark : realRatio < 1 ? colors.red : colors.green,
color: realRatio !== undefined ? (realRatio === 1 ? colors.blueGreyDark : realRatio < 1 ? colors.red : colors.green) : 'transparent',
};
}, [ratio]);
});

const formatWorklet = useCallback(
value => {
(value: string) => {
'worklet';
return formatDatetime(value, chartTimeDefaultValue);
},
[chartTimeDefaultValue]
);

return <ChartXLabel align="right" formatWorklet={formatWorklet} size="20pt" style={textStyle} tabularNumbers weight="semibold" />;
return (
<Animated.View entering={FadeIn.duration(140)} style={showPriceChangeStyle}>
<ChartXLabel align="right" formatWorklet={formatWorklet} size="20pt" style={textStyle} tabularNumbers weight="semibold" />
</Animated.View>
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { memo } from 'react';
import { useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
import { DerivedValue, SharedValue, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
import { AnimatedText } from '@/design-system';
import { IS_ANDROID } from '@/env';
import { useChartData } from '@/react-native-animated-charts/src';
import { useTheme } from '@/theme';
import { useRatio } from './useRatio';
import { DataType } from '@/react-native-animated-charts/src/helpers/ChartContext';

function formatNumber(num) {
function formatNumber(num: string) {
'worklet';
const first = num.split('.');
const digits = first[0].split('').reverse();
Expand All @@ -19,25 +21,29 @@ function formatNumber(num) {
return newDigits.reverse().join('') + '.' + first[1];
}

const formatWorklet = (originalY, data, latestChange) => {
const formatWorklet = (originalY: SharedValue<string>, data: DataType, latestChange: number | undefined) => {
'worklet';
const firstValue = data?.points?.[0]?.y;
const lastValue = data?.points?.[data.points.length - 1]?.y;

return firstValue === Number(firstValue) && firstValue
? (() => {
const originalYNumber = Number(originalY?.value);
const value =
originalY?.value === lastValue || !originalY?.value
? parseFloat(latestChange ?? 0)
: ((originalY.value || lastValue) / firstValue) * 100 - 100;
const numValue = parseFloat(value);
originalYNumber === lastValue || !originalYNumber ? latestChange ?? 0 : ((originalYNumber || lastValue) / firstValue) * 100 - 100;

return (IS_ANDROID ? '' : numValue > 0 ? '↑' : numValue < 0 ? '↓' : '') + ' ' + formatNumber(Math.abs(numValue).toFixed(2)) + '%';
return (IS_ANDROID ? '' : value > 0 ? '↑' : value < 0 ? '↓' : '') + ' ' + formatNumber(Math.abs(value).toFixed(2)) + '%';
})()
: '';
};

export default memo(function ChartPercentChangeLabel({ ratio, latestChange }) {
export default memo(function ChartPercentChangeLabel({
latestChange,
ratio,
}: {
latestChange: DerivedValue<number | undefined>;
ratio: number | undefined;
}) {
const { originalY, data, isActive } = useChartData();
const { colors } = useTheme();

Expand All @@ -47,9 +53,9 @@ export default memo(function ChartPercentChangeLabel({ ratio, latestChange }) {
const textStyle = useAnimatedStyle(() => {
const realRatio = isActive.value ? sharedRatio.value : ratio;
return {
color: realRatio === 1 ? colors.blueGreyDark : realRatio < 1 ? colors.red : colors.green,
color: realRatio !== undefined ? (realRatio === 1 ? colors.blueGreyDark : realRatio < 1 ? colors.red : colors.green) : 'transparent',
};
}, [ratio]);
});

return (
<AnimatedText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function calculateDecimalPlaces({
};
}

export function formatNative(value: string, defaultPriceValue: string, nativeSelected: SupportedCurrency[keyof SupportedCurrency]) {
export function formatNative(value: string, defaultPriceValue: string | null, nativeSelected: SupportedCurrency[keyof SupportedCurrency]) {
'worklet';
if (!value) {
return defaultPriceValue || '';
Expand Down
3 changes: 2 additions & 1 deletion src/components/value-chart/Chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useNavigation } from '@/navigation';
import styled from '@/styled-thing';
import { position } from '@/styles';
import { DOG_ADDRESS } from '@/references';
import { IS_IOS } from '@/env';

export const { width: WIDTH } = Dimensions.get('window');

Expand Down Expand Up @@ -43,7 +44,7 @@ const ChartSpinner = styled(ImgixImage).attrs(({ color }) => ({

const Container = styled(Column)({
paddingBottom: 30,
paddingTop: ios ? 0 : 20,
paddingTop: IS_IOS ? 0 : 4,
width: '100%',
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import React, { useCallback, useMemo, useState } from 'react';
import { View } from 'react-native';
import { LayoutChangeEvent, StyleProp, View, ViewStyle } from 'react-native';
import { formatNative } from '../expanded-state/chart/chart-data-labels/ChartPriceLabel';
import { useChartData } from '@/react-native-animated-charts/src';
import { Text } from '@/design-system';
import { useAccountSettings } from '@/hooks';
import { supportedNativeCurrencies } from '@/references';
import { useTheme } from '@/theme';
import { TextSize } from '@/design-system/typography/typeHierarchy';

function trim(val) {
function trim(val: number) {
return Math.min(Math.max(val, 0.05), 0.95);
}

const CenteredLabel = ({ position, fontSize = '14px / 19px (Deprecated)', style, width, ...props }) => {
const CenteredLabel = ({
children,
color,
position,
size = '14px / 19px (Deprecated)',
style,
width,
}: {
children: React.ReactNode;
color: string;
position: number;
size?: TextSize;
style: StyleProp<ViewStyle>;
width: number;
}) => {
const [componentWidth, setWidth] = useState(0);
const onLayout = useCallback(
({
nativeEvent: {
layout: { width: newWidth },
},
}) => {
}: LayoutChangeEvent) => {
setWidth(newWidth);
},
[setWidth]
Expand All @@ -30,29 +46,30 @@ const CenteredLabel = ({ position, fontSize = '14px / 19px (Deprecated)', style,
return (
<View
onLayout={onLayout}
style={{
...style,
left,
opacity: componentWidth ? 1 : 0,
position: 'absolute',
}}
style={[
style,
{
left,
opacity: componentWidth ? 1 : 0,
position: 'absolute',
},
]}
>
<Text color={{ custom: props.color }} size={fontSize} weight="bold">
{props.children}
<Text color={{ custom: color }} size={size} weight="bold">
{children}
</Text>
</View>
);
};

const Labels = ({ color, width, isCard }) => {
const Labels = ({ color, width, isCard }: { color: string; width: number; isCard: boolean }) => {
const { nativeCurrency } = useAccountSettings();
const nativeSelected = supportedNativeCurrencies?.[nativeCurrency];
const { greatestX, greatestY, smallestX, smallestY } = useChartData();
const { colors } = useTheme();

if (!greatestX) {
return null;
}
if (!greatestX || !greatestY || !smallestX || !smallestY) return null;

const positionMin = trim((smallestY.x - smallestX.x) / (greatestX.x - smallestX.x));
const positionMax = trim((greatestY.x - smallestX.x) / (greatestX.x - smallestX.x));

Expand All @@ -62,27 +79,22 @@ const Labels = ({ color, width, isCard }) => {
<CenteredLabel
color={colors.alpha(color, 0.8)}
position={positionMin}
fontSize={isCard ? '13pt' : undefined}
style={{
bottom: isCard ? -24 : -40,
}}
size={isCard ? '13pt' : undefined}
style={{ bottom: isCard ? -24 : -40 }}
width={width}
>
{formatNative(smallestY.y, null, nativeSelected)}
{formatNative(smallestY.y.toString(), null, nativeSelected)}
</CenteredLabel>
) : null}
{positionMax ? (
<CenteredLabel
color={colors.alpha(color, 0.8)}
position={positionMax}
fontSize={isCard ? '13pt' : undefined}
style={{
top: -20,
left: isCard ? 0 : 40,
}}
size={isCard ? '13pt' : undefined}
style={{ top: -20, left: isCard ? 0 : 40 }}
width={width}
>
{formatNative(greatestY.y, null, nativeSelected)}
{formatNative(greatestY.y.toString(), null, nativeSelected)}
</CenteredLabel>
) : null}
</>
Expand Down
36 changes: 28 additions & 8 deletions src/hooks/charts/useChartDataLabels.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import { useCallback } from 'react';
import ChartTypes, { ChartType } from '@/helpers/chartTypes';
import { ChartData } from './useChartInfo';
import { useDerivedValue } from 'react-native-reanimated';
import ChartTypes, { ChartType } from '@/helpers/chartTypes';
import { toFixedWorklet } from '@/safe-math/SafeMath';
import { AssetApiResponse, AssetMetadata } from '@/__swaps__/types/assets';
import { ChartData } from './useChartInfo';

const formatPercentChange = (change = 0) => {
'worklet';
return toFixedWorklet(change, 2);
};

export default function useChartDataLabels({ asset, chartType, points }: { asset: any; chartType: ChartType; points: ChartData[] }) {
type AssetWithPrice = (AssetApiResponse | AssetMetadata) & {
price?: {
relative_change_24h?: number;
relativeChange24h?: number;
value: number;
};
};

export default function useChartDataLabels({
asset,
chartType,
points,
}: {
asset: AssetWithPrice;
chartType: ChartType;
points: ChartData[];
}) {
const latestPrice = asset?.price?.value;

const getPercentChangeForPrice = useCallback(
Expand All @@ -23,11 +40,14 @@ export default function useChartDataLabels({ asset, chartType, points }: { asset
[points]
);

const latestChange = useDerivedValue(() =>
!points || chartType === ChartTypes.day
? formatPercentChange(asset?.price?.relative_change_24h || asset?.price?.relativeChange24h)
: getPercentChangeForPrice(points[0]?.y ?? 0)
);
const latestChange = useDerivedValue(() => {
if (!points || chartType === ChartTypes.day) {
// Handle both AssetApiResponse and AssetMetadata price change fields
const change = asset?.price?.relative_change_24h ?? asset?.price?.relativeChange24h ?? 0;
return formatPercentChange(change);
}
return getPercentChangeForPrice(points[0]?.y ?? 0);
});

return {
latestChange,
Expand Down
Loading

0 comments on commit 5506be4

Please sign in to comment.