From 97590d88af27245ec80b0b5c3d16697e4a98892c Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Sat, 11 Mar 2023 19:30:18 +0600 Subject: [PATCH] notifications: updating lastSeen when list is opened --- src/api/notifications/settings.ts | 12 +++++ src/api/notifications/types.ts | 4 ++ .../NotificationComment/styles.module.scss | 2 +- .../notifications/NotificationList/index.tsx | 8 +++- .../notifications/useNotificationSettings.ts | 17 ++++++- .../useNotificationSettingsRequest.ts | 48 ++++++++++++++++++- src/utils/providers/NotificationProvider.tsx | 1 + 7 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/api/notifications/settings.ts b/src/api/notifications/settings.ts index 8f670dac..6947bd81 100644 --- a/src/api/notifications/settings.ts +++ b/src/api/notifications/settings.ts @@ -4,6 +4,8 @@ import { api, cleanResult } from '~/utils/api'; import { ApiGetNotificationSettingsResponse, ApiGetNotificationsResponse, + ApiUpdateNotificationSettingsResponse, + ApiUpdateNotificationSettingsRequest, } from './types'; export const apiGetNotificationSettings = () => @@ -15,3 +17,13 @@ export const apiGetNotifications = () => api .get(API.NOTIFICATIONS.LIST) .then(cleanResult); + +export const apiUpdateNotificationSettings = ( + settings: ApiUpdateNotificationSettingsRequest, +) => + api + .post( + API.NOTIFICATIONS.SETTINGS, + settings, + ) + .then(cleanResult); diff --git a/src/api/notifications/types.ts b/src/api/notifications/types.ts index b242fc76..d998a1ec 100644 --- a/src/api/notifications/types.ts +++ b/src/api/notifications/types.ts @@ -7,7 +7,11 @@ export interface ApiGetNotificationSettingsResponse { last_seen?: string | null; last_date?: string | null; } +export type ApiUpdateNotificationSettingsResponse = + ApiGetNotificationSettingsResponse; +export type ApiUpdateNotificationSettingsRequest = + Partial; export interface ApiGetNotificationsResponse { items?: NotificationItem[]; } diff --git a/src/components/notifications/NotificationComment/styles.module.scss b/src/components/notifications/NotificationComment/styles.module.scss index 6d57a501..e12c2ecd 100644 --- a/src/components/notifications/NotificationComment/styles.module.scss +++ b/src/components/notifications/NotificationComment/styles.module.scss @@ -11,7 +11,7 @@ padding: $gap/2 $gap/2 $gap/4 $gap/2; min-height: calc(1.3em * 3 + $gap); display: grid; - grid-template-columns: 40px auto; + grid-template-columns: 32px auto; column-gap: 0; } diff --git a/src/containers/notifications/NotificationList/index.tsx b/src/containers/notifications/NotificationList/index.tsx index f2860246..c1759845 100644 --- a/src/containers/notifications/NotificationList/index.tsx +++ b/src/containers/notifications/NotificationList/index.tsx @@ -1,8 +1,9 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect } from 'react'; import { LoaderCircle } from '~/components/input/LoaderCircle'; import { NotificationComment } from '~/components/notifications/NotificationComment'; import { useNotificationsList } from '~/hooks/notifications/useNotificationsList'; +import { useNotifications } from '~/utils/providers/NotificationProvider'; import styles from './styles.module.scss'; @@ -10,6 +11,11 @@ interface NotificationListProps {} const NotificationList: FC = () => { const { isLoading, items } = useNotificationsList(); + const { markAsRead } = useNotifications(); + + useEffect(() => { + return () => markAsRead(); + }, []); if (isLoading) { return ; diff --git a/src/hooks/notifications/useNotificationSettings.ts b/src/hooks/notifications/useNotificationSettings.ts index 8e2f4f80..a4c6c354 100644 --- a/src/hooks/notifications/useNotificationSettings.ts +++ b/src/hooks/notifications/useNotificationSettings.ts @@ -1,4 +1,6 @@ -import { isAfter, isValid, parse, parseISO } from 'date-fns'; +import { useCallback } from 'react'; + +import { isAfter, isBefore, isEqual, isValid, parse, parseISO } from 'date-fns'; import { useAuth } from '../auth/useAuth'; @@ -13,6 +15,7 @@ export const useNotificationSettings = () => { lastSeen, lastDate, isLoading: isLoadingSettings, + update, } = useNotificationSettingsRequest(); const enabled = !isLoadingSettings && !settingsError && settingsEnabled; @@ -20,9 +23,21 @@ export const useNotificationSettings = () => { const hasNew = enabled && !!lastDate && (!lastSeen || isAfter(lastDate, lastSeen)); + const markAsRead = useCallback(() => { + if ( + !lastDate || + (lastSeen && (isEqual(lastSeen, lastDate) || isAfter(lastSeen, lastDate))) + ) { + return; + } + + update({ last_seen: lastDate.toISOString() }); + }, [update, lastDate, lastSeen]); + return { enabled, hasNew, available: isUser, + markAsRead, }; }; diff --git a/src/hooks/notifications/useNotificationSettingsRequest.ts b/src/hooks/notifications/useNotificationSettingsRequest.ts index 32e9de9a..6e54c08d 100644 --- a/src/hooks/notifications/useNotificationSettingsRequest.ts +++ b/src/hooks/notifications/useNotificationSettingsRequest.ts @@ -1,25 +1,68 @@ +import { useCallback, useState } from 'react'; + import { isValid, parseISO } from 'date-fns'; import useSWR from 'swr'; -import { apiGetNotificationSettings } from '~/api/notifications/settings'; +import { + apiGetNotificationSettings, + apiUpdateNotificationSettings, +} from '~/api/notifications/settings'; +import { ApiUpdateNotificationSettingsRequest } from '~/api/notifications/types'; import { API } from '~/constants/api'; +import { getErrorMessage } from '~/utils/errors/getErrorMessage'; +import { showErrorToast } from '~/utils/errors/showToast'; import { useAuth } from '../auth/useAuth'; const refreshInterval = 60e3; // 1min export const useNotificationSettingsRequest = () => { + const [isUpdating, setIsUpdating] = useState(false); + const [updateError, setUpdateError] = useState(''); + const { isUser } = useAuth(); const { data, isValidating: isLoading, error, + mutate, } = useSWR( isUser ? API.NOTIFICATIONS.SETTINGS : null, async () => apiGetNotificationSettings(), { refreshInterval }, ); + const update = useCallback( + async (settings: ApiUpdateNotificationSettingsRequest) => { + if (!data) { + return; + } + + try { + setIsUpdating(true); + setUpdateError(''); + + mutate({ ...data, ...settings }, { revalidate: false }); + + const result = await apiUpdateNotificationSettings(settings); + + mutate(result, { revalidate: false }); + } catch (error) { + mutate(data, { revalidate: false }); + + const message = getErrorMessage(error); + if (message) { + setUpdateError(message); + } + + showErrorToast(error); + } finally { + setIsUpdating(true); + } + }, + [data, mutate], + ); + return { isLoading, error, @@ -32,5 +75,8 @@ export const useNotificationSettingsRequest = () => { ? parseISO(data?.last_date) : undefined, enabled: !!data?.enabled && (data.flow || data.comments), + update, + updateError, + isUpdating, }; }; diff --git a/src/utils/providers/NotificationProvider.tsx b/src/utils/providers/NotificationProvider.tsx index 6f3d3c29..c1231183 100644 --- a/src/utils/providers/NotificationProvider.tsx +++ b/src/utils/providers/NotificationProvider.tsx @@ -10,6 +10,7 @@ const defaultValue = { available: false, enabled: false, hasNew: false, + markAsRead: () => {}, }; const NotificationContext = createContext(defaultValue);