Skip to content

Conversation

@ahmed-deriv
Copy link
Collaborator

Description

(Brief description of what this PR does)

Related Issue: Fixes #(issue)

Type of Change

  • πŸ› Bug fix
  • ✨ New feature
  • πŸ’₯ Breaking change
  • πŸ”§ Refactoring
  • πŸ“š Documentation
  • πŸ§ͺ Tests

Testing

  • Unit tests pass
  • Manual testing completed
  • No console errors
  • No test needed

Screenshots

(Add screenshots if UI changes)

Before:

After:

@github-actions
Copy link
Contributor

github-actions bot commented Nov 6, 2025

Claude finished @ahmed-deriv's task β€”β€” View job


PR Review Complete βœ…

I've completed a thorough review of this PR. Here's my analysis organized by priority:


πŸ”΄ High Priority Issues

1. Memory Leak: Timeout Not Cleaned Up on Component Unmount

packages/trader/src/Modules/SmartChart/Hooks/useSmartChartsAdapter.ts:133-171

Issue: The languageChangeTimeoutRef timeout is only cleaned up when language changes, not on unmount. If the component unmounts while waiting for the 2-second delay, the timeout will still fire and attempt to update state on an unmounted component.

Current code:

React.useEffect(() => {
    // First time: fetch data
    if (!hasFetchedRef.current) {
        hasFetchedRef.current = true;
        fetchChartData();
        return;
    }

    // Language changed: re-enrich existing data after 2 second delay
    if (previousLanguageRef.current !== current_language) {
        // ... sets timeout ...
    }

    // Cleanup timeout on unmount
    return () => {
        if (languageChangeTimeoutRef.current) {
            clearTimeout(languageChangeTimeoutRef.current);
        }
    };
}, [fetchChartData, current_language]);

Problem: The cleanup function is only returned when current_language changes and hasFetchedRef.current is true. On first render, when fetchChartData() is called, no cleanup is returned, so the effect has no cleanup on unmount.

Fix:

React.useEffect(() => {
    // First time: fetch data
    if (!hasFetchedRef.current) {
        hasFetchedRef.current = true;
        fetchChartData();
        // Still need to return cleanup for unmount case
        return () => {
            if (languageChangeTimeoutRef.current) {
                clearTimeout(languageChangeTimeoutRef.current);
            }
        };
    }

    // Language changed: re-enrich existing data after 2 second delay
    if (previousLanguageRef.current !== current_language) {
        previousLanguageRef.current = current_language;
        
        if (languageChangeTimeoutRef.current) {
            clearTimeout(languageChangeTimeoutRef.current);
        }

        languageChangeTimeoutRef.current = setTimeout(() => {
            const { rawActiveSymbols, rawTradingTimes } = rawDataRef.current;
            const enrichedSymbols = enrichActiveSymbols(rawActiveSymbols, rawTradingTimes);

            setChartData(prev => ({
                ...prev,
                activeSymbols: enrichedSymbols,
            }));
        }, 2000);
    }

    // Cleanup timeout on unmount or when dependencies change
    return () => {
        if (languageChangeTimeoutRef.current) {
            clearTimeout(languageChangeTimeoutRef.current);
        }
    };
}, [fetchChartData, current_language]);

🟑 Medium Priority Issues

2. Hard-coded 2-Second Delay Without Explanation

packages/trader/src/Modules/SmartChart/Hooks/useSmartChartsAdapter.ts:152

Issue: The 2-second delay before re-enriching symbols on language change is hard-coded with no explanation. This feels arbitrary and could impact UX.

Questions:

  • Why 2 seconds specifically?
  • Is this waiting for the translation library to load new translations?
  • Should this be a configurable constant?

Recommendation:

// At the top of the file
const LANGUAGE_CHANGE_DEBOUNCE_MS = 2000; // Wait for translation library to fully update

// In the effect
languageChangeTimeoutRef.current = setTimeout(() => {
    // ...
}, LANGUAGE_CHANGE_DEBOUNCE_MS);

Add a comment explaining the rationale for the delay.


3. No Error Handling for Translation Function Failures

packages/trader/src/AppV2/Utils/symbol-categories-utils.ts:9-79

Issue: The localize() function is called extensively but there's no error handling if it fails or returns undefined. While unlikely, defensive programming would be better.

Current pattern:

const market_display_names: Record<string, string> = {
    forex: localize('Forex'),
    synthetic_index: localize('Derived'),
    // ...
};

Potential issue: If localize() returns undefined or throws, this could break the mapping.

Recommendation: Add a safety wrapper or at least handle the fallback case explicitly:

const safeLocalize = (key: string, fallback: string = key) => {
    try {
        const translated = localize(key);
        return translated || fallback;
    } catch {
        return fallback;
    }
};

4. Exporting Previously Private Functions Changes API Surface

packages/trader/src/AppV2/Utils/symbol-categories-utils.ts:9, 24, 46

Issue: Three previously private functions are now exported:

  • getMarketDisplayName (line 9)
  • getSubgroupDisplayName (line 24)
  • getSubmarketDisplayName (line 46)

Impact: This changes the module's API surface area. While this is necessary for the refactoring, ensure these are considered stable public APIs now.

Recommendation: Add JSDoc comments to document these as public APIs:

/**
 * Returns the localized display name for a market code.
 * @param market - Market code (e.g., 'forex', 'synthetic_index')
 * @returns Localized market display name
 * @public
 */
export const getMarketDisplayName = (market: string) => {
    // ...
};

5. Inefficient: Re-enriching All Symbols on Language Change

packages/trader/src/Modules/SmartChart/Hooks/useSmartChartsAdapter.ts:155-161

Issue: When language changes, the entire active symbols array is re-enriched. For large symbol lists (100+ symbols), this could cause performance issues.

