-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Task/WG-422 poll notifications (#320)
* Allow user to set which DS site to use during local development * Update example * Improve comment * Add hook/context for notifcations * Use notification when adding feature file * Add polling * Improve polling and invalidate cache * Rework notfication for deleting project due to export const NotificationProvider/NotificationContext * Fix refreshing of features * Use notication hook and unify config options * Remove unused imports * Fix comment * Fix repeated notifications * Remove exclamation point at end of message
- Loading branch information
1 parent
6d52a02
commit 771a7ef
Showing
14 changed files
with
235 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import React from 'react'; | ||
import { notification } from 'antd'; | ||
import type { ArgsProps } from 'antd/es/notification'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
|
||
// takes antd's ArgProps or just a description and optional message | ||
type NotificationConfig = ArgsProps | { description: string; message?: string }; | ||
|
||
type NotificationAPI = { | ||
success: (config: NotificationConfig) => void; | ||
error: (config: NotificationConfig) => void; | ||
info: (config: NotificationConfig) => void; | ||
warning: (config: NotificationConfig) => void; | ||
open: (config: NotificationConfig) => void; | ||
}; | ||
|
||
// Create context with default value and proper typing | ||
export const NotificationContext = React.createContext<NotificationAPI>({ | ||
success: () => {}, | ||
error: () => {}, | ||
info: () => {}, | ||
warning: () => {}, | ||
open: () => {}, | ||
}); | ||
|
||
export const NotificationProvider: React.FC<{ | ||
children: React.ReactNode; | ||
}> = ({ children }) => { | ||
const [api, contextHolder] = notification.useNotification(); | ||
|
||
const notificationApi: NotificationAPI = React.useMemo(() => { | ||
const defaultProps: Partial<ArgsProps> = { | ||
placement: 'bottomLeft', | ||
closable: false, | ||
key: uuidv4(), | ||
/* antd reuses notifications when you don’t specify a key; so previous notification might briefly reappear without key*/ | ||
}; | ||
|
||
return { | ||
success: (config) => | ||
api.success({ message: 'Success', ...defaultProps, ...config }), | ||
error: (config) => | ||
api.error({ message: 'Error', ...defaultProps, ...config }), | ||
info: (config) => | ||
api.info({ message: 'Info', ...defaultProps, ...config }), | ||
warning: (config) => | ||
api.warning({ message: 'Warning', ...defaultProps, ...config }), | ||
open: (config) => | ||
api.open({ message: 'Unknown', ...defaultProps, ...config }), | ||
}; | ||
}, [api]); | ||
|
||
return ( | ||
<NotificationContext.Provider value={notificationApi}> | ||
{contextHolder} | ||
{children} | ||
</NotificationContext.Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { useNotification } from './useNotification'; | ||
export { useGeoapiNotificationsPolling } from './useGeoapiNotificationsPolling'; |
90 changes: 90 additions & 0 deletions
90
react/src/hooks/notifications/useGeoapiNotificationsPolling.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { useEffect } from 'react'; | ||
import { useQueryClient } from '@tanstack/react-query'; | ||
import { Notification } from '@hazmapper/types'; | ||
import { useGet } from '@hazmapper/requests'; | ||
import { | ||
KEY_USE_FEATURES, | ||
KEY_USE_POINT_CLOUDS, | ||
KEY_USE_TILE_SERVERS, | ||
} from '@hazmapper/hooks'; | ||
import { useNotification } from './useNotification'; | ||
|
||
const POLLING_INTERVAL = 5000; // 5 seconds | ||
|
||
export const useGeoapiNotificationsPolling = () => { | ||
const queryClient = useQueryClient(); | ||
const notification = useNotification(); | ||
|
||
const getStartDate = () => { | ||
// Get the current timestamp minus the polling interval | ||
const now = new Date(); | ||
const then = new Date(now.getTime() - POLLING_INTERVAL); | ||
return then.toISOString(); | ||
}; | ||
|
||
const { data: recentNotifications } = useGet<Notification[]>({ | ||
endpoint: '/notifications/', | ||
key: ['notifications'], | ||
params: { | ||
startDate: getStartDate(), | ||
}, | ||
options: { | ||
refetchInterval: POLLING_INTERVAL, | ||
refetchIntervalInBackground: true, | ||
retry: 3, | ||
}, | ||
}); | ||
|
||
useEffect(() => { | ||
if (recentNotifications?.length) { | ||
const hasSuccessNotification = recentNotifications.some( | ||
(note) => note.status === 'success' | ||
); | ||
|
||
recentNotifications.forEach((note) => { | ||
notification.open({ | ||
type: note.status, | ||
message: `${note.status[0].toUpperCase()}${note.status.slice(1)}`, | ||
description: note.message, | ||
}); | ||
}); | ||
|
||
if (hasSuccessNotification) { | ||
// we assume that if some action was updated we need to refresh things so | ||
// we invalidate relevant queries. | ||
// Note we are doing a force refetch as makes unique KEY_USE_FEATURES work | ||
|
||
Promise.all( | ||
[KEY_USE_FEATURES, KEY_USE_POINT_CLOUDS, KEY_USE_TILE_SERVERS].map( | ||
(key) => | ||
queryClient | ||
.invalidateQueries({ | ||
queryKey: [key], | ||
refetchType: 'all', | ||
}) | ||
.then(() => { | ||
const queries = queryClient.getQueriesData({ | ||
queryKey: [key], | ||
}); | ||
// Force refetch active queries for each key | ||
return Promise.all( | ||
queries.map(([queryKey]) => | ||
queryClient.refetchQueries({ | ||
queryKey, | ||
type: 'active', | ||
exact: true, | ||
}) | ||
) | ||
); | ||
}) | ||
) | ||
); | ||
} | ||
} | ||
}, [recentNotifications, notification, queryClient]); | ||
|
||
return { | ||
recentNotifications, | ||
isPolling: true, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from 'react'; | ||
import { NotificationContext } from '@hazmapper/context/NotificationProvider'; | ||
|
||
/** | ||
* Custom hook to access the notification context for client-side notifications (i.e. toasts) via antd's notifications. | ||
*/ | ||
export const useNotification = () => { | ||
const context = React.useContext(NotificationContext); | ||
if (!context) { | ||
throw new Error( | ||
'useNotification must be used within a NotificationProvider' | ||
); | ||
} | ||
return context; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.