diff --git a/pkg/webui/console/components/notifications/templates/api-key-created.js b/pkg/webui/console/components/notifications/templates/api-key-created.js index ea8a86e0bb..557a9e14c0 100644 --- a/pkg/webui/console/components/notifications/templates/api-key-created.js +++ b/pkg/webui/console/components/notifications/templates/api-key-created.js @@ -89,6 +89,7 @@ const ApiKeyCreated = ({ notificationData }) => { action: { Link: msg => ( { + const newArray = [...array] + const end = Math.min(start + values.length, totalCount) + for (let i = start; i < end; i++) { + newArray[i] = values[i - start] + } + return newArray +} + +const indicesToPage = (startIndex, stopIndex, limit) => { + const startPage = Math.floor(startIndex / limit) + 1 + const stopPage = Math.floor(stopIndex / limit) + 1 + return [startPage, stopPage] +} + +const pageToIndices = (page, limit) => { + const startIndex = (page - 1) * limit + const stopIndex = page * limit - 1 + return [startIndex, stopIndex] +} + const m = defineMessages({ seeArchived: 'See archived messages', seeAll: 'See all messages', }) -const pageSize = 6 -const DEFAULT_PAGE = 1 - const Notifications = () => { + const listRef = useRef(null) const userId = useSelector(selectUserId) const dispatch = useDispatch() const [selectedNotification, setSelectedNotification] = useState(undefined) const [hasNextPage, setHasNextPage] = useState(true) - const [isNextPageLoading, setIsNextPageLoading] = useState(false) - const [items, setItems] = useState([]) - const [page, setPage] = useState(DEFAULT_PAGE) + const [items, setItems] = useState(undefined) const [showArchived, setShowArchived] = useQueryState('archived', 'false') - const [isArchiving, setIsArchiving] = useState(false) + const [totalCount, setTotalCount] = useState(0) + const [fetching, setFetching] = useState(false) const loadNextPage = useCallback( - async filter => { - setIsNextPageLoading(true) + async (startIndex, stopIndex) => { + if (fetching) return + setFetching(true) + + // Determine filters based on whether archived notifications should be shown. const filters = showArchived === 'true' ? ['NOTIFICATION_STATUS_ARCHIVED'] - : typeof filter === 'string' - ? filter : ['NOTIFICATION_STATUS_UNSEEN', 'NOTIFICATION_STATUS_SEEN'] + + // Calculate the number of items to fetch. + const limit = Math.max(BATCH_SIZE, stopIndex - startIndex + 1) + const [startPage, stopPage] = indicesToPage(startIndex, stopIndex, limit) + + // Fetch new notifications with a maximum of 1000 items. const newItems = await dispatch( attachPromise( getNotifications(userId, filters, { - limit: pageSize, - page, + limit: Math.min((stopPage - startPage + 1) * BATCH_SIZE, 1000), + page: startPage, }), ), ) - setPage(page => page + 1) - setItems(items => [...items, ...newItems.notifications]) - setHasNextPage(items.length < newItems.totalCount) - setIsNextPageLoading(false) + + // Update the total count of notifications. + setTotalCount(newItems.totalCount) + + // Integrate the new items into the existing list. + const updatedItems = fillIntoArray( + items, + pageToIndices(startPage, limit)[0], + newItems.notifications, + newItems.totalCount, + ) + setItems(updatedItems) + + // Set the first notification as selected if none is currently selected. + if (!selectedNotification) { + setSelectedNotification(updatedItems[0]) + } + + // Determine if there are more pages to load. + setHasNextPage(updatedItems.length < newItems.totalCount) + setFetching(false) }, - [dispatch, userId, page, setPage, showArchived, items], + [fetching, showArchived, dispatch, userId, items, selectedNotification], ) - const handleShowArchived = useCallback(async () => { - setPage(DEFAULT_PAGE) + const handleShowArchived = useCallback(() => { + // Toggle the showArchived state. setShowArchived(showArchived === 'false' ? 'true' : 'false') + + // Reset items and selected notification. setItems([]) - setHasNextPage(true) setSelectedNotification(undefined) - }, [setShowArchived, showArchived, setPage]) + + // Load the first page of archived notifications. + loadNextPage(0, BATCH_SIZE) + }, [loadNextPage, setShowArchived, showArchived]) + + const handleArchive = useCallback( + async (_, id) => { + // Determine the filter to apply based on the showArchived state. + const updateFilter = + showArchived === 'true' ? 'NOTIFICATION_STATUS_SEEN' : 'NOTIFICATION_STATUS_ARCHIVED' + + // Update the status of the notification. + await dispatch(attachPromise(updateNotificationStatus(userId, [id], updateFilter))) + + // Find the index of the archived notification. + const index = items.findIndex(item => item.id === id) + + // Reload notifications starting from the archived one. + await loadNextPage( + index, + index + BATCH_SIZE > items.length - 1 ? items.length - 1 : index + BATCH_SIZE, + ) + + // Update the selected notification to the one above the archived one. + setSelectedNotification(items[Math.max(1, index - 1)]) + + // Reset the list cache if available so that old items are discarded. + if (listRef.current && listRef.current.resetloadMoreItemsCache) { + listRef.current.resetloadMoreItemsCache() + } + }, + [showArchived, dispatch, userId, items, loadNextPage], + ) + + // Load the first page of notifications when the component mounts. + useEffect(() => { + loadNextPage(0, BATCH_SIZE) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + if (!items) { + return ( +