From 0b26fce8ed599f0b43478059474e4127092e36c3 Mon Sep 17 00:00:00 2001 From: Wojciech Brygola Date: Thu, 14 Nov 2024 09:08:19 +0000 Subject: [PATCH] Revert "Reapply "farrah/integrate buildship" (#399) (#403)" This reverts commit 60d72b6c08479dc801a47450fbe7843c7e803f35. --- .../AppHeader/Notifications/Notifications.tsx | 16 +++- .../DailyLimitModal/DailyLimitModal.tsx | 4 +- .../__tests__/DailyLimitModal.spec.tsx | 2 +- .../__tests__/useNotificationList.spec.ts | 39 +++++++++ .../api/notification/useNotificationList.ts | 84 +++++++++++-------- .../api/notification/useNotificationUpdate.ts | 32 ++++--- 6 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 src/hooks/api/notification/__tests__/useNotificationList.spec.ts diff --git a/src/components/AppHeader/Notifications/Notifications.tsx b/src/components/AppHeader/Notifications/Notifications.tsx index d86f8293..72c32b01 100644 --- a/src/components/AppHeader/Notifications/Notifications.tsx +++ b/src/components/AppHeader/Notifications/Notifications.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { getNotification, MY_PROFILE_URL } from '@/constants'; import { api } from '@/hooks'; @@ -10,8 +10,9 @@ const Notifications = () => { const [isOpen, setIsOpen] = useState(false); const { localize } = useTranslations(); const { isDesktop, isMobile } = useDevice(); - const { data: notifications } = api.notification.useGetList(); - const { readAllNotifications } = api.notification.useUpdate(); + const { data: activeAccountData } = api.account.useActiveAccount(); + const { data: notifications, subscribe } = api.notification.useGetList(); + const { mutate: updateNotification } = api.notification.useUpdate(); const history = useHistory(); const modifiedNotifications = useMemo(() => { @@ -37,6 +38,13 @@ const Notifications = () => { }); }, [notifications]); + useEffect(() => { + if (activeAccountData) { + subscribe({}); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [activeAccountData]); + return ( <> { { - readAllNotifications(); + updateNotification({ ids: [], notifications_update_status: 'remove' }); }} componentConfig={{ clearButtonText: localize('Clear all'), diff --git a/src/components/Modals/DailyLimitModal/DailyLimitModal.tsx b/src/components/Modals/DailyLimitModal/DailyLimitModal.tsx index faa57c6f..4fcc6ca1 100644 --- a/src/components/Modals/DailyLimitModal/DailyLimitModal.tsx +++ b/src/components/Modals/DailyLimitModal/DailyLimitModal.tsx @@ -12,7 +12,7 @@ type TDailyLimitModalProps = { const DailyLimitModal = ({ currency, isModalOpen, onRequestClose }: TDailyLimitModalProps) => { const { data, error, isPending: isLoading, isSuccess, mutate } = api.advertiser.useUpdate(); - const { readAllNotifications } = api.notification.useUpdate(); + const { mutate: updateNotification } = api.notification.useUpdate(); const { daily_buy_limit: dailyBuyLimit, daily_sell_limit: dailySellLimit } = data ?? {}; const { isDesktop } = useDevice(); const textSize = isDesktop ? 'sm' : 'md'; @@ -40,7 +40,7 @@ const DailyLimitModal = ({ currency, isModalOpen, onRequestClose }: TDailyLimitM mutate({ upgrade_limits: 1 }); //TODO: Remove this once implemented in BE - readAllNotifications(); + updateNotification({ ids: [], notifications_update_status: 'remove' }); }} size='lg' textSize={textSize} diff --git a/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx b/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx index da96bcbd..ca802f9a 100644 --- a/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx +++ b/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx @@ -22,7 +22,7 @@ jest.mock('@/hooks', () => ({ useUpdate: jest.fn(() => mockUseAdvertiserUpdate), }, notification: { - useUpdate: jest.fn(() => ({ readAllNotifications: jest.fn() })), + useUpdate: jest.fn(() => ({ mutate: jest.fn() })), }, }, })); diff --git a/src/hooks/api/notification/__tests__/useNotificationList.spec.ts b/src/hooks/api/notification/__tests__/useNotificationList.spec.ts new file mode 100644 index 00000000..bf2a772e --- /dev/null +++ b/src/hooks/api/notification/__tests__/useNotificationList.spec.ts @@ -0,0 +1,39 @@ +import { renderHook } from '@testing-library/react'; +import useNotificationList from '../useNotificationList'; + +const mockNotificationListData = { + data: { + notifications_list: { + messages: [ + { + category: 'see', + id: 1, + message_key: 'p2p-limit-upgrade-available', + }, + { + category: 'see', + id: 2, + message_key: 'poi-verified', + }, + ], + }, + }, +}; + +jest.mock('@deriv-com/api-hooks', () => ({ + useSubscribe: jest.fn(() => mockNotificationListData), +})); + +describe('useNotificationList', () => { + it('should return the list of p2p-related notifications', () => { + const { result } = renderHook(() => useNotificationList()); + + expect(result.current.data).toEqual([ + { + category: 'see', + id: 1, + message_key: 'p2p-limit-upgrade-available', + }, + ]); + }); +}); diff --git a/src/hooks/api/notification/useNotificationList.ts b/src/hooks/api/notification/useNotificationList.ts index a48a1cdb..09a7aad1 100644 --- a/src/hooks/api/notification/useNotificationList.ts +++ b/src/hooks/api/notification/useNotificationList.ts @@ -1,4 +1,5 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; +import { useSubscribe } from '@deriv-com/api-hooks'; type TNotificationLinks = { href: string; @@ -7,54 +8,69 @@ type TNotificationLinks = { type TNotification = { category: string; id: number; - links?: TNotificationLinks[]; + links: TNotificationLinks[]; message_key: string; payload: string; read: boolean; removed: boolean; }; +const handleData = (incomingMessages: TNotification[], prevMessages: TNotification[]) => { + if (!incomingMessages) return prevMessages; + + let notifications = prevMessages; + for (let updateIdx = 0; updateIdx < incomingMessages.length; updateIdx++) { + const update = incomingMessages[updateIdx]; + + const existingMessageIndex = notifications.findIndex((message: TNotification) => message.id === update.id); + const existingMessage = notifications[existingMessageIndex]; + + if (existingMessage) { + if (update.removed) + notifications = notifications.filter((message: TNotification) => message.id !== update.id); + else notifications[existingMessageIndex] = { ...existingMessage, ...update }; + } else notifications.unshift(update); + } + + notifications.sort((a: TNotification, b: TNotification) => b.id - a.id); + + return [...notifications]; +}; + /** * Hook that returns the list of notifications. * + * @example const { data: notifications } = useNotificationList(); */ const useNotificationList = () => { - const [notifications, setNotifications] = useState([]); + // @ts-expect-error Type undefined. This endpoint will be added to api-hooks. + const { data, ...rest } = useSubscribe('notifications_list'); + const [messages, setMessages] = useState([]); + + const modified_data = useMemo(() => { + if (!messages) return undefined; + + // TODO: Remove this filter once all the notifications are implemented + const notifications = messages.filter((notification: { message_key: string }) => + ['p2p-limit-upgrade-available'].includes(notification.message_key) + ); + + return notifications; + }, [messages]); useEffect(() => { - const fetchNotifications = async () => { - try { - const response = await fetch('https://fs191x.buildship.run/v4/notification/list', { - headers: { - 'Content-Type': 'application/json', - token: localStorage.getItem('authToken') || '', - }, - method: 'GET', - }); - - if (response.ok) { - const data = await response.json(); - - // TODO: Remove this filter once all the notifications are implemented - const messages = data.filter((notification: { message_key: string }) => - ['p2p-limit-upgrade-available'].includes(notification.message_key) - ); - setNotifications(messages); - } else { - // eslint-disable-next-line no-console - console.error('Failed to fetch notifications'); - } - } catch (error) { - // eslint-disable-next-line no-console - console.error(error); - } - }; - - fetchNotifications(); - }, []); + // @ts-expect-error Type undefined. + if (data?.notifications_list) { + setMessages(prevMessages => { + // @ts-expect-error Type undefined. + return handleData(data.notifications_list.messages, prevMessages); + }); + } + }, [data]); return { - data: notifications || [], + data: modified_data || [], + ...rest, }; }; diff --git a/src/hooks/api/notification/useNotificationUpdate.ts b/src/hooks/api/notification/useNotificationUpdate.ts index e7694c89..8eeb47f5 100644 --- a/src/hooks/api/notification/useNotificationUpdate.ts +++ b/src/hooks/api/notification/useNotificationUpdate.ts @@ -1,26 +1,24 @@ +import { useMutation } from '@deriv-com/api-hooks'; + /** * Hook that updates the status of a notification. The notification can be removed or marked as read or unread. * + * @example + * const { data, mutate } = useNotificationUpdate(); + * mutate({ notifications_update_status: 'read', ids: [notification_id]}); + * mutate({ notifications_update_status: 'unread', ids: [notification_id]}); + * mutate({ notifications_update_status: 'remove', ids: []}); */ const useNotificationUpdate = () => { - const readAllNotifications = async () => { - try { - await fetch('https://fs191x.buildship.run/v4/notification/read', { - body: JSON.stringify({ - authorize: localStorage.getItem('authToken'), - }), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }); - } catch (error) { - // eslint-disable-next-line no-console - console.error(error); - } - }; + const { data, ...rest } = useMutation({ + // @ts-expect-error Type undefined. This endpoint will be added to api-hooks. + name: 'notifications_update_status', + }); + return { - readAllNotifications, + // @ts-expect-error Type undefined. + data: data?.notifications_update_status, + ...rest, }; };