-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathApp.tsx
More file actions
106 lines (97 loc) · 4.33 KB
/
Copy pathApp.tsx
File metadata and controls
106 lines (97 loc) · 4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { lazy, Suspense } from 'react';
import React from 'react';
import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom';
import ChunkLoader from '@/components/loader/chunk-loader';
import LocalStorageSyncWrapper from '@/components/localStorage-sync-wrapper';
import RoutePromptDialog from '@/components/route-prompt-dialog';
import { useAccountSwitching } from '@/hooks/useAccountSwitching';
import { useLanguageFromURL } from '@/hooks/useLanguageFromURL';
import { useOAuthCallback } from '@/hooks/useOAuthCallback';
import { StoreProvider } from '@/hooks/useStore';
import { OAuthTokenExchangeService } from '@/services/oauth-token-exchange.service';
import { initializeI18n, localize, TranslationProvider } from '@deriv-com/translations';
import CoreStoreProvider from './CoreStoreProvider';
import './app-root.scss';
const Layout = lazy(() => import('../components/layout'));
const AppRoot = lazy(() => import('./app-root'));
// Translations CDN is optional — requires TRANSLATIONS_CDN_URL, R2_PROJECT_NAME, and CROWDIN_BRANCH_NAME env vars.
// Without these, the app defaults to English. See user-guide/03-white-labeling.md#translations for setup instructions.
const i18nInstance = initializeI18n({ cdnUrl: '' });
/**
* Component wrapper to handle language URL parameter
* Uses the useLanguageFromURL hook to process language switching
*/
const LanguageHandler = ({ children }: { children: React.ReactNode }) => {
useLanguageFromURL();
return <>{children}</>;
};
const router = createBrowserRouter(
createRoutesFromElements(
<Route
path='/'
element={
<Suspense
fallback={<ChunkLoader message={localize('Please wait while we connect to the server...')} />}
>
<TranslationProvider defaultLang='EN' i18nInstance={i18nInstance}>
<LanguageHandler>
<StoreProvider>
<LocalStorageSyncWrapper>
<RoutePromptDialog />
<CoreStoreProvider>
<Layout />
</CoreStoreProvider>
</LocalStorageSyncWrapper>
</StoreProvider>
</LanguageHandler>
</TranslationProvider>
</Suspense>
}
>
{/* All child routes will be passed as children to Layout */}
<Route index element={<AppRoot />} />
</Route>
)
);
/**
* Main App component
*
* Responsibilities:
* 1. OAuth callback handling (via useOAuthCallback hook)
* 2. Account switching from URL (via useAccountSwitching hook)
* 3. Router provider setup
*
* All complex logic has been extracted into custom hooks for better maintainability
*/
function App() {
// Handle OAuth callback flow (CSRF validation + code extraction)
const { isProcessing, isValid, params, error, cleanupURL } = useOAuthCallback();
// Handle account switching via URL parameter
useAccountSwitching();
// Process the authorization code when OAuth callback is valid
React.useEffect(() => {
if (!isProcessing && isValid && params.code) {
// Exchange authorization code for access token
OAuthTokenExchangeService.exchangeCodeForToken(params.code)
.then(response => {
if (response.access_token) {
cleanupURL();
} else if (response.error) {
console.error('❌ Token exchange failed:', response.error);
console.error('Error description:', response.error_description);
// Clean up URL even on error
cleanupURL();
}
})
.catch(error => {
console.error('❌ Token exchange request failed:', error);
// Clean up URL even on error
cleanupURL();
});
} else if (!isProcessing && error) {
console.error('OAuth callback error:', error);
}
}, [isProcessing, isValid, params.code, error, cleanupURL]);
return <RouterProvider router={router} />;
}
export default App;