Skip to content

Commit

Permalink
Merge pull request #111 from shafin-deriv/shafin/BOT-1860/chore-fix-w…
Browse files Browse the repository at this point in the history
…ebsocket-conenction-issues

Shafin/bot 1860/chore fix websocket conenction issues
  • Loading branch information
shafin-deriv authored Nov 4, 2024
2 parents 6fc6bfd + 79f7079 commit 107d637
Show file tree
Hide file tree
Showing 34 changed files with 4,470 additions and 7,165 deletions.
9,557 changes: 2,949 additions & 6,608 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@
},
"dependencies": {
"@deriv-com/analytics": "^1.5.3",
"@deriv-com/api-hooks": "^1.6.7",
"@deriv-com/quill-ui": "1.18.1",
"@deriv-com/translations": "^1.3.9",
"@deriv-com/ui": "^1.36.4",
"@deriv-com/utils": "latest",
"@deriv/deriv-api": "^1.0.15",
"@deriv/deriv-charts": "^2.5.1",
"@deriv/js-interpreter": "^3.0.0",
"@deriv/quill-icons": "^2.1.2",
"@rsbuild/plugin-basic-ssl": "^1.1.1",
"@svgr/core": "^8.1.0",
"@tanstack/react-query": "^5.29.2",
"blockly": "^10.4.3",
Expand Down Expand Up @@ -86,9 +85,10 @@
"@babel/preset-typescript": "^7.24.1",
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"@deriv-com/eslint-config-deriv": "^2.1.0-beta.3",
"@deriv/api-types": "^1.0.2463",
"@jest/globals": "^29.7.0",
"@rsbuild/core": "^1.0.1-beta.1",
"@rsbuild/plugin-basic-ssl": "^1.1.1",
"@rsbuild/plugin-react": "^1.0.1-beta.1",
"@rsbuild/plugin-sass": "^1.0.1-beta.1",
"@testing-library/dom": "^10.3.1",
Expand Down Expand Up @@ -117,6 +117,7 @@
"eslint-plugin-react": "^7.34.3",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.5",
"eslint-plugin-simple-import-sort": "^12.1.1",
"history": "^5.3.0",
"html-webpack-plugin": "^5.6.0",
"husky": "^7.0.0",
Expand Down
55 changes: 37 additions & 18 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { Fragment, lazy, Suspense } from 'react';
import { Fragment, lazy, Suspense, useEffect } from 'react';
import React from 'react';
import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom';
import RoutePromptDialog from '@/components/route-prompt-dialog';
import Endpoint from '@/pages/endpoint';
import { AppDataProvider } from '@deriv-com/api-hooks';
import { initializeI18n, TranslationProvider } from '@deriv-com/translations';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { URLUtils } from '@deriv-com/utils';
import { StoreProvider } from '../hooks/useStore';
import CoreStoreProvider from './CoreStoreProvider';

const Layout = lazy(() => import('../components/layout'));
const AppRoot = lazy(() => import('./app-root'));

const queryClient = new QueryClient();

const { TRANSLATIONS_CDN_URL, R2_PROJECT_NAME, CROWDIN_BRANCH_NAME } = process.env;
const i18nInstance = initializeI18n({
cdnUrl: `${TRANSLATIONS_CDN_URL}/${R2_PROJECT_NAME}/${CROWDIN_BRANCH_NAME}`,
Expand All @@ -25,18 +22,14 @@ const router = createBrowserRouter(
path='/'
element={
<Suspense fallback={<div>Please wait while we load the app...</div>}>
<QueryClientProvider client={queryClient}>
<TranslationProvider defaultLang='EN' i18nInstance={i18nInstance}>
<AppDataProvider>
<StoreProvider>
<RoutePromptDialog />
<CoreStoreProvider>
<Layout />
</CoreStoreProvider>
</StoreProvider>
</AppDataProvider>
</TranslationProvider>
</QueryClientProvider>
<TranslationProvider defaultLang='EN' i18nInstance={i18nInstance}>
<StoreProvider>
<RoutePromptDialog />
<CoreStoreProvider>
<Layout />
</CoreStoreProvider>
</StoreProvider>
</TranslationProvider>
</Suspense>
}
>
Expand All @@ -48,9 +41,35 @@ const router = createBrowserRouter(
);

function App() {
const { loginInfo, paramsToDelete } = URLUtils.getLoginInfoFromURL();

useEffect(() => {
// Set login info to local storage and remove params from url
if (loginInfo.length) {
try {
const defaultActiveAccount = URLUtils.getDefaultActiveAccount(loginInfo);
if (!defaultActiveAccount) return;

const accountsList: Record<string, string> = {};

loginInfo.forEach(account => {
accountsList[account.loginid] = account.token;
});

localStorage.setItem('accountsList', JSON.stringify(accountsList));

URLUtils.filterSearchParams(paramsToDelete);

localStorage.setItem('authToken', loginInfo[0].token);
localStorage.setItem('active_loginid', loginInfo[0].loginid);
} catch (error) {
console.error('Error setting up login info:', error);
}
}
}, [loginInfo, paramsToDelete]);

React.useEffect(() => {
window?.dataLayer?.push({ event: 'page_load' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
Expand Down
187 changes: 115 additions & 72 deletions src/app/CoreStoreProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,56 @@
import { memo, useEffect } from 'react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { observer } from 'mobx-react-lite';
import { getDecimalPlaces, toMoment } from '@/components/shared';
import { FORM_ERROR_MESSAGES } from '@/components/shared/constants/form-error-messages';
import { initFormErrorMessages } from '@/components/shared/utils/validation/declarative-validation-rules';
import { api_base } from '@/external/bot-skeleton';
import { useApiBase } from '@/hooks/useApiBase';
import { useStore } from '@/hooks/useStore';
import { GetLandingCompanyResult } from '@/stores/client-store';
import { toMoment } from '@/utils/time';
import {
useAuthData,
useGetAccountStatus,
useGetSettings,
useLandingCompany,
useTime,
useWebsiteStatus,
} from '@deriv-com/api-hooks';
import { TLandingCompany, TSocketResponseData } from '@/types/api-types';
import { useTranslations } from '@deriv-com/translations';

const CoreStoreProvider: React.FC<{ children: React.ReactNode }> = memo(({ children }) => {
const { logout, isAuthorizing } = useAuthData();
const { data: accountStatus } = useGetAccountStatus();
const { data: accountSettings } = useGetSettings();
const { data: websiteStatus } = useWebsiteStatus({
refetchOnWindowFocus: false,
});
const { data: landingCompany } = useLandingCompany({
payload: {
landing_company: accountSettings?.country_code ?? '',
},
enabled: !!accountSettings?.country_code,
});
const { data: serverTime, error: serverTimeError } = useTime({
refetchInterval: 15000,
});
const CoreStoreProvider: React.FC<{ children: React.ReactNode }> = observer(({ children }) => {
const { isAuthorizing, isAuthorized, connectionStatus, accountList, activeLoginid } = useApiBase();

const appInitialization = useRef(false);
const accountInitialization = useRef(false);
const timeInterval = useRef<NodeJS.Timeout | null>(null);
const msg_listener = useRef<{ unsubscribe: () => void } | null>(null);
const { client, common } = useStore() ?? {};

const { currentLang } = useTranslations();

const { client, common } = useStore() ?? {
client: {
setApiHookLogout: () => {},
setAccountStatus: () => {},
setAccountSettings: () => {},
setWebsiteStatus: () => {},
setLandingCompany: () => {},
setSwitchBroadcast: () => {},
},
common: {
setServerTime: () => {},
setCurrentLanguage: () => {},
},
};
const activeAccount = useMemo(
() => accountList?.find(account => account.loginid === activeLoginid),
[activeLoginid, accountList]
);

useEffect(() => {
const currentBalanceData = client?.all_accounts_balance?.accounts?.[activeAccount?.loginid ?? ''];
if (currentBalanceData) {
client?.setBalance(currentBalanceData.balance.toFixed(getDecimalPlaces(currentBalanceData.currency)));
client?.setCurrency(currentBalanceData.currency);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeAccount?.loginid, client?.all_accounts_balance]);

useEffect(() => {
if (client && activeAccount) {
client?.setLoginId(activeLoginid);
client?.setAccountList(accountList);
client?.setIsLoggedIn(true);
}
}, [accountList, activeAccount, activeLoginid, client]);

useEffect(() => {
initFormErrorMessages(FORM_ERROR_MESSAGES());

return () => {
if (timeInterval.current) {
clearInterval(timeInterval.current);
}
};
}, []);

useEffect(() => {
Expand All @@ -58,50 +60,91 @@ const CoreStoreProvider: React.FC<{ children: React.ReactNode }> = memo(({ child
}, [currentLang, common]);

useEffect(() => {
if (common && serverTime) {
common.setServerTime(toMoment(serverTime), !!serverTimeError);
}
}, [serverTime, common, serverTimeError]);
if (client && !isAuthorizing && !appInitialization.current) {
appInitialization.current = true;

useEffect(() => {
if (client) {
client.setApiHookLogout(logout);
}
}, [logout, client]);
api_base.api?.websiteStatus().then((res: TSocketResponseData<'website_status'>) => {
client.setWebsiteStatus(res.website_status);
});

useEffect(() => {
if (client && accountStatus) {
client.setAccountStatus(accountStatus);
// Update server time every 10 seconds
timeInterval.current = setInterval(() => {
api_base.api
?.time()
.then((res: TSocketResponseData<'time'>) => {
common.setServerTime(toMoment(res.time), false);
})
.catch(() => {
common.setServerTime(toMoment(Date.now()), true);
});
}, 10000);
}
}, [accountStatus, client]);
}, [client, common, isAuthorizing]);

useEffect(() => {
if (client && accountSettings) {
client.setAccountSettings(accountSettings);
}
}, [accountSettings, client]);
const handleMessage = useCallback(
(res: Record<string, unknown>) => {
if (!res) return;
const data = res.data as TSocketResponseData<'balance'>;
const { msg_type, error } = data;

useEffect(() => {
if (client && websiteStatus) {
client.setWebsiteStatus(websiteStatus);
}
}, [websiteStatus, client]);
if (msg_type === 'balance' && data && !error) {
const balance = data.balance;
if (balance?.accounts) {
client.setAllAccountsBalance(balance);
} else if (balance?.loginid) {
if (!client?.all_accounts_balance?.accounts || !balance?.loginid) return;
const accounts = { ...client.all_accounts_balance.accounts };
const currentLoggedInBalance = accounts[balance.loginid];
currentLoggedInBalance.balance = balance.balance;

const updatedAccounts = {
...client.all_accounts_balance,
accounts: {
...client.all_accounts_balance.accounts,
[balance.loginid]: currentLoggedInBalance,
},
};
client.setAllAccountsBalance(updatedAccounts);
}
}
},
[client]
);

useEffect(() => {
if (client && landingCompany) {
client.setLandingCompany(landingCompany as GetLandingCompanyResult);
if (!isAuthorizing && isAuthorized && client) {
const subscription = api_base.api.onMessage().subscribe(handleMessage);
msg_listener.current = { unsubscribe: subscription.unsubscribe };
}
}, [landingCompany, client]);

return () => {
if (msg_listener.current) {
msg_listener.current.unsubscribe();
}
};
}, [connectionStatus, handleMessage, isAuthorizing, isAuthorized, client]);

useEffect(() => {
if (client) {
client.setSwitchBroadcast(isAuthorizing);
if (!isAuthorizing && isAuthorized && !accountInitialization.current && client) {
accountInitialization.current = true;
api_base.api.getSettings().then((settingRes: TSocketResponseData<'get_settings'>) => {
client?.setAccountSettings(settingRes.get_settings);
api_base.api
.landingCompany({
landing_company: settingRes.get_settings?.country_code,
})
.then((res: TSocketResponseData<'landing_company'>) => {
client?.setLandingCompany(res.landing_company as unknown as TLandingCompany);
});
});

api_base.api.getAccountStatus().then((res: TSocketResponseData<'get_account_status'>) => {
client?.setAccountStatus(res.get_account_status);
});
}
}, [isAuthorizing, client]);
}, [isAuthorizing, isAuthorized, client]);

return <>{children}</>;
});

CoreStoreProvider.displayName = 'CoreStoreProvider';

export default CoreStoreProvider;
8 changes: 2 additions & 6 deletions src/app/__tests__/app.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@ jest.mock('react-router-dom', () => ({
),
}));

jest.mock('@deriv-com/api-hooks', () => ({
AppDataProvider: ({ children }: { children: React.ReactNode }) => (
<div data-testid='app-data-provider'>{children}</div>
),
}));

jest.mock('@deriv-com/translations', () => ({
TranslationProvider: ({ children }: { children: React.ReactNode }) => (
<div data-testid='translation-provider'>{children}</div>
),
initializeI18n: jest.fn(),
localize: jest.fn(),
Localize: jest.fn(),
}));

jest.mock('@tanstack/react-query', () => ({
Expand Down
Loading

0 comments on commit 107d637

Please sign in to comment.