Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/scripts/known-feature-flag-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const FILE_SOURCES: Array<{ key: string; file: string; exportName: string }> = [
{ key: 'FULL_PAGE_ACCOUNT_LIST_FLAG_NAME', file: sel('fullPageAccountList'), exportName: 'FULL_PAGE_ACCOUNT_LIST_FLAG_NAME' },
{ key: 'IMPORT_SRP_WORD_SUGGESTION_FLAG_NAME', file: sel('importSrpWordSuggestion'), exportName: 'IMPORT_SRP_WORD_SUGGESTION_FLAG_NAME' },
{ key: 'ASSETS_UNIFY_STATE_FLAG', file: sel('assetsUnifyState'), exportName: 'ASSETS_UNIFY_STATE_FLAG' },
{ key: 'TOKEN_DETAILS_OHLCV_WS_INTEGRATION_FLAG_KEY', file: sel('tokenDetailsOhlcvWsIntegration'), exportName: 'TOKEN_DETAILS_OHLCV_WS_INTEGRATION_FLAG_KEY' },
];

function resolveConstantFromFile(filePath: string, constantName: string): string | undefined {
Expand Down
11 changes: 11 additions & 0 deletions app/components/UI/AssetOverview/Price/Price.advanced.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ jest.mock('../PriceChart/PriceChart.context', () => ({
}),
}));

jest.mock(
'../../../../selectors/featureFlagController/tokenDetailsOhlcvWsIntegration',
() => ({
selectTokenDetailsOhlcvWsEnabled: jest.fn(() => false),
}),
);

jest.mock('react-redux', () => {
const actual = jest.requireActual('react-redux');
return {
Expand Down Expand Up @@ -89,6 +96,10 @@ jest.mock('../../Charts/AdvancedChart/useOHLCVChart', () => ({
useOHLCVChart: (...args: unknown[]) => mockUseOHLCVChart(...args),
}));

jest.mock('../../Charts/AdvancedChart/useOHLCVRealtime', () => ({
useOHLCVRealtime: () => ({ latestBar: null }),
}));

jest.mock('../../Charts/AdvancedChart/TimeRangeSelector', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const { View, Pressable, Text } = require('react-native');
Expand Down
48 changes: 48 additions & 0 deletions app/components/UI/AssetOverview/Price/Price.advanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
type TimeRange,
} from '../../Charts/AdvancedChart/TimeRangeSelector';
import { useOHLCVChart } from '../../Charts/AdvancedChart/useOHLCVChart';
import { useOHLCVRealtime } from '../../Charts/AdvancedChart/useOHLCVRealtime';
import { OHLCVBar } from '../../Charts/AdvancedChart/OHLCVBar/OHLCVBar';
import {
Box,
Expand All @@ -62,9 +63,23 @@
TraceName,
TraceOperation,
} from '../../../../util/trace';
import { selectTokenDetailsOhlcvWsEnabled } from '../../../../selectors/featureFlagController/tokenDetailsOhlcvWsIntegration';

const EMPTY_INDICATORS: IndicatorType[] = [];

/**
* Maps UI time-range selections to the WebSocket candle interval used by
* OHLCVService. These match the default intervals the REST OHLCV API returns
* for each timePeriod.
*/
const WS_INTERVAL_BY_TIME_RANGE: Record<TimeRange, string> = {
'1H': '1m',
'1D': '15m',
'1W': '1h',
'1M': '1d',
'1Y': '1d',
};

const TIME_RANGE_LABELS: Record<TimeRange, string> = {
'1H': 'asset_overview.chart_time_period.1h',
'1D': 'asset_overview.chart_time_period.1d',
Expand Down Expand Up @@ -129,11 +144,12 @@
timePeriod = '1d',
chartNavigationButtons = [],
setTimePeriod,
}: PriceAdvancedProps) => {

Check failure on line 147 in app/components/UI/AssetOverview/Price/Price.advanced.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 31 to the 30 allowed.

See more on https://sonarcloud.io/project/issues?id=metamask-mobile&issues=AZ4Wbyc1-DQLXMz_mHED&open=AZ4Wbyc1-DQLXMz_mHED&pullRequest=29739
const dispatch = useDispatch();
const { trackEvent, createEventBuilder } = useAnalytics();
const [timeRange, setTimeRange] = useState<TimeRange>('1D');
const chartType = useSelector(selectTokenOverviewChartType);
const isOhlcvWsEnabled = useSelector(selectTokenDetailsOhlcvWsEnabled);
const [crosshairData, setCrosshairData] = useState<CrosshairData | null>(
null,
);
Expand Down Expand Up @@ -293,6 +309,37 @@
vsCurrency: currentCurrency,
});

const wsInterval = WS_INTERVAL_BY_TIME_RANGE[timeRange];
const wsEnabled =
isOhlcvWsEnabled &&
!chartLoading &&
ohlcvData.length >= CHART_DATA_THRESHOLD &&
!hasEmptyData &&
!chartError;

const { latestBar } = useOHLCVRealtime({
assetId,
interval: wsInterval,
currency: currentCurrency,
timePeriod: timeRange.toLowerCase(),
enabled: wsEnabled,
});

// TradingView Advanced Charts Bar.time expects milliseconds
// https://www.tradingview.com/charting-library-docs/latest/api/interfaces/Datafeed.Bar/
// OHLCVService delivers bars with `timestamp` in Unix seconds — multiply by 1000
const realtimeBar = useMemo(() => {
if (!latestBar) return undefined;
return {
time: latestBar.timestamp * 1000,
open: latestBar.open,
high: latestBar.high,
low: latestBar.low,
close: latestBar.close,
volume: latestBar.volume,
};
}, [latestBar]);
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated

const ohlcvPagination = useMemo(
() => ({
nextCursor,
Expand Down Expand Up @@ -554,6 +601,7 @@
<AdvancedChart
ohlcvData={ohlcvData}
ohlcvSeriesKey={ohlcvSeriesKey}
realtimeBar={realtimeBar}
height={CHART_HEIGHT}
showVolume={chartType === ChartType.Candles}
volumeOverlay
Expand Down
4 changes: 4 additions & 0 deletions app/components/UI/AssetOverview/Price/Price.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ jest.mock('../../Charts/AdvancedChart/useOHLCVChart', () => ({
useOHLCVChart: (...args: unknown[]) => mockUseOHLCVChart(...args),
}));

jest.mock('../../Charts/AdvancedChart/useOHLCVRealtime', () => ({
useOHLCVRealtime: () => ({ latestBar: null }),
}));

jest.mock('../PriceChart/PriceChart', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const { View } = require('react-native');
Expand Down
Loading
Loading