Skip to content

Commit

Permalink
[WALL-3504] wojtek/switch p2p-v2 to api-v2 (#13762)
Browse files Browse the repository at this point in the history
* feat: sync with api

* feat: siwtch to v2

* feat: fixed unit tests, added extra checks

* feat: added p2p-v2 to cache

* feat: fixed test

* feat: revert accidental change

* feat: fixed some tests

* feat: fixed tests

* feat: fixed tests

* feat: api update

* feat: ignored test

---------

Co-authored-by: Wojciech Brygola <wojciech@regentmarkets.com>
  • Loading branch information
wojciech-deriv and wojciech-deriv committed Feb 29, 2024
1 parent b956c83 commit ab383eb
Show file tree
Hide file tree
Showing 72 changed files with 725 additions and 296 deletions.
2 changes: 1 addition & 1 deletion packages/api-v2/src/APIProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ const APIProvider = ({ children, standalone = false }: PropsWithChildren<TAPIPro
);

useEffect(() => {
let interval_id: NodeJS.Timer;
let interval_id: ReturnType<typeof setInterval>;

if (standalone) {
interval_id = setInterval(() => standaloneDerivAPI.current?.send({ ping: 1 }), 10000);
Expand Down
98 changes: 71 additions & 27 deletions packages/api-v2/src/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,66 @@ type AuthContextType = {
error: unknown;
};

type LoginToken = {
loginId: string;
token: string;
};

// Create the context
const AuthContext = createContext<AuthContextType | undefined>(undefined);

type AuthProviderProps = {
children: React.ReactNode;
cookieTimeout?: number;
};

async function waitForLoginAndToken(): Promise<any> {
const checkLogin = (resolve: (value: any) => void, reject: (reason?: any) => void) => {
function waitForLoginAndTokenWithTimeout(cookieTimeout = 10000) {
// Default timeout of 10 seconds
let timeoutHandle: NodeJS.Timeout | undefined,
cookieTimeoutHandle: NodeJS.Timeout | undefined, // Handle for the cookieTimeout
rejectFunction: (reason?: string) => void; // To be used for rejecting the promise in case of a timeout or cookieTimeout expiry

const checkLogin = (
resolve: (value: { loginId: string; token: string }) => void,
reject: (reason?: string) => void
) => {
const loginId = getActiveLoginIDFromLocalStorage();
const token = getToken(loginId as string);
if (loginId && token) {
resolve({
loginId,
token,
});
clearTimeout(timeoutHandle); // Clear the checkLogin timeout as we've succeeded
clearTimeout(cookieTimeoutHandle); // Clear the cookieTimeout as well
resolve({ loginId, token });
} else {
setTimeout(checkLogin, 100, resolve, reject);
timeoutHandle = setTimeout(checkLogin, 100, resolve, reject);
}
};

return new Promise<any>(checkLogin);
// Function to clear the timeouts and reject the promise if called
const cleanup = () => {
clearTimeout(timeoutHandle);
clearTimeout(cookieTimeoutHandle);
rejectFunction('Operation cancelled');
};

const promise = new Promise<LoginToken>((resolve, reject) => {
rejectFunction = reject; // Assign reject function to be accessible outside promise scope for cleanup

// Set up the cookieTimeout to reject the promise if not resolved in time
cookieTimeoutHandle = setTimeout(() => {
cleanup(); // Cleanup and reject the promise
reject(new Error('Waiting for login or token timed out'));
}, cookieTimeout);

checkLogin(resolve, reject);
});

return {
promise,
cleanup,
};
}

const AuthProvider = ({ children }: AuthProviderProps) => {
const AuthProvider = ({ children, cookieTimeout }: AuthProviderProps) => {
const [loginid, setLoginid] = useState<string | null>(null);

const { mutateAsync } = useMutation('authorize');
Expand All @@ -58,24 +93,33 @@ const AuthProvider = ({ children }: AuthProviderProps) => {
setIsLoading(true);
setIsSuccess(false);

waitForLoginAndToken().then(({ token }) => {
setIsLoading(true);
setIsFetching(true);
mutateAsync({ payload: { authorize: token || '' } })
.then(res => {
setData(res);
setIsLoading(false);
setIsSuccess(true);
})
.catch(() => {
setIsLoading(false);
setIsError(true);
})
.finally(() => {
setIsLoading(false);
setIsFetching(false);
});
});
const { promise, cleanup } = waitForLoginAndTokenWithTimeout(cookieTimeout);

promise
.then(async ({ token }) => {
setIsLoading(true);
setIsFetching(true);
await mutateAsync({ payload: { authorize: token || '' } })
.then(res => {
setData(res);
setIsLoading(false);
setIsSuccess(true);
})
.catch(() => {
setIsLoading(false);
setIsError(true);
})
.finally(() => {
setIsLoading(false);
setIsFetching(false);
});
})
.catch(() => {
setIsLoading(false);
setIsError(true);
});

return cleanup;
}, []);

const switchAccount = useCallback(
Expand Down
109 changes: 109 additions & 0 deletions packages/api-v2/src/hooks/p2p/__tests__/useSettings.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import APIProvider from '../../../APIProvider';
import AuthProvider from '../../../AuthProvider';
import useP2PSettings from '../entity/settings/p2p-settings/useSettings';

const wrapper = ({ children }: { children: JSX.Element }) => (
<APIProvider standalone>
<AuthProvider>{children}</AuthProvider>
</APIProvider>
);

describe('useP2PSettings', () => {
it('should return an empty object if data is not available', () => {
const { result } = renderHook(() => useP2PSettings(), { wrapper });
expect(result.current.data).toEqual({});
});

it('should return the correct data if data is available', () => {
const mockData = {
adverts_active_limit: 3,
adverts_archive_period: 3,
block_trade: {
disabled: 1,
maximum_advert_amount: 20000,
},
cancellation_block_duration: 24,
cancellation_count_period: 24,
cancellation_grace_period: 0,
cancellation_limit: 300,
cross_border_ads_enabled: 1,
disabled: 0,
feature_level: 2,
fixed_rate_adverts: 'enabled',
float_rate_adverts: 'disabled',
float_rate_offset_limit: 10,
local_currencies: [
{
display_name: 'US Dollar',
has_adverts: 0,
symbol: 'USD',
},
],
maximum_advert_amount: 3000,
maximum_order_amount: 1000,
order_daily_limit: 300,
order_payment_period: 15,
payment_methods_enabled: 1,
review_period: 24,
supported_currencies: ['usd'],
is_cross_border_ads_enabled: true,
is_disabled: false,
is_payment_methods_enabled: true,
rate_type: 'fixed',
float_rate_offset_limit_string: '10.00',
reached_target_date: false,
currency_list: [
{
display_name: 'US Dollar',
has_adverts: 0,
is_default: 1,
text: 'USD',
value: 'USD',
},
],
};

window.localStorage.setItem('p2p_v2_p2p_settings', JSON.stringify(mockData));

const { result } = renderHook(() => useP2PSettings(), { wrapper });
const p2p_settings = result.current.data;

expect(p2p_settings?.adverts_active_limit).toBe(3);
expect(p2p_settings?.adverts_archive_period).toBe(3);
expect(p2p_settings?.block_trade?.disabled).toBe(1);
expect(p2p_settings?.block_trade?.maximum_advert_amount).toBe(20000);
expect(p2p_settings?.cancellation_block_duration).toBe(24);
expect(p2p_settings?.cancellation_count_period).toBe(24);
expect(p2p_settings?.cancellation_grace_period).toBe(0);
expect(p2p_settings?.cancellation_limit).toBe(300);
expect(p2p_settings?.cross_border_ads_enabled).toBe(1);
expect(p2p_settings?.disabled).toBe(0);
expect(p2p_settings?.feature_level).toBe(2);
expect(p2p_settings?.fixed_rate_adverts).toBe('enabled');
expect(p2p_settings?.float_rate_adverts).toBe('disabled');
expect(p2p_settings?.float_rate_offset_limit).toBe(10);
expect(p2p_settings?.local_currencies?.[0]?.display_name).toBe('US Dollar');
expect(p2p_settings?.local_currencies?.[0]?.has_adverts).toBe(0);
expect(p2p_settings?.local_currencies?.[0]?.symbol).toBe('USD');
expect(p2p_settings?.maximum_advert_amount).toBe(3000);
expect(p2p_settings?.maximum_order_amount).toBe(1000);
expect(p2p_settings?.order_daily_limit).toBe(300);
expect(p2p_settings?.order_payment_period).toBe(15);
expect(p2p_settings?.payment_methods_enabled).toBe(1);
expect(p2p_settings?.review_period).toBe(24);
expect(p2p_settings?.supported_currencies).toEqual(['usd']);
expect(p2p_settings?.is_cross_border_ads_enabled).toBe(true);
expect(p2p_settings?.is_disabled).toBe(false);
expect(p2p_settings?.is_payment_methods_enabled).toBe(true);
expect(p2p_settings?.rate_type).toBe('fixed');
expect(p2p_settings?.float_rate_offset_limit_string).toBe('10.00');
expect(p2p_settings?.reached_target_date).toBe(false);
expect(p2p_settings?.currency_list?.[0]?.display_name).toBe('US Dollar');
expect(p2p_settings?.currency_list?.[0]?.has_adverts).toBe(0);
expect(p2p_settings?.currency_list?.[0]?.is_default).toBe(1);
expect(p2p_settings?.currency_list?.[0]?.text).toBe('USD');
expect(p2p_settings?.currency_list?.[0]?.value).toBe('USD');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const useAdvertList = (
payload: { ...payload, offset: payload?.offset, limit: payload?.limit },
options: {
getNextPageParam: (lastPage, pages) => {
if (!lastPage?.p2p_advert_list?.list) return;
if (lastPage?.p2p_advert_list?.list.length === 0 || !lastPage?.p2p_advert_list?.list) return;

return pages.length;
},
Expand Down
3 changes: 2 additions & 1 deletion packages/api-v2/src/hooks/p2p/entity/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export * from './advert';
export * from './advertiser';
export * from './counterparty';
export * from './order-dispute';
export * from './payment-method';
export * from './order';
export * from './payment-method';
export * from './settings';
1 change: 1 addition & 0 deletions packages/api-v2/src/hooks/p2p/entity/settings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as settings from './p2p-settings';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useGetSettings } from './useSettings';
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { useEffect } from 'react';
import useSubscription from '../../../../../useSubscription';
import { TSocketResponseData } from '../../../../../../types';
import { useLocalStorage } from 'usehooks-ts';

type TP2PSettings =
| (TSocketResponseData<'p2p_settings'>['p2p_settings'] & {
currency_list: {
display_name: string;
has_adverts: 0 | 1;
is_default?: 1;
text: string;
value: string;
}[];
float_rate_offset_limit_string: string;
is_cross_border_ads_enabled: boolean;
is_disabled: boolean;
is_payment_methods_enabled: boolean;
localCurrency?: string;
rate_type: 'float' | 'fixed';
reached_target_date: boolean;
})
| undefined;

type TCurrencyListItem = {
display_name: string;
has_adverts: 0 | 1;
is_default?: 1;
text: string;
value: string;
};

const useSettings = () => {
const { data, ...rest } = useSubscription('p2p_settings');
const [p2pSettings, setP2PSettings] = useLocalStorage<DeepPartial<TP2PSettings>>('p2p_v2_p2p_settings', {});

useEffect(() => {
if (data) {
const p2p_settings_data = data.p2p_settings;

if (!p2p_settings_data) return undefined;

const reached_target_date = () => {
if (!p2p_settings_data?.fixed_rate_adverts_end_date) return false;

const current_date = new Date(new Date().getTime()).setUTCHours(23, 59, 59, 999);
const cutoff_date = new Date(
new Date(p2p_settings_data?.fixed_rate_adverts_end_date).getTime()
).setUTCHours(23, 59, 59, 999);

return current_date > cutoff_date;
};

let localCurrency;

const currency_list = p2p_settings_data.local_currencies.reduce((acc: TCurrencyListItem[], currency) => {
const { display_name, has_adverts, is_default, symbol } = currency;

if (is_default) localCurrency = symbol;

if (has_adverts) {
acc.push({
display_name,
has_adverts,
is_default,
text: symbol,
value: symbol,
});
}

return acc;
}, []);

setP2PSettings({
...p2p_settings_data,
/** Modified list of local_currencies */
currency_list,
/** Indicates the maximum rate offset for floating rate adverts. */
float_rate_offset_limit_string:
p2p_settings_data?.float_rate_offset_limit?.toString().split('.')?.[1]?.length > 2
? (p2p_settings_data?.float_rate_offset_limit - 0.005).toFixed(2)
: p2p_settings_data?.float_rate_offset_limit.toFixed(2),
/** Indicates if the cross border ads feature is enabled. */
is_cross_border_ads_enabled: Boolean(p2p_settings_data?.cross_border_ads_enabled),
/** Indicates if the P2P service is unavailable. */
is_disabled: Boolean(p2p_settings_data?.disabled),
/** Indicates if the payment methods feature is enabled. */
is_payment_methods_enabled: Boolean(p2p_settings_data?.payment_methods_enabled),
/** Indicates the default local currency */
localCurrency,
/** Indicates if the current rate type is floating or fixed rates */
rate_type: (p2p_settings_data?.float_rate_adverts === 'enabled' ? 'float' : 'fixed') as
| 'float'
| 'fixed',
/** Indicates if the fixed rate adverts end date has been reached. */
reached_target_date: reached_target_date(),
});
}
}, [data, setP2PSettings]);

return {
...rest,
data: p2pSettings,
};
};

export default useSettings;
Loading

0 comments on commit ab383eb

Please sign in to comment.