feat(notifications): use pagination on the backend layer
The NotificationsPage uses pagination by the backend to avoid large datasets to be loaded into frontend. Signed-off-by: Marek Libra <marek.libra@gmail.com>
This commit is contained in:
@@ -40,9 +40,17 @@ export type UpdateNotificationsOptions = {
|
||||
saved?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type GetNotificationsResponse = {
|
||||
notifications: Notification[];
|
||||
totalCount: number;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface NotificationsApi {
|
||||
getNotifications(options?: GetNotificationsOptions): Promise<Notification[]>;
|
||||
getNotifications(
|
||||
options?: GetNotificationsOptions,
|
||||
): Promise<GetNotificationsResponse>;
|
||||
|
||||
getNotification(id: string): Promise<Notification>;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
import {
|
||||
GetNotificationsOptions,
|
||||
GetNotificationsResponse,
|
||||
NotificationsApi,
|
||||
UpdateNotificationsOptions,
|
||||
} from './NotificationsApi';
|
||||
@@ -40,7 +41,7 @@ export class NotificationsClient implements NotificationsApi {
|
||||
|
||||
async getNotifications(
|
||||
options?: GetNotificationsOptions,
|
||||
): Promise<Notification[]> {
|
||||
): Promise<GetNotificationsResponse> {
|
||||
const queryString = new URLSearchParams();
|
||||
if (options?.limit !== undefined) {
|
||||
queryString.append('limit', options.limit.toString(10));
|
||||
@@ -59,7 +60,7 @@ export class NotificationsClient implements NotificationsApi {
|
||||
}
|
||||
const urlSegment = `?${queryString}`;
|
||||
|
||||
return await this.request<Notification[]>(urlSegment);
|
||||
return await this.request<GetNotificationsResponse>(urlSegment);
|
||||
}
|
||||
|
||||
async getNotification(id: string): Promise<Notification> {
|
||||
|
||||
@@ -35,13 +35,18 @@ export const NotificationsPage = () => {
|
||||
const [refresh, setRefresh] = React.useState(false);
|
||||
const { lastSignal } = useSignal('notifications');
|
||||
const [unreadOnly, setUnreadOnly] = React.useState<boolean | undefined>(true);
|
||||
const [pageNumber, setPageNumber] = React.useState(0);
|
||||
const [pageSize, setPageSize] = React.useState(5);
|
||||
const [containsText, setContainsText] = React.useState<string>();
|
||||
const [createdAfter, setCreatedAfter] = React.useState<string>('lastWeek');
|
||||
|
||||
const { error, value, retry, loading } = useNotificationsApi(
|
||||
// TODO: add pagination and other filters
|
||||
api => {
|
||||
const options: GetNotificationsOptions = { search: containsText };
|
||||
const options: GetNotificationsOptions = {
|
||||
search: containsText,
|
||||
limit: pageSize,
|
||||
offset: pageNumber * pageSize,
|
||||
};
|
||||
if (unreadOnly !== undefined) {
|
||||
options.read = !unreadOnly;
|
||||
}
|
||||
@@ -53,7 +58,7 @@ export const NotificationsPage = () => {
|
||||
|
||||
return api.getNotifications(options);
|
||||
},
|
||||
[containsText, unreadOnly, createdAfter],
|
||||
[containsText, unreadOnly, createdAfter, pageNumber, pageSize],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -94,9 +99,14 @@ export const NotificationsPage = () => {
|
||||
<Grid item xs={10}>
|
||||
<NotificationsTable
|
||||
isLoading={loading}
|
||||
notifications={value}
|
||||
notifications={value?.notifications}
|
||||
onUpdate={onUpdate}
|
||||
setContainsText={setContainsText}
|
||||
onPageChange={setPageNumber}
|
||||
onRowsPerPageChange={setPageSize}
|
||||
page={pageNumber}
|
||||
pageSize={pageSize}
|
||||
totalCount={value?.totalCount}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -15,25 +15,34 @@
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
// @ts-ignore
|
||||
import RelativeTime from 'react-relative-time';
|
||||
import { Box, IconButton, Tooltip, Typography } from '@material-ui/core';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
|
||||
import { notificationsApiRef } from '../../api';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import MarkAsUnreadIcon from '@material-ui/icons/Markunread';
|
||||
import MarkAsReadIcon from '@material-ui/icons/CheckCircle';
|
||||
|
||||
// @ts-ignore
|
||||
import RelativeTime from 'react-relative-time';
|
||||
import { Link, Table, TableColumn } from '@backstage/core-components';
|
||||
import {
|
||||
Link,
|
||||
Table,
|
||||
TableProps,
|
||||
TableColumn,
|
||||
} from '@backstage/core-components';
|
||||
|
||||
const ThrottleDelayMs = 1000;
|
||||
|
||||
/** @public */
|
||||
export type NotificationsTableProps = {
|
||||
export type NotificationsTableProps = Pick<
|
||||
TableProps,
|
||||
'onPageChange' | 'onRowsPerPageChange' | 'page' | 'totalCount'
|
||||
> & {
|
||||
isLoading?: boolean;
|
||||
notifications?: Notification[];
|
||||
onUpdate: () => void;
|
||||
setContainsText: (search: string) => void;
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
@@ -42,6 +51,11 @@ export const NotificationsTable = ({
|
||||
notifications = [],
|
||||
onUpdate,
|
||||
setContainsText,
|
||||
onPageChange,
|
||||
onRowsPerPageChange,
|
||||
page,
|
||||
pageSize,
|
||||
totalCount,
|
||||
}: NotificationsTableProps) => {
|
||||
const notificationsApi = useApi(notificationsApiRef);
|
||||
|
||||
@@ -156,16 +170,15 @@ export const NotificationsTable = ({
|
||||
isLoading={isLoading}
|
||||
options={{
|
||||
search: true,
|
||||
// TODO: add pagination
|
||||
// paging: true,
|
||||
// pageSize,
|
||||
paging: true,
|
||||
pageSize,
|
||||
header: false,
|
||||
sorting: false,
|
||||
}}
|
||||
// onPageChange={setPageNumber}
|
||||
// onRowsPerPageChange={setPageSize}
|
||||
// page={offset}
|
||||
// totalCount={value?.totalCount}
|
||||
onPageChange={onPageChange}
|
||||
onRowsPerPageChange={onRowsPerPageChange}
|
||||
page={page}
|
||||
totalCount={totalCount}
|
||||
onSearchChange={throttledContainsTextHandler}
|
||||
data={notifications}
|
||||
columns={compactColumns}
|
||||
|
||||
Reference in New Issue
Block a user