Skip to content

Commit

Permalink
feat: Add indicators status of rate changes in UserAds [#183873108]
Browse files Browse the repository at this point in the history
  • Loading branch information
trzmaxim committed Dec 12, 2022
1 parent dce81bf commit a0441b3
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 18 deletions.
11 changes: 8 additions & 3 deletions web/src/components/shared/UserAds/SetRate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Box } from 'web/src/components/ui/Box';
import { Text } from 'web/src/components/ui/Text';
import { useAdapterContext } from 'web/src/components/shared/Adapter';
import { SelectCustom } from 'web/src/components/SelectCustom/SelectCustom';
import { useSetUserAdRate } from 'web/src/hooks/mutations/useSetUserAdRate';
import { SetUserAdRateData, useSetUserAdRate } from 'web/src/hooks/mutations/useSetUserAdRate';
import { AdvertType } from 'web/src/modules/p2p/types';
import { useAppContext } from 'web/src/components/app/AppContext';
import { useStateWithDeps } from 'web/src/hooks/useStateWithDeps';
Expand All @@ -18,11 +18,12 @@ interface Props {
type: AdvertType;
cryptoCurrency: string;
currencyList: string[];
onChanged?: ((data: SetUserAdRateData) => void) | undefined;
}

const getOptionValue = (option: string | undefined) => option || '';

export const SetRate: FC<Props> = ({ type, cryptoCurrency, currencyList }) => {
export const SetRate: FC<Props> = ({ type, cryptoCurrency, currencyList, onChanged }) => {
const { t } = useAdapterContext();
const { lang } = useAppContext();
const [setUserAdRate, { status }] = useSetUserAdRate(lang);
Expand Down Expand Up @@ -67,7 +68,7 @@ export const SetRate: FC<Props> = ({ type, cryptoCurrency, currencyList }) => {
return;
}

await setUserAdRate({
const data = await setUserAdRate({
type,
cryptocurrency: cryptoCurrency,
currency: selectedCurrency,
Expand All @@ -80,6 +81,10 @@ export const SetRate: FC<Props> = ({ type, cryptoCurrency, currencyList }) => {
setFixedValue('');

handleClose();

if (data && onChanged) {
onChanged(data);
}
} catch (error) {
if (error instanceof FetchError) {
if (error.code === 481 && error.payload.code === 'AdsUpdatedToOften') {
Expand Down
33 changes: 32 additions & 1 deletion web/src/components/shared/UserAds/UserAds.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useMemo, useState } from 'react';
import { FC, useCallback, useMemo, useState } from 'react';
import { useAppContext } from 'web/src/components/app/AppContext';
import { Container } from 'web/src/components/ui/Container';
import { Box } from 'web/src/components/ui/Box';
Expand All @@ -13,6 +13,8 @@ import { Breadcrumbs, BreadcrumbsItem } from 'web/src/components/ui/Breadcrumbs'
import { Modal, ModalBody, ModalHeader } from 'web/src/components/ui/Modal';
import { UserAdvertSource } from 'web/src/modules/p2p/types';
import { StorageKeys } from 'web/src/helpers/storageKeys';
import { SetUserAdRateData } from 'web/src/hooks/mutations/useSetUserAdRate';
import { useDebouncedCallback } from 'use-debounce';
import { UserAdsList } from './UserAdsList';
import {
UserAdsFilter,
Expand All @@ -27,6 +29,9 @@ import { SelectCryptoCurrency } from './SelectCryptoCurrency';
import { UserAdsAlerts } from './UserAdsAlerts';
import * as s from './UserAds.css';

const CLEAR_SUCCESSFUL_RATE_CHANGES_WAIT = 30 * 1000; // 30 seconds
const CLEAR_FAILURE_RATE_CHANGES_WAIT = 60 * 1000; // 60 seconds

export const UserAds: FC = () => {
const { t, Link, history } = useAdapterContext<
{},
Expand All @@ -43,6 +48,7 @@ export const UserAds: FC = () => {
...lastFilter,
};
});
const [rateChanged, setRateChanged] = useState<SetUserAdRateData>();
const [showCreatedAdModal, setShowCreatedAdModal] = useState(
history.location.state?.createdAdId !== undefined,
);
Expand Down Expand Up @@ -74,6 +80,19 @@ export const UserAds: FC = () => {
[filteredAds],
);

const clearSuccessfulRateChanges = useDebouncedCallback(
useCallback(() => {
setRateChanged((prev) => ({ updatedAds: [], notUpdatedAds: prev?.notUpdatedAds ?? [] }));
}, []),
CLEAR_SUCCESSFUL_RATE_CHANGES_WAIT,
);
const clearRateChanges = useDebouncedCallback(
useCallback(() => {
setRateChanged(undefined);
}, []),
CLEAR_FAILURE_RATE_CHANGES_WAIT,
);

const handleEnableTradeToggle = () => {
updateTradeStatus({ [filterParams.cryptocurrency]: !isTradeEnabled });
};
Expand Down Expand Up @@ -106,11 +125,21 @@ export const UserAds: FC = () => {
history.push('/p2p/adverts/create');
};

const handleRateChanged = (result: SetUserAdRateData) => {
clearSuccessfulRateChanges.cancel();
clearRateChanges.cancel();
setRateChanged(result);

clearSuccessfulRateChanges();
clearRateChanges();
};

const ads = (
<Box mt={{ mobile: '4x', tablet: isAdsEmpty ? '6x' : '10x' }}>
<UserAdsList
data={filteredAds}
params={filterParams}
rateChanged={rateChanged}
isLoading={!filteredAds}
isRefreshing={isValidating}
onRefresh={handleRefresh}
Expand Down Expand Up @@ -188,6 +217,7 @@ export const UserAds: FC = () => {
type={filterParams.type}
cryptoCurrency={filterParams.cryptocurrency}
currencyList={currencyList}
onChanged={handleRateChanged}
/>
</Box>

Expand Down Expand Up @@ -248,6 +278,7 @@ export const UserAds: FC = () => {
type={filterParams.type}
cryptoCurrency={filterParams.cryptocurrency}
currencyList={currencyList}
onChanged={handleRateChanged}
/>
<Button as={Link} to="/p2p/adverts/create">
{t('Create advert')}
Expand Down
24 changes: 23 additions & 1 deletion web/src/components/shared/UserAds/UserAdsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,38 @@ import { UserAdvert } from 'web/src/modules/p2p/types';
import { UserAdsListItem } from 'web/src/components/shared/UserAds/UserAdsListItem';
import { Spinner } from 'web/src/components/ui/Spinner';
import RefreshIcon from 'web/src/assets/svg/RefreshIcon.svg';
import { SetUserAdRateData } from 'web/src/hooks/mutations/useSetUserAdRate';
import { UserAdsFilterParams } from './UserAdsFilter';
import { UserAdsTradingStatusSwitch } from './UserAdsTradingStatusSwitch';

interface Props {
data?: UserAdvert[] | undefined;
params: UserAdsFilterParams;
rateChanged?: SetUserAdRateData | undefined;
isLoading?: boolean;
isRefreshing?: boolean;
onRefresh?: () => void;
}

const getRateChangedStatus = (
{ updatedAds, notUpdatedAds }: SetUserAdRateData,
id: number,
): 'success' | 'failure' | undefined => {
if (updatedAds.includes(id)) {
return 'success';
}

if (notUpdatedAds.includes(id)) {
return 'failure';
}

return undefined;
};

export const UserAdsList: FC<Props> = ({
data,
params,
rateChanged,
isLoading = false,
isRefreshing = false,
onRefresh,
Expand Down Expand Up @@ -96,7 +114,11 @@ export const UserAdsList: FC<Props> = ({
{data && data.length > 0 && (
<AdsTableBody>
{data.map((ad) => (
<UserAdsListItem key={ad.id} ad={ad} />
<UserAdsListItem
key={ad.id}
ad={ad}
rateChangedStatus={rateChanged ? getRateChangedStatus(rateChanged, ad.id) : undefined}
/>
))}
</AdsTableBody>
)}
Expand Down
20 changes: 19 additions & 1 deletion web/src/components/shared/UserAds/UserAdsListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import * as s from './UserAdsListItem.css';

interface Props {
ad: UserAdvert;
rateChangedStatus?: 'success' | 'failure' | undefined;
}

export const UserAdsListItem: FC<Props> = ({ ad }) => {
export const UserAdsListItem: FC<Props> = ({ ad, rateChangedStatus }) => {
const { lang, isMobileDevice } = useAppContext();
const { t, history } = useAdapterContext();
const [updateUserAd, { status: updateUserAdState }] = useUpdateUserAd(lang);
Expand Down Expand Up @@ -68,6 +69,21 @@ export const UserAdsListItem: FC<Props> = ({ ad }) => {
}
};

let rateChanged;
if (rateChangedStatus === 'success') {
rateChanged = (
<Text variant="caption" color="success" fontWeight="strong">
{t('Change accepted')}
</Text>
);
} else if (rateChangedStatus === 'failure') {
rateChanged = (
<Text variant="caption" color="danger" fontWeight="strong">
{t('Change rejected')}
</Text>
);
}

const controls = (
<Box className={s.statusSwitch}>
<SwitchField
Expand Down Expand Up @@ -134,6 +150,7 @@ export const UserAdsListItem: FC<Props> = ({ ad }) => {
</Text>
<Text variant="label" textAlign="right">
<P2PFiatFormat money={ad.rate} cryptoCurrency={ad.cryptoCurrency} />
{rateChanged}
</Text>
</Box>
<Box display="flex" justifyContent="space-between" px="4x">
Expand Down Expand Up @@ -174,6 +191,7 @@ export const UserAdsListItem: FC<Props> = ({ ad }) => {
<Box pr="4x" textOverflow="ellipsis">
<P2PFiatFormat money={ad.rate} cryptoCurrency={ad.cryptoCurrency} />
{ad.ratePercent ? ` (${ad.ratePercent}%)` : null}
{rateChanged}
</Box>
</AdsTableColumn>
<AdsTableColumn size="small">
Expand Down
22 changes: 10 additions & 12 deletions web/src/hooks/mutations/useSetUserAdRate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useSWRConfig } from 'swr';
import useMutation from 'use-mutation';
import { p2pUrl } from 'web/src/api/config';
import { useHandleFetchError } from 'web/src/components/app/AppContext';
import { fetchJson, FetchError } from 'web/src/helpers/fetch';
import { FetchError, fetchMutation } from 'web/src/helpers/fetch';
import { Language } from 'web/src/types';

interface SetUserAdRateInput {
Expand All @@ -13,24 +13,22 @@ interface SetUserAdRateInput {
ratePercent: string | null | undefined;
}

const setUserAdRate = async (input: SetUserAdRateInput): Promise<void> => {
const response = await fetchJson(`${p2pUrl()}/dsa/changerates`, {
export interface SetUserAdRateData {
updatedAds: ReadonlyArray<number>;
notUpdatedAds: ReadonlyArray<number>;
}

const setUserAdRate = async (input: SetUserAdRateInput): Promise<SetUserAdRateData> =>
fetchMutation(`${p2pUrl()}/dsa/changerates`, {
method: 'PUT',
body: JSON.stringify(input),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
credentials: 'include',
body: input,
});

return response;
};

export const useSetUserAdRate = (lang: Language) => {
const { mutate } = useSWRConfig();
const handleFetchError = useHandleFetchError();

return useMutation<SetUserAdRateInput>(setUserAdRate, {
return useMutation<SetUserAdRateInput, SetUserAdRateData>(setUserAdRate, {
throwOnFailure: true,
onSuccess: () => {
mutate(`${p2pUrl()}/dsa/all?lang=${lang}`);
Expand Down

0 comments on commit a0441b3

Please sign in to comment.