Skip to content

Commit

Permalink
[FEQ] / Ameerul / FEQ-1969 Refactor useAdvertiserInfo hook (deriv-com…
Browse files Browse the repository at this point in the history
…#14611)

* chore: refactored useAdvertiserInfo hook

* fix: failing test cases

* fix: payload type and useAdvertiserStats test case

* fix: sonar cloud issue

* fix: online status always online in chat, minor Type errors

* chore: added comments and did minor cleanups

* chore: added useIsAdvertiser hook, fixed handling useAdvertiserInfo states issue

* fix: reverted removal of provider, fixed issues with verification flow

* chore: added tsdocs
  • Loading branch information
ameerul-deriv authored Apr 22, 2024
1 parent 39717e8 commit 230f15c
Show file tree
Hide file tree
Showing 39 changed files with 474 additions and 177 deletions.
Original file line number Diff line number Diff line change
@@ -1,53 +1,91 @@
import { useMemo } from 'react';
import useQuery from '../../../../../useQuery';
import useAuthorize from '../../../../useAuthorize';
import { useCallback, useEffect } from 'react';
import { useLocalStorage } from 'usehooks-ts';
import useSubscription from '../../../../../useSubscription';
import { TSocketRequestPayload, TSocketResponseData } from '../../../../../../types';

type TP2PAdvertiserInfo = TSocketResponseData<'p2p_advertiser_info'>['p2p_advertiser_info'] & {
has_basic_verification: boolean;
has_full_verification: boolean;
is_approved_boolean: boolean;
is_blocked_boolean: boolean;
is_favourite_boolean: boolean;
is_listed_boolean: boolean;
is_online_boolean: boolean;
should_show_name: boolean;
};

type TPayload = NonNullable<TSocketRequestPayload<'p2p_advertiser_info'>> & { id?: string };

/** This custom hook returns information about the given advertiser ID */
const useAdvertiserInfo = (id?: string) => {
const { isSuccess } = useAuthorize();
const { data, ...rest } = useQuery('p2p_advertiser_info', { payload: { id }, options: { enabled: isSuccess } });
const { data, error, subscribe: subscribeAdvertiserInfo, ...rest } = useSubscription('p2p_advertiser_info');

/**
* Use different local storage key for each advertiser, one to keep the current user's info, the other to keep the advertiser's info
* This is to prevent the current user's info from being overwritten by the advertiser's info when the current user views the advertiser's profile.
*
* Key removal is handled in useAdvertiserStats hook's useEffect.
* */
const local_storage_key = id ? `p2p_v2_p2p_advertiser_info_${id}` : 'p2p_v2_p2p_advertiser_info';
const [p2p_advertiser_info, setP2PAdvertiserInfo] = useLocalStorage<DeepPartial<TP2PAdvertiserInfo>>(
local_storage_key,
{}
);

const subscribe = useCallback(
(payload?: TPayload) => {
subscribeAdvertiserInfo({ payload });
},
[subscribeAdvertiserInfo]
);

// Add additional information to the p2p_advertiser_info data
const modified_data = useMemo(() => {
const advertiser_info = data?.p2p_advertiser_info;

if (!advertiser_info) return undefined;

const {
basic_verification,
full_verification,
is_approved,
is_blocked,
is_favourite,
is_listed,
is_online,
show_name,
} = advertiser_info;

return {
...advertiser_info,
/** Indicating whether the advertiser's identify has been verified. */
basic_verification: Boolean(basic_verification),
/** Indicating whether the advertiser's address has been verified. */
full_verification: Boolean(full_verification),
/** The approval status of the advertiser. */
is_approved: Boolean(is_approved),
/** Indicates that the advertiser is blocked by the current user. */
is_blocked: Boolean(is_blocked),
/** Indicates that the advertiser is a favourite of the current user. */
is_favourite: Boolean(is_favourite),
/** Indicates if the advertiser's active adverts are listed. When false, adverts won't be listed regardless if they are active or not. */
is_listed: Boolean(is_listed),
/** Indicates if the advertiser is currently online. */
is_online: Boolean(is_online),
/** When true, the advertiser's real name will be displayed on to other users on adverts and orders. */
show_name: Boolean(show_name),
};
}, [data?.p2p_advertiser_info]);
useEffect(() => {
if (data) {
const advertiser_info = data?.p2p_advertiser_info;

if (!advertiser_info) return;

const {
basic_verification,
full_verification,
is_approved,
is_blocked,
is_favourite,
is_listed,
is_online,
show_name,
} = advertiser_info;

setP2PAdvertiserInfo({
...advertiser_info,
/** Indicating whether the advertiser's identify has been verified. */
has_basic_verification: Boolean(basic_verification),
/** Indicating whether the advertiser's address has been verified. */
has_full_verification: Boolean(full_verification),
/** The approval status of the advertiser. */
is_approved_boolean: Boolean(is_approved),
/** Indicates that the advertiser is blocked by the current user. */
is_blocked_boolean: Boolean(is_blocked),
/** Indicates that the advertiser is a favourite of the current user. */
is_favourite_boolean: Boolean(is_favourite),
/** Indicates if the advertiser's active adverts are listed. When false, adverts won't be listed regardless if they are active or not. */
is_listed_boolean: Boolean(is_listed),
/** Indicates if the advertiser is currently online. */
is_online_boolean: Boolean(is_online),
/** When true, the advertiser's real name will be displayed on to other users on adverts and orders. */
should_show_name: Boolean(show_name),
});
} else if (error) {
setP2PAdvertiserInfo({});
}
}, [data, error, setP2PAdvertiserInfo]);

return {
/** P2P advertiser information */
data: modified_data,
data: p2p_advertiser_info,
error,
subscribe,
...rest,
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import useAuthorize from '../../../../useAuthorize';
import useQuery from '../../../../../useQuery';

/** A custom hook that returns the list of P2P Advertiser Payment Methods */
const useAdvertiserPaymentMethods = () => {
const useAdvertiserPaymentMethods = (is_enabled = true) => {
const { isSuccess } = useAuthorize();
const { data, ...rest } = useQuery('p2p_advertiser_payment_methods', { options: { enabled: isSuccess } });
const { data, ...rest } = useQuery('p2p_advertiser_payment_methods', {
options: { enabled: isSuccess && is_enabled },
});

// Modify the response to add additional information
const modified_data = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import AdvertiserNameStats from './AdvertiserNameStats';
import AdvertiserNameToggle from './AdvertiserNameToggle';
import './AdvertiserName.scss';

const AdvertiserName = ({ advertiserStats }: { advertiserStats: TAdvertiserStats }) => {
const AdvertiserName = ({ advertiserStats }: { advertiserStats: DeepPartial<TAdvertiserStats> }) => {
const {
data: { email },
} = useSettings();
Expand All @@ -27,7 +27,7 @@ const AdvertiserName = ({ advertiserStats }: { advertiserStats: TAdvertiserStats
<Text size='md' weight='bold'>
{name}
</Text>
{(advertiserStats?.show_name || !isMyProfile) && (
{(advertiserStats?.should_show_name || !isMyProfile) && (
<Text color='less-prominent' size='sm'>
({advertiserStats?.fullName})
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import './AdvertiserNameBadges.scss';
*
* Use cases are usually in My Profile page and Advertiser page used under the advertiser's name
*/
const AdvertiserNameBadges = ({ advertiserStats }: { advertiserStats: TAdvertiserStats }) => {
const AdvertiserNameBadges = ({ advertiserStats }: { advertiserStats: DeepPartial<TAdvertiserStats> }) => {
const { isAddressVerified, isIdentityVerified, totalOrders } = advertiserStats || {};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import './AdvertiserNameStats.scss';
*
* Use cases are to show this in My Profile and Advertiser page
*/
const AdvertiserNameStats = ({ advertiserStats }: { advertiserStats: TAdvertiserStats }) => {
const AdvertiserNameStats = ({ advertiserStats }: { advertiserStats: DeepPartial<TAdvertiserStats> }) => {
const { isMobile } = useDevice();
const isMyProfile = getCurrentRoute() === 'my-profile';

Expand All @@ -39,12 +39,15 @@ const AdvertiserNameStats = ({ advertiserStats }: { advertiserStats: TAdvertiser
<div>
{!isMyProfile && (
<div className='border-r-[1px] border-solid border-r-[#ededed]'>
<OnlineStatusIcon isOnline={isOnline} isRelative size='0.8em' />
<OnlineStatusLabel isOnline={isOnline} lastOnlineTime={lastOnlineTime} />
<OnlineStatusIcon isOnline={!!isOnline} isRelative size='0.8em' />
<OnlineStatusLabel
isOnline={!!isOnline}
lastOnlineTime={lastOnlineTime === null ? undefined : lastOnlineTime}
/>
</div>
)}
<Text color='less-prominent' size='sm'>
Joined {daysSinceJoined}d
Joined {daysSinceJoined && daysSinceJoined > 0 ? `${daysSinceJoined}d` : 'Today'}
</Text>
</div>
{!ratingAverage && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import { Text, ToggleSwitch } from '@deriv-com/ui';
import './AdvertiserNameToggle.scss';

type TAdvertiserNameToggle = {
advertiserInfo: TAdvertiserStats;
advertiserInfo: DeepPartial<TAdvertiserStats>;
onToggle?: (shouldShowRealName: boolean) => void;
};
const AdvertiserNameToggle = memo(({ advertiserInfo, onToggle }: TAdvertiserNameToggle) => {
const [shouldShowRealName, setShouldShowRealName] = useState(false);
const { mutate: advertiserUpdate } = p2p.advertiser.useUpdate();

useEffect(() => {
setShouldShowRealName(advertiserInfo?.show_name || false);
}, [advertiserInfo?.show_name]);
setShouldShowRealName(advertiserInfo?.should_show_name || false);
}, [advertiserInfo?.should_show_name]);

const onToggleShowRealName = () => {
advertiserUpdate({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const mockProps = {
advertiserStats: {
fullName: 'Jane Doe',
name: 'Jane',
show_name: 1,
should_show_name: true,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const wrapper = ({ children }: { children: JSX.Element }) => (
const mockProps = {
advertiserInfo: {
fullName: 'Jane Doe',
show_name: 0,
should_show_name: false,
},
onToggle: jest.fn(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useHistory } from 'react-router-dom';
import { TAdvertsTableRowRenderer } from 'types';
import { Badge, BuySellForm, PaymentMethodLabel, StarRating, UserAvatar } from '@/components';
import { ADVERTISER_URL, BUY_SELL } from '@/constants';
import { useIsAdvertiser } from '@/hooks';
import { generateEffectiveRate, getCurrentRoute } from '@/utils';
import { p2p, useExchangeRateSubscription } from '@deriv/api-v2';
import { LabelPairedChevronRightMdRegularIcon } from '@deriv/quill-icons';
Expand All @@ -20,8 +21,9 @@ const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => {
const history = useHistory();
const isBuySellPage = getCurrentRoute() === 'buy-sell';

const isAdvertiser = useIsAdvertiser();
const { data: paymentMethods } = p2p.paymentMethods.useGet();
const { data: advertiserPaymentMethods } = p2p.advertiserPaymentMethods.useGet();
const { data: advertiserPaymentMethods } = p2p.advertiserPaymentMethods.useGet(isAdvertiser);
const { data } = p2p.advertiser.useGetInfo() || {};
const { daily_buy = 0, daily_buy_limit = 0, daily_sell = 0, daily_sell_limit = 0 } = data || {};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react';
import { THooks } from 'types';
import { Search } from '@/components/Search';
import { p2p } from '@deriv/api-v2';
import { Checkbox, Text } from '@deriv-com/ui';
import './FilterModalPaymentMethods.scss';
import { THooks } from 'types';

type TFilterModalPaymentMethodsProps = {
selectedPaymentMethods: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { debounce } from 'lodash';
import { Controller, useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom';
import { BUY_SELL_URL } from '@/constants';
import { useAdvertiserInfoState } from '@/providers/AdvertiserInfoStateProvider';
import { p2p } from '@deriv/api-v2';
import { DerivLightIcCashierUserIcon } from '@deriv/quill-icons';
import { Button, Input, Modal, Text, useDevice } from '@deriv-com/ui';
Expand All @@ -28,6 +29,7 @@ const NicknameModal = ({ isModalOpen, setIsModalOpen }: TNicknameModalProps) =>

const history = useHistory();
const { error: createError, isError, isSuccess, mutate, reset } = p2p.advertiser.useCreate();
const { setHasCreatedAdvertiser } = useAdvertiserInfoState();
const { isMobile } = useDevice();
const textSize = isMobile ? 'md' : 'sm';
const debouncedReset = debounce(reset, 3000);
Expand All @@ -39,13 +41,14 @@ const NicknameModal = ({ isModalOpen, setIsModalOpen }: TNicknameModalProps) =>
useEffect(() => {
if (isSuccess) {
setIsModalOpen(false);
setHasCreatedAdvertiser(true);
} else if (isError) {
debouncedReset();
}
}, [isError, isSuccess]);
}, [isError, isSuccess, setHasCreatedAdvertiser]);

return (
<Modal className='p2p-v2-nickname-modal' isOpen={!!isModalOpen}>
<Modal ariaHideApp={false} className='p2p-v2-nickname-modal' isOpen={!!isModalOpen}>
<form onSubmit={handleSubmit(onSubmit)}>
<Modal.Body className='p2p-v2-nickname-modal__body'>
<DerivLightIcCashierUserIcon height='12.8rem' width='12.8rem' />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useAdvertiserInfoState } from '@/providers/AdvertiserInfoStateProvider';
import { APIProvider, AuthProvider, p2p } from '@deriv/api-v2';
import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
Expand All @@ -17,6 +18,7 @@ const mockedMutate = jest.fn();
const mockedReset = jest.fn();
const mockedUseAdvertiserCreate = p2p.advertiser.useCreate as jest.MockedFunction<typeof p2p.advertiser.useCreate>;
const mockPush = jest.fn();
const mockUseAdvertiserInfoState = useAdvertiserInfoState as jest.MockedFunction<typeof useAdvertiserInfoState>;

jest.mock('lodash', () => ({
...jest.requireActual('lodash'),
Expand Down Expand Up @@ -52,6 +54,12 @@ jest.mock('@deriv/api-v2', () => ({
},
}));

jest.mock('@/providers/AdvertiserInfoStateProvider', () => ({
useAdvertiserInfoState: jest.fn().mockReturnValue({
setHasCreatedAdvertiser: jest.fn(),
}),
}));

describe('NicknameModal', () => {
it('should render title and description correctly', () => {
render(<NicknameModal isModalOpen setIsModalOpen={jest.fn()} />, { wrapper });
Expand All @@ -75,9 +83,10 @@ describe('NicknameModal', () => {
expect(mockedMutate).toHaveBeenCalledWith({
name: 'Nahida',
});
expect(mockUseAdvertiserInfoState().setHasCreatedAdvertiser).toBeCalledWith(true);
});
it('should invoke reset when there is an error from creating advertiser', async () => {
mockedUseAdvertiserCreate.mockImplementationOnce(() => ({
(mockedUseAdvertiserCreate as jest.Mock).mockImplementationOnce(() => ({
error: undefined,
isError: true,
isSuccess: false,
Expand All @@ -92,7 +101,7 @@ describe('NicknameModal', () => {
expect(mockedReset).toBeCalled();
});
it('should close the modal when Cancel button is clicked', async () => {
mockedUseAdvertiserCreate.mockImplementationOnce(() => ({
(mockedUseAdvertiserCreate as jest.Mock).mockImplementationOnce(() => ({
error: undefined,
isError: false,
isSuccess: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Text } from '@deriv-com/ui';

type TOnlineStatusLabelProps = {
isOnline?: boolean;
lastOnlineTime: number;
lastOnlineTime?: number;
};

const OnlineStatusLabel = ({ isOnline = false, lastOnlineTime }: TOnlineStatusLabelProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Text } from '@deriv-com/ui';
import { ProfileDailyLimit } from '../ProfileDailyLimit';
import './ProfileBalance.scss';

const ProfileBalance = ({ advertiserStats }: { advertiserStats: TAdvertiserStats }) => {
const ProfileBalance = ({ advertiserStats }: { advertiserStats: DeepPartial<TAdvertiserStats> }) => {
const { data: activeAccount } = useActiveAccount();
const { isDesktop } = useDevice();
const [shouldShowAvailableBalanceModal, setShouldShowAvailableBalanceModal] = useState(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const wrapper = ({ children }: { children: JSX.Element }) => (
let mockAdvertiserStatsProp = {
advertiserStats: {
balance_available: 50000,
daily_buy_limit: 500,
daily_sell_limit: 500,
daily_buy_limit: '500',
daily_sell_limit: '500',
dailyAvailableBuyLimit: 10,
dailyAvailableSellLimit: 10,
fullName: 'Jane Doe',
isEligibleForLimitUpgrade: false,
name: 'Jane',
show_name: 0,
should_show_name: false,
},
};
const mockUseActiveAccount = {
Expand Down Expand Up @@ -58,8 +58,8 @@ describe('ProfileBalance', () => {
mockAdvertiserStatsProp = {
advertiserStats: {
...mockAdvertiserStatsProp.advertiserStats,
daily_buy_limit: 500,
daily_sell_limit: 2000,
daily_buy_limit: '500',
daily_sell_limit: '2000',
dailyAvailableBuyLimit: 100,
dailyAvailableSellLimit: 600,
},
Expand Down
Loading

0 comments on commit 230f15c

Please sign in to comment.