Current approach:

const enrichedSymbols = enrichActiveSymbols(rawActiveSymbols, rawTradingTimes);
setChartData(prev => ({
    ...prev,
    activeSymbols: enrichedSymbols,
}));

Performance concern:

  • enrichActiveSymbols is O(n*m) where n = active symbols, m = trading times markets
  • Maps are built from scratch each time
  • All symbols are mapped even if only display names change

Recommendation: Consider memoizing the enrichment function or only updating the display name properties:

const enrichedSymbols = React.useMemo(
    () => enrichActiveSymbols(rawActiveSymbols, rawTradingTimes),
    [rawActiveSymbols, rawTradingTimes, current_language]
);

Or optimize enrichActiveSymbols to be more efficient for translation-only updates.


6. Type Safety: ChartDataResponse rawData Could Be More Specific

packages/trader/src/Modules/SmartChart/Adapters/types.ts:114-120

Issue: The rawData.activeSymbols is typed as ActiveSymbols but is actually the raw, un-enriched data from the API. This could lead to confusion.

Current:

export interface ChartDataResponse {
    rawData: {
        activeSymbols: ActiveSymbols; // ❌ These are NOT enriched ActiveSymbols
        tradingTimes: TradingTimesMap;
    };
    activeSymbols: ActiveSymbols; // βœ… These ARE enriched
    tradingTimes: TradingTimesMap;
}

Recommendation: Use a different type or any[] for raw data:

export interface ChartDataResponse {
    rawData: {
        activeSymbols: any[]; // Raw API response, not enriched
        tradingTimes: any;    // Raw API response
    };
    activeSymbols: ActiveSymbols; // Enriched symbols
    tradingTimes: TradingTimesMap;
}

🟒 Low Priority / Nice to Have

7. Missing Tests for New Language Change Logic

Issue: The new language change detection and re-enrichment logic in useSmartChartsAdapter.ts has no test coverage. This is complex async logic with timeouts that should be tested.

Recommendation: Add tests for:

  • Language change triggers re-enrichment after 2 seconds
  • Multiple rapid language changes only trigger one enrichment (debouncing)
  • Component unmount cleans up pending timeouts
  • Enrichment fails gracefully if raw data is unavailable

Note: I couldn't run tests as they require permissions not in my --allowedTools.


8. Deprecated Code Should Have Removal Timeline

packages/trader/src/Modules/SmartChart/Adapters/transformers.ts:189-247

Issue: MARKET_MAPPINGS is marked as @deprecated but there's no migration timeline or ticket reference.

Recommendation:

// @deprecated Use getMarketDisplayName and getSubmarketDisplayName from symbol-categories-utils.ts instead
// TODO: Remove in Q2 2025 after confirming no external usage (Ticket: JIRA-123)
export const MARKET_MAPPINGS = {

9. Inconsistent Null Checking Pattern

packages/trader/src/Modules/SmartChart/Adapters/transformers.ts:62-72

Issue: The function checks trading_times?.markets twice in slightly different ways:

// Line 62
if (!trading_times?.markets) {
    return active_symbols;
}

// Line 70 (redundant check)
if (!trading_times.markets || !Array.isArray(trading_times.markets)) {
    return active_symbols;
}

Recommendation: Combine into a single check at the top:

if (!trading_times?.markets || !Array.isArray(trading_times.markets)) {
    return active_symbols;
}

10. Magic Number: 2000ms Timeout

See Medium Priority Issue #2 - extract to named constant.


πŸ“Š Summary

Correctness & Edge Cases

Code Quality & Best Practices

Performance

  • βœ… Language change uses memoized raw data (good!)
  • ⚠️ Re-enrichment on language change could be expensive for large symbol lists (Medium Priority build(deps-dev): bump form-data from 4.0.1 to 4.0.4Β #5)
  • ℹ️ 2-second delay adds UX latency - ensure it's necessary

Security

  • βœ… No XSS risks detected
  • βœ… No auth/secrets issues
  • βœ… No malicious code

Test Coverage


🎯 Recommended Action Items

Must Fix Before Merge:

  1. Fix the timeout cleanup memory leak (Issue build(deps-dev): bump webpack-dev-server from 5.2.0 to 5.2.1Β #1)

Should Fix Before Merge:
2. Extract hard-coded 2000ms to a named constant with explanation (Issue #2)
3. Add JSDoc to newly exported functions (Issue #4)

Nice to Have:
4. Add error handling for localize() failures (Issue #3)
5. Consider performance optimization for large symbol lists (Issue #5)
6. Add test coverage for language change logic (Issue #7)
7. Remove redundant null checks (Issue #9)


πŸ“ Questions for Author

  1. Why 2 seconds? What's the rationale for the 2-second delay on language change? Is this waiting for the translation library to update?

  2. Performance testing: Have you tested this with a large number of active symbols (e.g., 100+)? Does the re-enrichment on language change cause any noticeable lag?

  3. Translation coverage: Are all the localize() calls guaranteed to have translations in all supported languages? What happens if a translation is missing?

  4. Testing approach: How should we test the language change functionality? Can you add some unit tests for the new logic?


⚠️ Note on Testing

I was unable to run the test suite or linters as I don't have the necessary permissions (--allowedTools doesn't include npm or yarn). Please ensure you:

  • Run npm test or yarn test to verify all existing tests pass
  • Run your linter (npm run lint) to check for style issues
  • Test the language change functionality manually in the browser

@ahmed-deriv ahmed-deriv merged commit 8fa8bd7 into deriv-com:refactor/champion-chart Nov 6, 2025
1 check passed
@github-actions github-actions bot mentioned this pull request Nov 7, 2025
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants