@backstage/plugin-notifications 0.0.1 → 0.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @backstage/plugin-notifications
2
2
 
3
+ ## 0.1.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 758f2a4: The Notifications frontend has been redesigned towards list view with condensed row details. The 'done' attribute has been removed to keep the Notifications aligned with the idea of a messaging system instead of a task manager.
8
+
9
+ ### Patch Changes
10
+
11
+ - 5d9c5ba: The Notifications can be newly filtered based on the Created Date.
12
+ - Updated dependencies
13
+ - @backstage/errors@1.2.4-next.0
14
+ - @backstage/theme@0.5.2-next.0
15
+ - @backstage/core-components@0.14.1-next.0
16
+ - @backstage/plugin-notifications-common@0.0.2-next.0
17
+ - @backstage/core-plugin-api@1.9.1-next.0
18
+ - @backstage/types@1.1.1
19
+ - @backstage/plugin-signals-react@0.0.2-next.0
20
+
3
21
  ## 0.0.1
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,134 @@
1
+ import React, { useEffect } from 'react';
2
+ import { ResponseErrorPanel, PageWithHeader, Content } from '@backstage/core-components';
3
+ import { Grid, Typography, Divider, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core';
4
+ import { useSignal } from '@backstage/plugin-signals-react';
5
+ import { useNotificationsApi, NotificationsTable } from '../index.esm.js';
6
+ import '@backstage/core-plugin-api';
7
+ import '@backstage/errors';
8
+ import 'react-use/lib/useAsyncRetry';
9
+ import '@material-ui/icons/Notifications';
10
+ import 'lodash/throttle';
11
+ import '@material-ui/icons/Markunread';
12
+ import '@material-ui/icons/CheckCircle';
13
+ import 'react-relative-time';
14
+
15
+ const CreatedAfterOptions = {
16
+ last24h: {
17
+ label: "Last 24h",
18
+ getDate: () => new Date(Date.now() - 24 * 3600 * 1e3)
19
+ },
20
+ lastWeek: {
21
+ label: "Last week",
22
+ getDate: () => new Date(Date.now() - 7 * 24 * 3600 * 1e3)
23
+ },
24
+ all: {
25
+ label: "Any time",
26
+ getDate: () => /* @__PURE__ */ new Date(0)
27
+ }
28
+ };
29
+ const NotificationsFilters = ({
30
+ // sorting,
31
+ // setSorting,
32
+ unreadOnly,
33
+ onUnreadOnlyChanged,
34
+ createdAfter,
35
+ onCreatedAfterChanged
36
+ }) => {
37
+ const handleOnCreatedAfterChanged = (event) => {
38
+ onCreatedAfterChanged(event.target.value);
39
+ };
40
+ const handleOnUnreadOnlyChanged = (event) => {
41
+ let value = void 0;
42
+ if (event.target.value === "unread")
43
+ value = true;
44
+ if (event.target.value === "read")
45
+ value = false;
46
+ onUnreadOnlyChanged(value);
47
+ };
48
+ let unreadOnlyValue = "all";
49
+ if (unreadOnly)
50
+ unreadOnlyValue = "unread";
51
+ if (unreadOnly === false)
52
+ unreadOnlyValue = "read";
53
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, "Filters"), /* @__PURE__ */ React.createElement(Divider, { variant: "fullWidth" })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(FormControl, { fullWidth: true, variant: "outlined", size: "small" }, /* @__PURE__ */ React.createElement(InputLabel, { id: "notifications-filter-view" }, "View"), /* @__PURE__ */ React.createElement(
54
+ Select,
55
+ {
56
+ labelId: "notifications-filter-view",
57
+ label: "View",
58
+ value: unreadOnlyValue,
59
+ onChange: handleOnUnreadOnlyChanged
60
+ },
61
+ /* @__PURE__ */ React.createElement(MenuItem, { value: "unread" }, "New only"),
62
+ /* @__PURE__ */ React.createElement(MenuItem, { value: "read" }, "Marked as read"),
63
+ /* @__PURE__ */ React.createElement(MenuItem, { value: "all" }, "All")
64
+ ))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(FormControl, { fullWidth: true, variant: "outlined", size: "small" }, /* @__PURE__ */ React.createElement(InputLabel, { id: "notifications-filter-view" }, "Created after"), /* @__PURE__ */ React.createElement(
65
+ Select,
66
+ {
67
+ label: "Created after",
68
+ placeholder: "Notifications since",
69
+ value: createdAfter,
70
+ onChange: handleOnCreatedAfterChanged
71
+ },
72
+ Object.keys(CreatedAfterOptions).map((key) => /* @__PURE__ */ React.createElement(MenuItem, { value: key, key }, CreatedAfterOptions[key].label))
73
+ )))));
74
+ };
75
+
76
+ const NotificationsPage = () => {
77
+ const [refresh, setRefresh] = React.useState(false);
78
+ const { lastSignal } = useSignal("notifications");
79
+ const [unreadOnly, setUnreadOnly] = React.useState(true);
80
+ const [containsText, setContainsText] = React.useState();
81
+ const [createdAfter, setCreatedAfter] = React.useState("lastWeek");
82
+ const { error, value, retry, loading } = useNotificationsApi(
83
+ // TODO: add pagination and other filters
84
+ (api) => {
85
+ const options = { search: containsText };
86
+ if (unreadOnly !== void 0) {
87
+ options.read = !unreadOnly;
88
+ }
89
+ const createdAfterDate = CreatedAfterOptions[createdAfter].getDate();
90
+ if (createdAfterDate.valueOf() > 0) {
91
+ options.createdAfter = createdAfterDate;
92
+ }
93
+ return api.getNotifications(options);
94
+ },
95
+ [containsText, unreadOnly, createdAfter]
96
+ );
97
+ useEffect(() => {
98
+ if (refresh) {
99
+ retry();
100
+ setRefresh(false);
101
+ }
102
+ }, [refresh, setRefresh, retry]);
103
+ useEffect(() => {
104
+ if (lastSignal && lastSignal.action) {
105
+ setRefresh(true);
106
+ }
107
+ }, [lastSignal]);
108
+ const onUpdate = () => {
109
+ setRefresh(true);
110
+ };
111
+ if (error) {
112
+ return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error });
113
+ }
114
+ return /* @__PURE__ */ React.createElement(PageWithHeader, { title: "Notifications", themeId: "tool" }, /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 2 }, /* @__PURE__ */ React.createElement(
115
+ NotificationsFilters,
116
+ {
117
+ unreadOnly,
118
+ onUnreadOnlyChanged: setUnreadOnly,
119
+ createdAfter,
120
+ onCreatedAfterChanged: setCreatedAfter
121
+ }
122
+ )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 10 }, /* @__PURE__ */ React.createElement(
123
+ NotificationsTable,
124
+ {
125
+ isLoading: loading,
126
+ notifications: value,
127
+ onUpdate,
128
+ setContainsText
129
+ }
130
+ )))));
131
+ };
132
+
133
+ export { NotificationsPage };
134
+ //# sourceMappingURL=index-3798ac09.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-3798ac09.esm.js","sources":["../../src/components/NotificationsFilters/NotificationsFilters.tsx","../../src/components/NotificationsPage/NotificationsPage.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\n\nimport {\n Divider,\n FormControl,\n Grid,\n InputLabel,\n MenuItem,\n Select,\n Typography,\n} from '@material-ui/core';\n\nexport type NotificationsFiltersProps = {\n unreadOnly?: boolean;\n onUnreadOnlyChanged: (checked: boolean | undefined) => void;\n createdAfter?: string;\n onCreatedAfterChanged: (value: string) => void;\n\n // sorting?: {\n // orderBy: GetNotificationsOrderByEnum;\n // orderByDirec: GetNotificationsOrderByDirecEnum;\n // };\n // setSorting: ({\n // orderBy,\n // orderByDirec,\n // }: {\n // orderBy: GetNotificationsOrderByEnum;\n // orderByDirec: GetNotificationsOrderByDirecEnum;\n // }) => void;\n};\n\nexport const CreatedAfterOptions: {\n [key: string]: { label: string; getDate: () => Date };\n} = {\n last24h: {\n label: 'Last 24h',\n getDate: () => new Date(Date.now() - 24 * 3600 * 1000),\n },\n lastWeek: {\n label: 'Last week',\n getDate: () => new Date(Date.now() - 7 * 24 * 3600 * 1000),\n },\n all: {\n label: 'Any time',\n getDate: () => new Date(0),\n },\n};\n\n// export const SortByOptions: {\n// [key: string]: {\n// label: string;\n// orderBy: GetNotificationsOrderByEnum;\n// orderByDirec: GetNotificationsOrderByDirecEnum;\n// };\n// } = {\n// newest: {\n// label: 'Newest on top',\n// orderBy: GetNotificationsOrderByEnum.Created,\n// orderByDirec: GetNotificationsOrderByDirecEnum.Asc,\n// },\n// oldest: {\n// label: 'Oldest on top',\n// orderBy: GetNotificationsOrderByEnum.Created,\n// orderByDirec: GetNotificationsOrderByDirecEnum.Desc,\n// },\n// topic: {\n// label: 'Topic',\n// orderBy: GetNotificationsOrderByEnum.Topic,\n// orderByDirec: GetNotificationsOrderByDirecEnum.Asc,\n// },\n// origin: {\n// label: 'Origin',\n// orderBy: GetNotificationsOrderByEnum.Origin,\n// orderByDirec: GetNotificationsOrderByDirecEnum.Asc,\n// },\n// };\n\n// TODO: Implement sorting on server (to work with pagination)\n// const getSortBy = (sorting: NotificationsFiltersProps['sorting']): string => {\n// if (\n// sorting?.orderBy === GetNotificationsOrderByEnum.Created &&\n// sorting.orderByDirec === GetNotificationsOrderByDirecEnum.Desc\n// ) {\n// return 'oldest';\n// }\n// if (sorting?.orderBy === GetNotificationsOrderByEnum.Topic) {\n// return 'topic';\n// }\n// if (sorting?.orderBy === GetNotificationsOrderByEnum.Origin) {\n// return 'origin';\n// }\n\n// return 'newest';\n// };\n\nexport const NotificationsFilters = ({\n // sorting,\n // setSorting,\n unreadOnly,\n onUnreadOnlyChanged,\n createdAfter,\n onCreatedAfterChanged,\n}: NotificationsFiltersProps) => {\n // const sortBy = getSortBy(sorting);\n\n const handleOnCreatedAfterChanged = (\n event: React.ChangeEvent<{ name?: string; value: unknown }>,\n ) => {\n onCreatedAfterChanged(event.target.value as string);\n };\n\n const handleOnUnreadOnlyChanged = (\n event: React.ChangeEvent<{ name?: string; value: unknown }>,\n ) => {\n let value = undefined;\n if (event.target.value === 'unread') value = true;\n if (event.target.value === 'read') value = false;\n onUnreadOnlyChanged(value);\n };\n\n // const handleOnSortByChanged = (\n // event: React.ChangeEvent<{ name?: string; value: unknown }>,\n // ) => {\n // const idx = (event.target.value as string) || 'newest';\n // const option = SortByOptions[idx];\n // setSorting({\n // orderBy: option.orderBy,\n // orderByDirec: option.orderByDirec,\n // });\n // };\n\n let unreadOnlyValue = 'all';\n if (unreadOnly) unreadOnlyValue = 'unread';\n if (unreadOnly === false) unreadOnlyValue = 'read';\n\n return (\n <>\n <Grid container>\n <Grid item xs={12}>\n <Typography variant=\"h6\">Filters</Typography>\n <Divider variant=\"fullWidth\" />\n </Grid>\n <Grid item xs={12}>\n <FormControl fullWidth variant=\"outlined\" size=\"small\">\n <InputLabel id=\"notifications-filter-view\">View</InputLabel>\n <Select\n labelId=\"notifications-filter-view\"\n label=\"View\"\n value={unreadOnlyValue}\n onChange={handleOnUnreadOnlyChanged}\n >\n <MenuItem value=\"unread\">New only</MenuItem>\n <MenuItem value=\"read\">Marked as read</MenuItem>\n <MenuItem value=\"all\">All</MenuItem>\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12}>\n <FormControl fullWidth variant=\"outlined\" size=\"small\">\n <InputLabel id=\"notifications-filter-view\">\n Created after\n </InputLabel>\n\n <Select\n label=\"Created after\"\n placeholder=\"Notifications since\"\n value={createdAfter}\n onChange={handleOnCreatedAfterChanged}\n >\n {Object.keys(CreatedAfterOptions).map((key: string) => (\n <MenuItem value={key} key={key}>\n {CreatedAfterOptions[key].label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Grid>\n\n {/*\n <Grid item xs={12}>\n <FormControl fullWidth variant=\"outlined\" size=\"small\">\n <InputLabel id=\"notifications-filter-sort\">Sort by</InputLabel>\n\n <Select\n label=\"Sort by\"\n placeholder=\"Field to sort by\"\n value={sortBy}\n onChange={handleOnSortByChanged}\n >\n {Object.keys(SortByOptions).map((key: string) => (\n <MenuItem value={key} key={key}>\n {SortByOptions[key].label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Grid> */}\n </Grid>\n </>\n );\n};\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect } from 'react';\nimport {\n Content,\n PageWithHeader,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { Grid } from '@material-ui/core';\nimport { useSignal } from '@backstage/plugin-signals-react';\n\nimport { NotificationsTable } from '../NotificationsTable';\nimport { useNotificationsApi } from '../../hooks';\nimport {\n CreatedAfterOptions,\n NotificationsFilters,\n} from '../NotificationsFilters';\nimport { GetNotificationsOptions } from '../../api';\n\nexport const NotificationsPage = () => {\n const [refresh, setRefresh] = React.useState(false);\n const { lastSignal } = useSignal('notifications');\n const [unreadOnly, setUnreadOnly] = React.useState<boolean | undefined>(true);\n const [containsText, setContainsText] = React.useState<string>();\n const [createdAfter, setCreatedAfter] = React.useState<string>('lastWeek');\n\n const { error, value, retry, loading } = useNotificationsApi(\n // TODO: add pagination and other filters\n api => {\n const options: GetNotificationsOptions = { search: containsText };\n if (unreadOnly !== undefined) {\n options.read = !unreadOnly;\n }\n\n const createdAfterDate = CreatedAfterOptions[createdAfter].getDate();\n if (createdAfterDate.valueOf() > 0) {\n options.createdAfter = createdAfterDate;\n }\n\n return api.getNotifications(options);\n },\n [containsText, unreadOnly, createdAfter],\n );\n\n useEffect(() => {\n if (refresh) {\n retry();\n setRefresh(false);\n }\n }, [refresh, setRefresh, retry]);\n\n useEffect(() => {\n if (lastSignal && lastSignal.action) {\n setRefresh(true);\n }\n }, [lastSignal]);\n\n const onUpdate = () => {\n setRefresh(true);\n };\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n return (\n <PageWithHeader title=\"Notifications\" themeId=\"tool\">\n <Content>\n <Grid container>\n <Grid item xs={2}>\n <NotificationsFilters\n unreadOnly={unreadOnly}\n onUnreadOnlyChanged={setUnreadOnly}\n createdAfter={createdAfter}\n onCreatedAfterChanged={setCreatedAfter}\n // setSorting={setSorting}\n // sorting={sorting}\n />\n </Grid>\n <Grid item xs={10}>\n <NotificationsTable\n isLoading={loading}\n notifications={value}\n onUpdate={onUpdate}\n setContainsText={setContainsText}\n />\n </Grid>\n </Grid>\n </Content>\n </PageWithHeader>\n );\n};\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import React__default from 'react';
4
4
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
5
5
  import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
6
- import { NotificationType, Notification as Notification$1, NotificationStatus } from '@backstage/plugin-notifications-common';
6
+ import { Notification as Notification$1, NotificationStatus } from '@backstage/plugin-notifications-common';
7
7
 
8
8
  /** @public */
9
9
  declare const notificationsPlugin: _backstage_core_plugin_api.BackstagePlugin<{
@@ -16,15 +16,15 @@ declare const NotificationsPage: () => React.JSX.Element;
16
16
  declare const notificationsApiRef: _backstage_core_plugin_api.ApiRef<NotificationsApi>;
17
17
  /** @public */
18
18
  type GetNotificationsOptions = {
19
- type?: NotificationType;
20
19
  offset?: number;
21
20
  limit?: number;
22
21
  search?: string;
22
+ read?: boolean;
23
+ createdAfter?: Date;
23
24
  };
24
25
  /** @public */
25
26
  type UpdateNotificationsOptions = {
26
27
  ids: string[];
27
- done?: boolean;
28
28
  read?: boolean;
29
29
  saved?: boolean;
30
30
  };
@@ -95,10 +95,13 @@ declare const NotificationsSidebarItem: (props?: {
95
95
  }) => React__default.JSX.Element;
96
96
 
97
97
  /** @public */
98
- declare const NotificationsTable: (props: {
99
- onUpdate: () => void;
100
- type: NotificationType;
98
+ type NotificationsTableProps = {
99
+ isLoading?: boolean;
101
100
  notifications?: Notification$1[];
102
- }) => React__default.JSX.Element;
101
+ onUpdate: () => void;
102
+ setContainsText: (search: string) => void;
103
+ };
104
+ /** @public */
105
+ declare const NotificationsTable: ({ isLoading, notifications, onUpdate, setContainsText, }: NotificationsTableProps) => React__default.JSX.Element;
103
106
 
104
- export { GetNotificationsOptions, NotificationsApi, NotificationsClient, NotificationsPage, NotificationsSidebarItem, NotificationsTable, UpdateNotificationsOptions, notificationsApiRef, notificationsPlugin, useNotificationsApi, useTitleCounter, useWebNotifications };
107
+ export { GetNotificationsOptions, NotificationsApi, NotificationsClient, NotificationsPage, NotificationsSidebarItem, NotificationsTable, NotificationsTableProps, UpdateNotificationsOptions, notificationsApiRef, notificationsPlugin, useNotificationsApi, useTitleCounter, useWebNotifications };
package/dist/index.esm.js CHANGED
@@ -1,19 +1,15 @@
1
1
  import { createRouteRef, createApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, useApi, useRouteRef } from '@backstage/core-plugin-api';
2
2
  import { ResponseError } from '@backstage/errors';
3
3
  import useAsyncRetry from 'react-use/lib/useAsyncRetry';
4
- import React, { useState, useEffect, useCallback } from 'react';
5
- import { SidebarItem } from '@backstage/core-components';
4
+ import React, { useState, useEffect, useCallback, useMemo } from 'react';
5
+ import { SidebarItem, Link, Table } from '@backstage/core-components';
6
6
  import NotificationsIcon from '@material-ui/icons/Notifications';
7
7
  import { useSignal } from '@backstage/plugin-signals-react';
8
- import { makeStyles, Table, TableHead, TableRow, TableCell, Button, TableBody, Typography, Box, Tooltip, IconButton } from '@material-ui/core';
9
- import { useNavigate } from 'react-router-dom';
10
- import Checkbox from '@material-ui/core/Checkbox';
11
- import Check from '@material-ui/icons/Check';
12
- import Bookmark from '@material-ui/icons/Bookmark';
13
- import Inbox from '@material-ui/icons/Inbox';
14
- import CloseIcon from '@material-ui/icons/Close';
8
+ import throttle from 'lodash/throttle';
9
+ import { Box, Typography, Tooltip, IconButton } from '@material-ui/core';
10
+ import MarkAsUnreadIcon from '@material-ui/icons/Markunread';
11
+ import MarkAsReadIcon from '@material-ui/icons/CheckCircle';
15
12
  import RelativeTime from 'react-relative-time';
16
- import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
17
13
 
18
14
  const rootRouteRef = createRouteRef({
19
15
  id: "notifications"
@@ -38,9 +34,6 @@ class NotificationsClient {
38
34
  }
39
35
  async getNotifications(options) {
40
36
  const queryString = new URLSearchParams();
41
- if (options == null ? void 0 : options.type) {
42
- queryString.append("type", options.type);
43
- }
44
37
  if ((options == null ? void 0 : options.limit) !== void 0) {
45
38
  queryString.append("limit", options.limit.toString(10));
46
39
  }
@@ -50,6 +43,12 @@ class NotificationsClient {
50
43
  if (options == null ? void 0 : options.search) {
51
44
  queryString.append("search", options.search);
52
45
  }
46
+ if ((options == null ? void 0 : options.read) !== void 0) {
47
+ queryString.append("read", options.read ? "true" : "false");
48
+ }
49
+ if ((options == null ? void 0 : options.createdAfter) !== void 0) {
50
+ queryString.append("created_after", options.createdAfter.toISOString());
51
+ }
53
52
  const urlSegment = `?${queryString}`;
54
53
  return await this.request(urlSegment);
55
54
  }
@@ -93,7 +92,7 @@ const notificationsPlugin = createPlugin({
93
92
  const NotificationsPage = notificationsPlugin.provide(
94
93
  createRoutableExtension({
95
94
  name: "NotificationsPage",
96
- component: () => import('./esm/index-1ad83349.esm.js').then((m) => m.NotificationsPage),
95
+ component: () => import('./esm/index-3798ac09.esm.js').then((m) => m.NotificationsPage),
97
96
  mountPoint: rootRouteRef
98
97
  })
99
98
  );
@@ -243,204 +242,94 @@ const NotificationsSidebarItem = (props) => {
243
242
  );
244
243
  };
245
244
 
246
- const useStyles = makeStyles((theme) => ({
247
- table: {
248
- border: `1px solid ${theme.palette.divider}`
249
- },
250
- header: {
251
- borderBottom: `1px solid ${theme.palette.divider}`
252
- },
253
- notificationRow: {
254
- cursor: "pointer",
255
- "&.unread": {
256
- border: "1px solid rgba(255, 255, 255, .3)"
257
- },
258
- "& .hideOnHover": {
259
- display: "initial"
260
- },
261
- "& .showOnHover": {
262
- display: "none"
245
+ const ThrottleDelayMs = 1e3;
246
+ const NotificationsTable = ({
247
+ isLoading,
248
+ notifications = [],
249
+ onUpdate,
250
+ setContainsText
251
+ }) => {
252
+ const notificationsApi = useApi(notificationsApiRef);
253
+ const onSwitchReadStatus = React.useCallback(
254
+ (notification) => {
255
+ notificationsApi.updateNotifications({
256
+ ids: [notification.id],
257
+ read: !notification.read
258
+ }).then(() => onUpdate());
263
259
  },
264
- "&:hover": {
265
- "& .hideOnHover": {
266
- display: "none"
260
+ [notificationsApi, onUpdate]
261
+ );
262
+ const throttledContainsTextHandler = useMemo(
263
+ () => throttle(setContainsText, ThrottleDelayMs),
264
+ [setContainsText]
265
+ );
266
+ const compactColumns = React.useMemo(
267
+ () => [
268
+ {
269
+ customFilterAndSearch: () => true,
270
+ render: (notification) => {
271
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2" }, notification.payload.link ? /* @__PURE__ */ React.createElement(Link, { to: notification.payload.link }, notification.payload.title) : notification.payload.title), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, notification.payload.description), /* @__PURE__ */ React.createElement(Typography, { variant: "caption" }, notification.origin && /* @__PURE__ */ React.createElement(React.Fragment, null, notification.origin, "\xA0\u2022\xA0"), notification.payload.topic && /* @__PURE__ */ React.createElement(React.Fragment, null, notification.payload.topic, "\xA0\u2022\xA0"), notification.created && /* @__PURE__ */ React.createElement(RelativeTime, { value: notification.created }))));
272
+ }
267
273
  },
268
- "& .showOnHover": {
269
- display: "initial"
270
- }
271
- }
272
- },
273
- actionButton: {
274
- padding: "9px"
275
- },
276
- checkBox: {
277
- padding: "0 10px 10px 0"
278
- }
279
- }));
280
- const NotificationsTable = (props) => {
281
- var _a, _b;
282
- const { notifications, type } = props;
283
- const navigate = useNavigate();
284
- const styles = useStyles();
285
- const [selected, setSelected] = useState([]);
286
- const notificationsApi = useApi(notificationsApiRef);
287
- const onCheckBoxClick = (id) => {
288
- const index = selected.indexOf(id);
289
- if (index !== -1) {
290
- setSelected(selected.filter((s) => s !== id));
291
- } else {
292
- setSelected([...selected, id]);
293
- }
294
- };
295
- useEffect(() => {
296
- setSelected([]);
297
- }, [type]);
298
- const isChecked = (id) => {
299
- return selected.indexOf(id) !== -1;
300
- };
301
- const isAllSelected = () => {
302
- return selected.length === (notifications == null ? void 0 : notifications.length) && notifications.length > 0;
303
- };
304
- return /* @__PURE__ */ React.createElement(Table, { size: "small", className: styles.table }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, { colSpan: 3 }, type !== "saved" && !(notifications == null ? void 0 : notifications.length) && "No notifications", type !== "saved" && !!(notifications == null ? void 0 : notifications.length) && /* @__PURE__ */ React.createElement(
305
- Checkbox,
306
- {
307
- size: "small",
308
- style: { paddingLeft: 0 },
309
- checked: isAllSelected(),
310
- onClick: () => {
311
- if (isAllSelected()) {
312
- setSelected([]);
313
- } else {
314
- setSelected(
315
- notifications ? notifications.map((n) => n.id) : []
316
- );
274
+ // {
275
+ // // TODO: additional action links
276
+ // width: '25%',
277
+ // render: (notification: Notification) => {
278
+ // return (
279
+ // notification.payload.link && (
280
+ // <Grid container>
281
+ // {/* TODO: render additionalLinks of different titles */}
282
+ // <Grid item>
283
+ // <Link
284
+ // key={notification.payload.link}
285
+ // to={notification.payload.link}
286
+ // >
287
+ // &nbsp;More info
288
+ // </Link>
289
+ // </Grid>
290
+ // </Grid>
291
+ // )
292
+ // );
293
+ // },
294
+ // },
295
+ {
296
+ // TODO: action for saving notifications
297
+ // actions
298
+ width: "1rem",
299
+ render: (notification) => {
300
+ const markAsReadText = !!notification.read ? "Return among unread" : "Mark as read";
301
+ const IconComponent = !!notification.read ? MarkAsUnreadIcon : MarkAsReadIcon;
302
+ return /* @__PURE__ */ React.createElement(Tooltip, { title: markAsReadText }, /* @__PURE__ */ React.createElement(
303
+ IconButton,
304
+ {
305
+ onClick: () => {
306
+ onSwitchReadStatus(notification);
307
+ }
308
+ },
309
+ /* @__PURE__ */ React.createElement(IconComponent, { "aria-label": markAsReadText })
310
+ ));
317
311
  }
318
312
  }
319
- }
320
- ), type === "saved" && `${(_a = notifications == null ? void 0 : notifications.length) != null ? _a : 0} saved notifications`, selected.length === 0 && !!(notifications == null ? void 0 : notifications.length) && type !== "saved" && "Select all", selected.length > 0 && `${selected.length} selected`, type === "done" && selected.length > 0 && /* @__PURE__ */ React.createElement(
321
- Button,
322
- {
323
- startIcon: /* @__PURE__ */ React.createElement(Inbox, { fontSize: "small" }),
324
- onClick: () => {
325
- notificationsApi.updateNotifications({ ids: selected, done: false }).then(() => props.onUpdate());
326
- setSelected([]);
327
- }
328
- },
329
- "Move to inbox"
330
- ), type === "undone" && selected.length > 0 && /* @__PURE__ */ React.createElement(
331
- Button,
313
+ ],
314
+ [onSwitchReadStatus]
315
+ );
316
+ return /* @__PURE__ */ React.createElement(
317
+ Table,
332
318
  {
333
- startIcon: /* @__PURE__ */ React.createElement(Check, { fontSize: "small" }),
334
- onClick: () => {
335
- notificationsApi.updateNotifications({ ids: selected, done: true }).then(() => props.onUpdate());
336
- setSelected([]);
337
- }
338
- },
339
- "Mark as done"
340
- )))), /* @__PURE__ */ React.createElement(TableBody, null, (_b = props.notifications) == null ? void 0 : _b.map((notification) => {
341
- return /* @__PURE__ */ React.createElement(
342
- TableRow,
343
- {
344
- key: notification.id,
345
- className: `${styles.notificationRow} ${!notification.read ? "unread" : ""}`,
346
- hover: true
319
+ isLoading,
320
+ options: {
321
+ search: true,
322
+ // TODO: add pagination
323
+ // paging: true,
324
+ // pageSize,
325
+ header: false,
326
+ sorting: false
347
327
  },
348
- /* @__PURE__ */ React.createElement(
349
- TableCell,
350
- {
351
- width: "60px",
352
- style: { verticalAlign: "center", paddingRight: "0px" }
353
- },
354
- /* @__PURE__ */ React.createElement(
355
- Checkbox,
356
- {
357
- className: styles.checkBox,
358
- size: "small",
359
- checked: isChecked(notification.id),
360
- onClick: () => onCheckBoxClick(notification.id)
361
- }
362
- )
363
- ),
364
- /* @__PURE__ */ React.createElement(
365
- TableCell,
366
- {
367
- onClick: () => notificationsApi.updateNotifications({ ids: [notification.id], read: true }).then(() => navigate(notification.payload.link)),
368
- style: { paddingLeft: 0 }
369
- },
370
- /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2" }, notification.payload.title),
371
- /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, notification.payload.description)
372
- ),
373
- /* @__PURE__ */ React.createElement(TableCell, { style: { textAlign: "right" } }, /* @__PURE__ */ React.createElement(Box, { className: "hideOnHover" }, /* @__PURE__ */ React.createElement(RelativeTime, { value: notification.created })), /* @__PURE__ */ React.createElement(Box, { className: "showOnHover" }, /* @__PURE__ */ React.createElement(Tooltip, { title: notification.payload.link }, /* @__PURE__ */ React.createElement(
374
- IconButton,
375
- {
376
- className: styles.actionButton,
377
- onClick: () => notificationsApi.updateNotifications({
378
- ids: [notification.id],
379
- read: true
380
- }).then(() => navigate(notification.payload.link))
381
- },
382
- /* @__PURE__ */ React.createElement(ArrowForwardIcon, null)
383
- )), /* @__PURE__ */ React.createElement(
384
- Tooltip,
385
- {
386
- title: notification.read ? "Move to inbox" : "Mark as done"
387
- },
388
- /* @__PURE__ */ React.createElement(
389
- IconButton,
390
- {
391
- className: styles.actionButton,
392
- onClick: () => {
393
- if (notification.read) {
394
- notificationsApi.updateNotifications({
395
- ids: [notification.id],
396
- done: false
397
- }).then(() => {
398
- props.onUpdate();
399
- });
400
- } else {
401
- notificationsApi.updateNotifications({
402
- ids: [notification.id],
403
- done: true
404
- }).then(() => {
405
- props.onUpdate();
406
- });
407
- }
408
- }
409
- },
410
- notification.read ? /* @__PURE__ */ React.createElement(Inbox, { fontSize: "small" }) : /* @__PURE__ */ React.createElement(Check, { fontSize: "small" })
411
- )
412
- ), /* @__PURE__ */ React.createElement(
413
- Tooltip,
414
- {
415
- title: notification.saved ? "Remove from saved" : "Save"
416
- },
417
- /* @__PURE__ */ React.createElement(
418
- IconButton,
419
- {
420
- className: styles.actionButton,
421
- onClick: () => {
422
- if (notification.saved) {
423
- notificationsApi.updateNotifications({
424
- ids: [notification.id],
425
- saved: false
426
- }).then(() => {
427
- props.onUpdate();
428
- });
429
- } else {
430
- notificationsApi.updateNotifications({
431
- ids: [notification.id],
432
- saved: true
433
- }).then(() => {
434
- props.onUpdate();
435
- });
436
- }
437
- }
438
- },
439
- notification.saved ? /* @__PURE__ */ React.createElement(CloseIcon, { fontSize: "small" }) : /* @__PURE__ */ React.createElement(Bookmark, { fontSize: "small" })
440
- )
441
- )))
442
- );
443
- })));
328
+ onSearchChange: throttledContainsTextHandler,
329
+ data: notifications,
330
+ columns: compactColumns
331
+ }
332
+ );
444
333
  };
445
334
 
446
335
  export { NotificationsClient, NotificationsPage, NotificationsSidebarItem, NotificationsTable, notificationsApiRef, notificationsPlugin, useNotificationsApi, useTitleCounter, useWebNotifications };
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/routes.ts","../src/api/NotificationsApi.ts","../src/api/NotificationsClient.ts","../src/plugin.ts","../src/hooks/useNotificationsApi.ts","../src/hooks/useWebNotifications.ts","../src/hooks/useTitleCounter.ts","../src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx","../src/components/NotificationsTable/NotificationsTable.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'notifications',\n});\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createApiRef } from '@backstage/core-plugin-api';\nimport {\n Notification,\n NotificationStatus,\n NotificationType,\n} from '@backstage/plugin-notifications-common';\n\n/** @public */\nexport const notificationsApiRef = createApiRef<NotificationsApi>({\n id: 'plugin.notifications.service',\n});\n\n/** @public */\nexport type GetNotificationsOptions = {\n type?: NotificationType;\n offset?: number;\n limit?: number;\n search?: string;\n};\n\n/** @public */\nexport type UpdateNotificationsOptions = {\n ids: string[];\n done?: boolean;\n read?: boolean;\n saved?: boolean;\n};\n\n/** @public */\nexport interface NotificationsApi {\n getNotifications(options?: GetNotificationsOptions): Promise<Notification[]>;\n\n getNotification(id: string): Promise<Notification>;\n\n getStatus(): Promise<NotificationStatus>;\n\n updateNotifications(\n options: UpdateNotificationsOptions,\n ): Promise<Notification[]>;\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n GetNotificationsOptions,\n NotificationsApi,\n UpdateNotificationsOptions,\n} from './NotificationsApi';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport {\n Notification,\n NotificationStatus,\n} from '@backstage/plugin-notifications-common';\n\n/** @public */\nexport class NotificationsClient implements NotificationsApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n public constructor(options: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getNotifications(\n options?: GetNotificationsOptions,\n ): Promise<Notification[]> {\n const queryString = new URLSearchParams();\n if (options?.type) {\n queryString.append('type', options.type);\n }\n if (options?.limit !== undefined) {\n queryString.append('limit', options.limit.toString(10));\n }\n if (options?.offset !== undefined) {\n queryString.append('offset', options.offset.toString(10));\n }\n if (options?.search) {\n queryString.append('search', options.search);\n }\n\n const urlSegment = `?${queryString}`;\n\n return await this.request<Notification[]>(urlSegment);\n }\n\n async getNotification(id: string): Promise<Notification> {\n return await this.request<Notification>(`${id}`);\n }\n\n async getStatus(): Promise<NotificationStatus> {\n return await this.request<NotificationStatus>('status');\n }\n\n async updateNotifications(\n options: UpdateNotificationsOptions,\n ): Promise<Notification[]> {\n return await this.request<Notification[]>('update', {\n method: 'POST',\n body: JSON.stringify(options),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n private async request<T>(path: string, init?: any): Promise<T> {\n const baseUrl = `${await this.discoveryApi.getBaseUrl('notifications')}/`;\n const url = new URL(path, baseUrl);\n\n const response = await this.fetchApi.fetch(url.toString(), init);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { notificationsApiRef } from './api/NotificationsApi';\nimport { NotificationsClient } from './api';\n\n/** @public */\nexport const notificationsPlugin = createPlugin({\n id: 'notifications',\n routes: {\n root: rootRouteRef,\n },\n apis: [\n createApiFactory({\n api: notificationsApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new NotificationsClient({ discoveryApi, fetchApi }),\n }),\n ],\n});\n\n/** @public */\nexport const NotificationsPage = notificationsPlugin.provide(\n createRoutableExtension({\n name: 'NotificationsPage',\n component: () =>\n import('./components/NotificationsPage').then(m => m.NotificationsPage),\n mountPoint: rootRouteRef,\n }),\n);\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { NotificationsApi, notificationsApiRef } from '../api';\nimport { useApi } from '@backstage/core-plugin-api';\nimport useAsyncRetry from 'react-use/lib/useAsyncRetry';\n\n/** @public */\nexport function useNotificationsApi<T>(\n f: (api: NotificationsApi) => Promise<T>,\n deps: any[] = [],\n) {\n const notificationsApi = useApi(notificationsApiRef);\n\n return useAsyncRetry(async () => {\n return await f(notificationsApi);\n }, deps);\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback, useEffect, useState } from 'react';\n\n/** @public */\nexport function useWebNotifications() {\n const [webNotificationPermission, setWebNotificationPermission] =\n useState('default');\n const [webNotifications, setWebNotifications] = useState<Notification[]>([]);\n\n useEffect(() => {\n if ('Notification' in window && webNotificationPermission === 'default') {\n window.Notification.requestPermission().then(permission => {\n setWebNotificationPermission(permission);\n });\n }\n }, [webNotificationPermission]);\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n webNotifications.forEach(n => n.close());\n setWebNotifications([]);\n }\n });\n\n const sendWebNotification = useCallback(\n (options: { title: string; description: string; link?: string }) => {\n if (webNotificationPermission !== 'granted') {\n return null;\n }\n\n const notification = new Notification(options.title, {\n body: options.description,\n });\n\n notification.onclick = event => {\n event.preventDefault();\n notification.close();\n if (options.link) {\n window.open(options.link, '_blank');\n }\n };\n\n return notification;\n },\n [webNotificationPermission],\n );\n\n return { sendWebNotification };\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback, useEffect, useState } from 'react';\n\n/** @public */\nexport function useTitleCounter() {\n const [title, setTitle] = useState(document.title);\n const [count, setCount] = useState(0);\n\n const getPrefix = (value: number) => {\n return value === 0 ? '' : `(${value}) `;\n };\n\n const cleanTitle = (currentTitle: string) => {\n return currentTitle.replace(/^\\(\\d+\\)\\s/, '');\n };\n\n useEffect(() => {\n document.title = title;\n }, [title]);\n\n useEffect(() => {\n const baseTitle = cleanTitle(title);\n setTitle(`${getPrefix(count)}${baseTitle}`);\n return () => {\n document.title = cleanTitle(title);\n };\n }, [title, count]);\n\n const titleElement = document.querySelector('title');\n if (titleElement) {\n new MutationObserver(() => {\n setTitle(document.title);\n }).observe(titleElement, {\n subtree: true,\n characterData: true,\n childList: true,\n });\n }\n\n const setNotificationCount = useCallback(\n (newCount: number) => setCount(newCount),\n [],\n );\n\n return { setNotificationCount };\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useEffect } from 'react';\nimport { useNotificationsApi } from '../../hooks';\nimport { SidebarItem } from '@backstage/core-components';\nimport NotificationsIcon from '@material-ui/icons/Notifications';\nimport { useApi, useRouteRef } from '@backstage/core-plugin-api';\nimport { rootRouteRef } from '../../routes';\nimport { useSignal } from '@backstage/plugin-signals-react';\nimport { NotificationSignal } from '@backstage/plugin-notifications-common';\nimport { useWebNotifications } from '../../hooks/useWebNotifications';\nimport { useTitleCounter } from '../../hooks/useTitleCounter';\nimport { notificationsApiRef } from '../../api';\n\n/** @public */\nexport const NotificationsSidebarItem = (props?: {\n webNotificationsEnabled?: boolean;\n titleCounterEnabled?: boolean;\n}) => {\n const { webNotificationsEnabled = false, titleCounterEnabled = true } =\n props ?? { webNotificationsEnabled: false, titleCounterEnabled: true };\n\n const { loading, error, value, retry } = useNotificationsApi(api =>\n api.getStatus(),\n );\n const notificationsApi = useApi(notificationsApiRef);\n const [unreadCount, setUnreadCount] = React.useState(0);\n const notificationsRoute = useRouteRef(rootRouteRef);\n // TODO: Do we want to add long polling in case signals are not available\n const { lastSignal } = useSignal<NotificationSignal>('notifications');\n const { sendWebNotification } = useWebNotifications();\n const [refresh, setRefresh] = React.useState(false);\n const { setNotificationCount } = useTitleCounter();\n\n useEffect(() => {\n if (refresh) {\n retry();\n setRefresh(false);\n }\n }, [refresh, retry]);\n\n useEffect(() => {\n const handleWebNotification = (signal: NotificationSignal) => {\n if (!webNotificationsEnabled || signal.action !== 'new_notification') {\n return;\n }\n\n notificationsApi\n .getNotification(signal.notification_id)\n .then(notification => {\n if (!notification) {\n return;\n }\n sendWebNotification({\n title: notification.payload.title,\n description: notification.payload.description ?? '',\n link: notification.payload.link,\n });\n });\n };\n\n if (lastSignal && lastSignal.action) {\n handleWebNotification(lastSignal);\n setRefresh(true);\n }\n }, [\n lastSignal,\n sendWebNotification,\n webNotificationsEnabled,\n notificationsApi,\n ]);\n\n useEffect(() => {\n if (!loading && !error && value) {\n setUnreadCount(value.unread);\n if (titleCounterEnabled) {\n setNotificationCount(value.unread);\n }\n }\n }, [loading, error, value, titleCounterEnabled, setNotificationCount]);\n\n // TODO: Figure out if the count can be added to hasNotifications\n return (\n <SidebarItem\n icon={NotificationsIcon}\n to={notificationsRoute()}\n text=\"Notifications\"\n hasNotifications={!error && !!unreadCount}\n />\n );\n};\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useEffect, useState } from 'react';\nimport {\n Box,\n Button,\n IconButton,\n makeStyles,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Tooltip,\n Typography,\n} from '@material-ui/core';\nimport {\n Notification,\n NotificationType,\n} from '@backstage/plugin-notifications-common';\nimport { useNavigate } from 'react-router-dom';\nimport Checkbox from '@material-ui/core/Checkbox';\nimport Check from '@material-ui/icons/Check';\nimport Bookmark from '@material-ui/icons/Bookmark';\nimport { notificationsApiRef } from '../../api';\nimport { useApi } from '@backstage/core-plugin-api';\nimport Inbox from '@material-ui/icons/Inbox';\nimport CloseIcon from '@material-ui/icons/Close';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport ArrowForwardIcon from '@material-ui/icons/ArrowForward';\n\nconst useStyles = makeStyles(theme => ({\n table: {\n border: `1px solid ${theme.palette.divider}`,\n },\n header: {\n borderBottom: `1px solid ${theme.palette.divider}`,\n },\n\n notificationRow: {\n cursor: 'pointer',\n '&.unread': {\n border: '1px solid rgba(255, 255, 255, .3)',\n },\n '& .hideOnHover': {\n display: 'initial',\n },\n '& .showOnHover': {\n display: 'none',\n },\n '&:hover': {\n '& .hideOnHover': {\n display: 'none',\n },\n '& .showOnHover': {\n display: 'initial',\n },\n },\n },\n actionButton: {\n padding: '9px',\n },\n checkBox: {\n padding: '0 10px 10px 0',\n },\n}));\n\n/** @public */\nexport const NotificationsTable = (props: {\n onUpdate: () => void;\n type: NotificationType;\n notifications?: Notification[];\n}) => {\n const { notifications, type } = props;\n const navigate = useNavigate();\n const styles = useStyles();\n const [selected, setSelected] = useState<string[]>([]);\n const notificationsApi = useApi(notificationsApiRef);\n\n const onCheckBoxClick = (id: string) => {\n const index = selected.indexOf(id);\n if (index !== -1) {\n setSelected(selected.filter(s => s !== id));\n } else {\n setSelected([...selected, id]);\n }\n };\n\n useEffect(() => {\n setSelected([]);\n }, [type]);\n\n const isChecked = (id: string) => {\n return selected.indexOf(id) !== -1;\n };\n\n const isAllSelected = () => {\n return (\n selected.length === notifications?.length && notifications.length > 0\n );\n };\n\n return (\n <Table size=\"small\" className={styles.table}>\n <TableHead>\n <TableRow>\n <TableCell colSpan={3}>\n {type !== 'saved' && !notifications?.length && 'No notifications'}\n {type !== 'saved' && !!notifications?.length && (\n <Checkbox\n size=\"small\"\n style={{ paddingLeft: 0 }}\n checked={isAllSelected()}\n onClick={() => {\n if (isAllSelected()) {\n setSelected([]);\n } else {\n setSelected(\n notifications ? notifications.map(n => n.id) : [],\n );\n }\n }}\n />\n )}\n {type === 'saved' &&\n `${notifications?.length ?? 0} saved notifications`}\n {selected.length === 0 &&\n !!notifications?.length &&\n type !== 'saved' &&\n 'Select all'}\n {selected.length > 0 && `${selected.length} selected`}\n {type === 'done' && selected.length > 0 && (\n <Button\n startIcon={<Inbox fontSize=\"small\" />}\n onClick={() => {\n notificationsApi\n .updateNotifications({ ids: selected, done: false })\n .then(() => props.onUpdate());\n setSelected([]);\n }}\n >\n Move to inbox\n </Button>\n )}\n\n {type === 'undone' && selected.length > 0 && (\n <Button\n startIcon={<Check fontSize=\"small\" />}\n onClick={() => {\n notificationsApi\n .updateNotifications({ ids: selected, done: true })\n .then(() => props.onUpdate());\n setSelected([]);\n }}\n >\n Mark as done\n </Button>\n )}\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {props.notifications?.map(notification => {\n return (\n <TableRow\n key={notification.id}\n className={`${styles.notificationRow} ${\n !notification.read ? 'unread' : ''\n }`}\n hover\n >\n <TableCell\n width=\"60px\"\n style={{ verticalAlign: 'center', paddingRight: '0px' }}\n >\n <Checkbox\n className={styles.checkBox}\n size=\"small\"\n checked={isChecked(notification.id)}\n onClick={() => onCheckBoxClick(notification.id)}\n />\n </TableCell>\n <TableCell\n onClick={() =>\n notificationsApi\n .updateNotifications({ ids: [notification.id], read: true })\n .then(() => navigate(notification.payload.link))\n }\n style={{ paddingLeft: 0 }}\n >\n <Typography variant=\"subtitle2\">\n {notification.payload.title}\n </Typography>\n <Typography variant=\"body2\">\n {notification.payload.description}\n </Typography>\n </TableCell>\n <TableCell style={{ textAlign: 'right' }}>\n <Box className=\"hideOnHover\">\n <RelativeTime value={notification.created} />\n </Box>\n <Box className=\"showOnHover\">\n <Tooltip title={notification.payload.link}>\n <IconButton\n className={styles.actionButton}\n onClick={() =>\n notificationsApi\n .updateNotifications({\n ids: [notification.id],\n read: true,\n })\n .then(() => navigate(notification.payload.link))\n }\n >\n <ArrowForwardIcon />\n </IconButton>\n </Tooltip>\n <Tooltip\n title={notification.read ? 'Move to inbox' : 'Mark as done'}\n >\n <IconButton\n className={styles.actionButton}\n onClick={() => {\n if (notification.read) {\n notificationsApi\n .updateNotifications({\n ids: [notification.id],\n done: false,\n })\n .then(() => {\n props.onUpdate();\n });\n } else {\n notificationsApi\n .updateNotifications({\n ids: [notification.id],\n done: true,\n })\n .then(() => {\n props.onUpdate();\n });\n }\n }}\n >\n {notification.read ? (\n <Inbox fontSize=\"small\" />\n ) : (\n <Check fontSize=\"small\" />\n )}\n </IconButton>\n </Tooltip>\n <Tooltip\n title={notification.saved ? 'Remove from saved' : 'Save'}\n >\n <IconButton\n className={styles.actionButton}\n onClick={() => {\n if (notification.saved) {\n notificationsApi\n .updateNotifications({\n ids: [notification.id],\n saved: false,\n })\n .then(() => {\n props.onUpdate();\n });\n } else {\n notificationsApi\n .updateNotifications({\n ids: [notification.id],\n saved: true,\n })\n .then(() => {\n props.onUpdate();\n });\n }\n }}\n >\n {notification.saved ? (\n <CloseIcon fontSize=\"small\" />\n ) : (\n <Bookmark fontSize=\"small\" />\n )}\n </IconButton>\n </Tooltip>\n </Box>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/routes.ts","../src/api/NotificationsApi.ts","../src/api/NotificationsClient.ts","../src/plugin.ts","../src/hooks/useNotificationsApi.ts","../src/hooks/useWebNotifications.ts","../src/hooks/useTitleCounter.ts","../src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx","../src/components/NotificationsTable/NotificationsTable.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'notifications',\n});\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createApiRef } from '@backstage/core-plugin-api';\nimport {\n Notification,\n NotificationStatus,\n} from '@backstage/plugin-notifications-common';\n\n/** @public */\nexport const notificationsApiRef = createApiRef<NotificationsApi>({\n id: 'plugin.notifications.service',\n});\n\n/** @public */\nexport type GetNotificationsOptions = {\n offset?: number;\n limit?: number;\n search?: string;\n read?: boolean;\n createdAfter?: Date;\n};\n\n/** @public */\nexport type UpdateNotificationsOptions = {\n ids: string[];\n read?: boolean;\n saved?: boolean;\n};\n\n/** @public */\nexport interface NotificationsApi {\n getNotifications(options?: GetNotificationsOptions): Promise<Notification[]>;\n\n getNotification(id: string): Promise<Notification>;\n\n getStatus(): Promise<NotificationStatus>;\n\n updateNotifications(\n options: UpdateNotificationsOptions,\n ): Promise<Notification[]>;\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n GetNotificationsOptions,\n NotificationsApi,\n UpdateNotificationsOptions,\n} from './NotificationsApi';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport {\n Notification,\n NotificationStatus,\n} from '@backstage/plugin-notifications-common';\n\n/** @public */\nexport class NotificationsClient implements NotificationsApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n public constructor(options: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getNotifications(\n options?: GetNotificationsOptions,\n ): Promise<Notification[]> {\n const queryString = new URLSearchParams();\n if (options?.limit !== undefined) {\n queryString.append('limit', options.limit.toString(10));\n }\n if (options?.offset !== undefined) {\n queryString.append('offset', options.offset.toString(10));\n }\n if (options?.search) {\n queryString.append('search', options.search);\n }\n if (options?.read !== undefined) {\n queryString.append('read', options.read ? 'true' : 'false');\n }\n if (options?.createdAfter !== undefined) {\n queryString.append('created_after', options.createdAfter.toISOString());\n }\n const urlSegment = `?${queryString}`;\n\n return await this.request<Notification[]>(urlSegment);\n }\n\n async getNotification(id: string): Promise<Notification> {\n return await this.request<Notification>(`${id}`);\n }\n\n async getStatus(): Promise<NotificationStatus> {\n return await this.request<NotificationStatus>('status');\n }\n\n async updateNotifications(\n options: UpdateNotificationsOptions,\n ): Promise<Notification[]> {\n return await this.request<Notification[]>('update', {\n method: 'POST',\n body: JSON.stringify(options),\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n private async request<T>(path: string, init?: any): Promise<T> {\n const baseUrl = `${await this.discoveryApi.getBaseUrl('notifications')}/`;\n const url = new URL(path, baseUrl);\n\n const response = await this.fetchApi.fetch(url.toString(), init);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { notificationsApiRef } from './api/NotificationsApi';\nimport { NotificationsClient } from './api';\n\n/** @public */\nexport const notificationsPlugin = createPlugin({\n id: 'notifications',\n routes: {\n root: rootRouteRef,\n },\n apis: [\n createApiFactory({\n api: notificationsApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new NotificationsClient({ discoveryApi, fetchApi }),\n }),\n ],\n});\n\n/** @public */\nexport const NotificationsPage = notificationsPlugin.provide(\n createRoutableExtension({\n name: 'NotificationsPage',\n component: () =>\n import('./components/NotificationsPage').then(m => m.NotificationsPage),\n mountPoint: rootRouteRef,\n }),\n);\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { NotificationsApi, notificationsApiRef } from '../api';\nimport { useApi } from '@backstage/core-plugin-api';\nimport useAsyncRetry from 'react-use/lib/useAsyncRetry';\n\n/** @public */\nexport function useNotificationsApi<T>(\n f: (api: NotificationsApi) => Promise<T>,\n deps: any[] = [],\n) {\n const notificationsApi = useApi(notificationsApiRef);\n\n return useAsyncRetry(async () => {\n return await f(notificationsApi);\n }, deps);\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback, useEffect, useState } from 'react';\n\n/** @public */\nexport function useWebNotifications() {\n const [webNotificationPermission, setWebNotificationPermission] =\n useState('default');\n const [webNotifications, setWebNotifications] = useState<Notification[]>([]);\n\n useEffect(() => {\n if ('Notification' in window && webNotificationPermission === 'default') {\n window.Notification.requestPermission().then(permission => {\n setWebNotificationPermission(permission);\n });\n }\n }, [webNotificationPermission]);\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n webNotifications.forEach(n => n.close());\n setWebNotifications([]);\n }\n });\n\n const sendWebNotification = useCallback(\n (options: { title: string; description: string; link?: string }) => {\n if (webNotificationPermission !== 'granted') {\n return null;\n }\n\n const notification = new Notification(options.title, {\n body: options.description,\n });\n\n notification.onclick = event => {\n event.preventDefault();\n notification.close();\n if (options.link) {\n window.open(options.link, '_blank');\n }\n };\n\n return notification;\n },\n [webNotificationPermission],\n );\n\n return { sendWebNotification };\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback, useEffect, useState } from 'react';\n\n/** @public */\nexport function useTitleCounter() {\n const [title, setTitle] = useState(document.title);\n const [count, setCount] = useState(0);\n\n const getPrefix = (value: number) => {\n return value === 0 ? '' : `(${value}) `;\n };\n\n const cleanTitle = (currentTitle: string) => {\n return currentTitle.replace(/^\\(\\d+\\)\\s/, '');\n };\n\n useEffect(() => {\n document.title = title;\n }, [title]);\n\n useEffect(() => {\n const baseTitle = cleanTitle(title);\n setTitle(`${getPrefix(count)}${baseTitle}`);\n return () => {\n document.title = cleanTitle(title);\n };\n }, [title, count]);\n\n const titleElement = document.querySelector('title');\n if (titleElement) {\n new MutationObserver(() => {\n setTitle(document.title);\n }).observe(titleElement, {\n subtree: true,\n characterData: true,\n childList: true,\n });\n }\n\n const setNotificationCount = useCallback(\n (newCount: number) => setCount(newCount),\n [],\n );\n\n return { setNotificationCount };\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useEffect } from 'react';\nimport { useNotificationsApi } from '../../hooks';\nimport { SidebarItem } from '@backstage/core-components';\nimport NotificationsIcon from '@material-ui/icons/Notifications';\nimport { useApi, useRouteRef } from '@backstage/core-plugin-api';\nimport { rootRouteRef } from '../../routes';\nimport { useSignal } from '@backstage/plugin-signals-react';\nimport { NotificationSignal } from '@backstage/plugin-notifications-common';\nimport { useWebNotifications } from '../../hooks/useWebNotifications';\nimport { useTitleCounter } from '../../hooks/useTitleCounter';\nimport { notificationsApiRef } from '../../api';\n\n/** @public */\nexport const NotificationsSidebarItem = (props?: {\n webNotificationsEnabled?: boolean;\n titleCounterEnabled?: boolean;\n}) => {\n const { webNotificationsEnabled = false, titleCounterEnabled = true } =\n props ?? { webNotificationsEnabled: false, titleCounterEnabled: true };\n\n const { loading, error, value, retry } = useNotificationsApi(api =>\n api.getStatus(),\n );\n const notificationsApi = useApi(notificationsApiRef);\n const [unreadCount, setUnreadCount] = React.useState(0);\n const notificationsRoute = useRouteRef(rootRouteRef);\n // TODO: Do we want to add long polling in case signals are not available\n const { lastSignal } = useSignal<NotificationSignal>('notifications');\n const { sendWebNotification } = useWebNotifications();\n const [refresh, setRefresh] = React.useState(false);\n const { setNotificationCount } = useTitleCounter();\n\n useEffect(() => {\n if (refresh) {\n retry();\n setRefresh(false);\n }\n }, [refresh, retry]);\n\n useEffect(() => {\n const handleWebNotification = (signal: NotificationSignal) => {\n if (!webNotificationsEnabled || signal.action !== 'new_notification') {\n return;\n }\n\n notificationsApi\n .getNotification(signal.notification_id)\n .then(notification => {\n if (!notification) {\n return;\n }\n sendWebNotification({\n title: notification.payload.title,\n description: notification.payload.description ?? '',\n link: notification.payload.link,\n });\n });\n };\n\n if (lastSignal && lastSignal.action) {\n handleWebNotification(lastSignal);\n setRefresh(true);\n }\n }, [\n lastSignal,\n sendWebNotification,\n webNotificationsEnabled,\n notificationsApi,\n ]);\n\n useEffect(() => {\n if (!loading && !error && value) {\n setUnreadCount(value.unread);\n if (titleCounterEnabled) {\n setNotificationCount(value.unread);\n }\n }\n }, [loading, error, value, titleCounterEnabled, setNotificationCount]);\n\n // TODO: Figure out if the count can be added to hasNotifications\n return (\n <SidebarItem\n icon={NotificationsIcon}\n to={notificationsRoute()}\n text=\"Notifications\"\n hasNotifications={!error && !!unreadCount}\n />\n );\n};\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useMemo } from 'react';\nimport throttle from 'lodash/throttle';\nimport { Box, IconButton, Tooltip, Typography } from '@material-ui/core';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport { notificationsApiRef } from '../../api';\nimport { useApi } from '@backstage/core-plugin-api';\nimport MarkAsUnreadIcon from '@material-ui/icons/Markunread';\nimport MarkAsReadIcon from '@material-ui/icons/CheckCircle';\n\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { Link, Table, TableColumn } from '@backstage/core-components';\n\nconst ThrottleDelayMs = 1000;\n\n/** @public */\nexport type NotificationsTableProps = {\n isLoading?: boolean;\n notifications?: Notification[];\n onUpdate: () => void;\n setContainsText: (search: string) => void;\n};\n\n/** @public */\nexport const NotificationsTable = ({\n isLoading,\n notifications = [],\n onUpdate,\n setContainsText,\n}: NotificationsTableProps) => {\n const notificationsApi = useApi(notificationsApiRef);\n\n const onSwitchReadStatus = React.useCallback(\n (notification: Notification) => {\n notificationsApi\n .updateNotifications({\n ids: [notification.id],\n read: !notification.read,\n })\n .then(() => onUpdate());\n },\n [notificationsApi, onUpdate],\n );\n\n const throttledContainsTextHandler = useMemo(\n () => throttle(setContainsText, ThrottleDelayMs),\n [setContainsText],\n );\n\n const compactColumns = React.useMemo(\n (): TableColumn<Notification>[] => [\n {\n customFilterAndSearch: () =>\n true /* Keep it on backend due to pagination. If recent flickering is an issue, implement search here as well. */,\n render: (notification: Notification) => {\n // Compact content\n return (\n <>\n <Box>\n <Typography variant=\"subtitle2\">\n {notification.payload.link ? (\n <Link to={notification.payload.link}>\n {notification.payload.title}\n </Link>\n ) : (\n notification.payload.title\n )}\n </Typography>\n <Typography variant=\"body2\">\n {notification.payload.description}\n </Typography>\n <Typography variant=\"caption\">\n {notification.origin && (\n <>{notification.origin}&nbsp;&bull;&nbsp;</>\n )}\n {notification.payload.topic && (\n <>{notification.payload.topic}&nbsp;&bull;&nbsp;</>\n )}\n {notification.created && (\n <RelativeTime value={notification.created} />\n )}\n </Typography>\n </Box>\n </>\n );\n },\n },\n // {\n // // TODO: additional action links\n // width: '25%',\n // render: (notification: Notification) => {\n // return (\n // notification.payload.link && (\n // <Grid container>\n // {/* TODO: render additionalLinks of different titles */}\n // <Grid item>\n // <Link\n // key={notification.payload.link}\n // to={notification.payload.link}\n // >\n // &nbsp;More info\n // </Link>\n // </Grid>\n // </Grid>\n // )\n // );\n // },\n // },\n {\n // TODO: action for saving notifications\n // actions\n width: '1rem',\n render: (notification: Notification) => {\n const markAsReadText = !!notification.read\n ? 'Return among unread'\n : 'Mark as read';\n const IconComponent = !!notification.read\n ? MarkAsUnreadIcon\n : MarkAsReadIcon;\n\n return (\n <Tooltip title={markAsReadText}>\n <IconButton\n onClick={() => {\n onSwitchReadStatus(notification);\n }}\n >\n <IconComponent aria-label={markAsReadText} />\n </IconButton>\n </Tooltip>\n );\n },\n },\n ],\n [onSwitchReadStatus],\n );\n\n // TODO: render \"Saved notifications\" as \"Pinned\"\n return (\n <Table<Notification>\n isLoading={isLoading}\n options={{\n search: true,\n // TODO: add pagination\n // paging: true,\n // pageSize,\n header: false,\n sorting: false,\n }}\n // onPageChange={setPageNumber}\n // onRowsPerPageChange={setPageSize}\n // page={offset}\n // totalCount={value?.totalCount}\n onSearchChange={throttledContainsTextHandler}\n data={notifications}\n columns={compactColumns}\n />\n );\n};\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-notifications",
3
- "version": "0.0.1",
3
+ "version": "0.1.0-next.0",
4
4
  "main": "dist/index.esm.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -28,17 +28,18 @@
28
28
  "postpack": "backstage-cli package postpack"
29
29
  },
30
30
  "dependencies": {
31
- "@backstage/core-components": "^0.14.0",
32
- "@backstage/core-plugin-api": "^1.9.0",
33
- "@backstage/errors": "^1.2.3",
34
- "@backstage/plugin-notifications-common": "^0.0.1",
35
- "@backstage/plugin-signals-react": "^0.0.1",
36
- "@backstage/theme": "^0.5.1",
31
+ "@backstage/core-components": "^0.14.1-next.0",
32
+ "@backstage/core-plugin-api": "^1.9.1-next.0",
33
+ "@backstage/errors": "^1.2.4-next.0",
34
+ "@backstage/plugin-notifications-common": "^0.0.2-next.0",
35
+ "@backstage/plugin-signals-react": "^0.0.2-next.0",
36
+ "@backstage/theme": "^0.5.2-next.0",
37
37
  "@backstage/types": "^1.1.1",
38
38
  "@material-ui/core": "^4.9.13",
39
39
  "@material-ui/icons": "^4.9.1",
40
40
  "@material-ui/lab": "^4.0.0-alpha.61",
41
41
  "@types/react": "^16.13.1 || ^17.0.0",
42
+ "lodash": "^4.17.21",
42
43
  "react-relative-time": "^0.0.9",
43
44
  "react-use": "^17.2.4"
44
45
  },
@@ -47,10 +48,10 @@
47
48
  "react-router-dom": "6.0.0-beta.0 || ^6.3.0"
48
49
  },
49
50
  "devDependencies": {
50
- "@backstage/cli": "^0.25.2",
51
- "@backstage/core-app-api": "^1.12.0",
52
- "@backstage/dev-utils": "^1.0.27",
53
- "@backstage/test-utils": "^1.5.0",
51
+ "@backstage/cli": "^0.25.3-next.0",
52
+ "@backstage/core-app-api": "^1.12.1-next.0",
53
+ "@backstage/dev-utils": "^1.0.28-next.0",
54
+ "@backstage/test-utils": "^1.5.1-next.0",
54
55
  "@testing-library/jest-dom": "^6.0.0",
55
56
  "@testing-library/react": "^14.0.0",
56
57
  "@testing-library/user-event": "^14.0.0",
@@ -1,89 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { ErrorPanel, PageWithHeader, Content } from '@backstage/core-components';
3
- import { useNotificationsApi, NotificationsTable } from '../index.esm.js';
4
- import { makeStyles, Grid, Button } from '@material-ui/core';
5
- import Bookmark from '@material-ui/icons/Bookmark';
6
- import Check from '@material-ui/icons/Check';
7
- import Inbox from '@material-ui/icons/Inbox';
8
- import { useSignal } from '@backstage/plugin-signals-react';
9
- import '@backstage/core-plugin-api';
10
- import '@backstage/errors';
11
- import 'react-use/lib/useAsyncRetry';
12
- import '@material-ui/icons/Notifications';
13
- import 'react-router-dom';
14
- import '@material-ui/core/Checkbox';
15
- import '@material-ui/icons/Close';
16
- import 'react-relative-time';
17
- import '@material-ui/icons/ArrowForward';
18
-
19
- const useStyles = makeStyles((_theme) => ({
20
- filterButton: {
21
- width: "100%",
22
- justifyContent: "start"
23
- }
24
- }));
25
- const NotificationsPage = () => {
26
- const [type, setType] = useState("undone");
27
- const [refresh, setRefresh] = React.useState(false);
28
- const { error, value, retry } = useNotificationsApi(
29
- (api) => api.getNotifications({ type }),
30
- [type]
31
- );
32
- useEffect(() => {
33
- if (refresh) {
34
- retry();
35
- setRefresh(false);
36
- }
37
- }, [refresh, setRefresh, retry]);
38
- const { lastSignal } = useSignal("notifications");
39
- useEffect(() => {
40
- if (lastSignal && lastSignal.action) {
41
- setRefresh(true);
42
- }
43
- }, [lastSignal]);
44
- const onUpdate = () => {
45
- setRefresh(true);
46
- };
47
- const styles = useStyles();
48
- if (error) {
49
- return /* @__PURE__ */ React.createElement(ErrorPanel, { error: new Error("Failed to load notifications") });
50
- }
51
- return /* @__PURE__ */ React.createElement(PageWithHeader, { title: "Notifications", themeId: "tool" }, /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 2 }, /* @__PURE__ */ React.createElement(
52
- Button,
53
- {
54
- className: styles.filterButton,
55
- startIcon: /* @__PURE__ */ React.createElement(Inbox, null),
56
- variant: type === "undone" ? "contained" : "text",
57
- onClick: () => setType("undone")
58
- },
59
- "Inbox"
60
- ), /* @__PURE__ */ React.createElement(
61
- Button,
62
- {
63
- className: styles.filterButton,
64
- startIcon: /* @__PURE__ */ React.createElement(Check, null),
65
- variant: type === "done" ? "contained" : "text",
66
- onClick: () => setType("done")
67
- },
68
- "Done"
69
- ), /* @__PURE__ */ React.createElement(
70
- Button,
71
- {
72
- className: styles.filterButton,
73
- startIcon: /* @__PURE__ */ React.createElement(Bookmark, null),
74
- variant: type === "saved" ? "contained" : "text",
75
- onClick: () => setType("saved")
76
- },
77
- "Saved"
78
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 10 }, /* @__PURE__ */ React.createElement(
79
- NotificationsTable,
80
- {
81
- notifications: value,
82
- type,
83
- onUpdate
84
- }
85
- )))));
86
- };
87
-
88
- export { NotificationsPage };
89
- //# sourceMappingURL=index-1ad83349.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-1ad83349.esm.js","sources":["../../src/components/NotificationsPage/NotificationsPage.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useState } from 'react';\nimport {\n Content,\n ErrorPanel,\n PageWithHeader,\n} from '@backstage/core-components';\nimport { NotificationsTable } from '../NotificationsTable';\nimport { useNotificationsApi } from '../../hooks';\nimport { Button, Grid, makeStyles } from '@material-ui/core';\nimport Bookmark from '@material-ui/icons/Bookmark';\nimport Check from '@material-ui/icons/Check';\nimport Inbox from '@material-ui/icons/Inbox';\nimport { NotificationType } from '@backstage/plugin-notifications-common';\nimport { useSignal } from '@backstage/plugin-signals-react';\n\nconst useStyles = makeStyles(_theme => ({\n filterButton: {\n width: '100%',\n justifyContent: 'start',\n },\n}));\n\nexport const NotificationsPage = () => {\n const [type, setType] = useState<NotificationType>('undone');\n const [refresh, setRefresh] = React.useState(false);\n\n const { error, value, retry } = useNotificationsApi(\n api => api.getNotifications({ type }),\n [type],\n );\n\n useEffect(() => {\n if (refresh) {\n retry();\n setRefresh(false);\n }\n }, [refresh, setRefresh, retry]);\n\n const { lastSignal } = useSignal('notifications');\n useEffect(() => {\n if (lastSignal && lastSignal.action) {\n setRefresh(true);\n }\n }, [lastSignal]);\n\n const onUpdate = () => {\n setRefresh(true);\n };\n\n const styles = useStyles();\n if (error) {\n return <ErrorPanel error={new Error('Failed to load notifications')} />;\n }\n\n return (\n <PageWithHeader title=\"Notifications\" themeId=\"tool\">\n <Content>\n <Grid container>\n <Grid item xs={2}>\n <Button\n className={styles.filterButton}\n startIcon={<Inbox />}\n variant={type === 'undone' ? 'contained' : 'text'}\n onClick={() => setType('undone')}\n >\n Inbox\n </Button>\n <Button\n className={styles.filterButton}\n startIcon={<Check />}\n variant={type === 'done' ? 'contained' : 'text'}\n onClick={() => setType('done')}\n >\n Done\n </Button>\n <Button\n className={styles.filterButton}\n startIcon={<Bookmark />}\n variant={type === 'saved' ? 'contained' : 'text'}\n onClick={() => setType('saved')}\n >\n Saved\n </Button>\n </Grid>\n <Grid item xs={10}>\n <NotificationsTable\n notifications={value}\n type={type}\n onUpdate={onUpdate}\n />\n </Grid>\n </Grid>\n </Content>\n </PageWithHeader>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,MAAM,SAAA,GAAY,WAAW,CAAW,MAAA,MAAA;AAAA,EACtC,YAAc,EAAA;AAAA,IACZ,KAAO,EAAA,MAAA;AAAA,IACP,cAAgB,EAAA,OAAA;AAAA,GAClB;AACF,CAAE,CAAA,CAAA,CAAA;AAEK,MAAM,oBAAoB,MAAM;AACrC,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAA2B,QAAQ,CAAA,CAAA;AAC3D,EAAA,MAAM,CAAC,OAAS,EAAA,UAAU,CAAI,GAAA,KAAA,CAAM,SAAS,KAAK,CAAA,CAAA;AAElD,EAAA,MAAM,EAAE,KAAA,EAAO,KAAO,EAAA,KAAA,EAAU,GAAA,mBAAA;AAAA,IAC9B,CAAO,GAAA,KAAA,GAAA,CAAI,gBAAiB,CAAA,EAAE,MAAM,CAAA;AAAA,IACpC,CAAC,IAAI,CAAA;AAAA,GACP,CAAA;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAS,EAAA;AACX,MAAM,KAAA,EAAA,CAAA;AACN,MAAA,UAAA,CAAW,KAAK,CAAA,CAAA;AAAA,KAClB;AAAA,GACC,EAAA,CAAC,OAAS,EAAA,UAAA,EAAY,KAAK,CAAC,CAAA,CAAA;AAE/B,EAAA,MAAM,EAAE,UAAA,EAAe,GAAA,SAAA,CAAU,eAAe,CAAA,CAAA;AAChD,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,UAAA,IAAc,WAAW,MAAQ,EAAA;AACnC,MAAA,UAAA,CAAW,IAAI,CAAA,CAAA;AAAA,KACjB;AAAA,GACF,EAAG,CAAC,UAAU,CAAC,CAAA,CAAA;AAEf,EAAA,MAAM,WAAW,MAAM;AACrB,IAAA,UAAA,CAAW,IAAI,CAAA,CAAA;AAAA,GACjB,CAAA;AAEA,EAAA,MAAM,SAAS,SAAU,EAAA,CAAA;AACzB,EAAA,IAAI,KAAO,EAAA;AACT,IAAA,2CAAQ,UAAW,EAAA,EAAA,KAAA,EAAO,IAAI,KAAA,CAAM,8BAA8B,CAAG,EAAA,CAAA,CAAA;AAAA,GACvE;AAEA,EAAA,2CACG,cAAe,EAAA,EAAA,KAAA,EAAM,eAAgB,EAAA,OAAA,EAAQ,0BAC3C,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,IAAA,kBACE,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,WAAS,IACb,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,IAAI,EAAA,IAAA,EAAC,IAAI,CACb,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,MAAO,CAAA,YAAA;AAAA,MAClB,SAAA,sCAAY,KAAM,EAAA,IAAA,CAAA;AAAA,MAClB,OAAA,EAAS,IAAS,KAAA,QAAA,GAAW,WAAc,GAAA,MAAA;AAAA,MAC3C,OAAA,EAAS,MAAM,OAAA,CAAQ,QAAQ,CAAA;AAAA,KAAA;AAAA,IAChC,OAAA;AAAA,GAGD,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,MAAO,CAAA,YAAA;AAAA,MAClB,SAAA,sCAAY,KAAM,EAAA,IAAA,CAAA;AAAA,MAClB,OAAA,EAAS,IAAS,KAAA,MAAA,GAAS,WAAc,GAAA,MAAA;AAAA,MACzC,OAAA,EAAS,MAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,KAAA;AAAA,IAC9B,MAAA;AAAA,GAGD,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,MAAO,CAAA,YAAA;AAAA,MAClB,SAAA,sCAAY,QAAS,EAAA,IAAA,CAAA;AAAA,MACrB,OAAA,EAAS,IAAS,KAAA,OAAA,GAAU,WAAc,GAAA,MAAA;AAAA,MAC1C,OAAA,EAAS,MAAM,OAAA,CAAQ,OAAO,CAAA;AAAA,KAAA;AAAA,IAC/B,OAAA;AAAA,GAGH,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,IAAI,EAAA,IAAA,EAAC,IAAI,EACb,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,kBAAA;AAAA,IAAA;AAAA,MACC,aAAe,EAAA,KAAA;AAAA,MACf,IAAA;AAAA,MACA,QAAA;AAAA,KAAA;AAAA,GAEJ,CACF,CACF,CACF,CAAA,CAAA;AAEJ;;;;"}