@backstage/plugin-notifications 0.5.21-next.1 → 0.6.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 +30 -0
- package/dist/alpha/extensions/unreadNotificationsHomeWidget.esm.js +57 -0
- package/dist/alpha/extensions/unreadNotificationsHomeWidget.esm.js.map +1 -0
- package/dist/alpha.d.ts +17 -0
- package/dist/alpha.esm.js +2 -1
- package/dist/alpha.esm.js.map +1 -1
- package/dist/components/NotificationsPage/NotificationsPage.esm.js +47 -3
- package/dist/components/NotificationsPage/NotificationsPage.esm.js.map +1 -1
- package/dist/components/NotificationsSideBarItem/NotificationsSideBarItem.esm.js +1 -1
- package/dist/components/NotificationsTable/NotificationIcon.esm.js +14 -8
- package/dist/components/NotificationsTable/NotificationIcon.esm.js.map +1 -1
- package/dist/components/NotificationsTable/NotificationsTable.esm.js +76 -41
- package/dist/components/NotificationsTable/NotificationsTable.esm.js.map +1 -1
- package/dist/components/NotificationsTable/NotificationsTable.module.css.esm.js +2 -2
- package/dist/components/UnreadNotificationsCard/UnreadNotificationsCard.esm.js +255 -0
- package/dist/components/UnreadNotificationsCard/UnreadNotificationsCard.esm.js.map +1 -0
- package/dist/components/UnreadNotificationsCard/UnreadNotificationsCard.module.css.esm.js +8 -0
- package/dist/components/UnreadNotificationsCard/UnreadNotificationsCard.module.css.esm.js.map +1 -0
- package/dist/components/UnreadNotificationsCard/index.esm.js +7 -0
- package/dist/components/UnreadNotificationsCard/index.esm.js.map +1 -0
- package/dist/components/UserNotificationSettingsCard/OriginRow.esm.js +3 -3
- package/dist/components/UserNotificationSettingsCard/OriginRow.esm.js.map +1 -1
- package/dist/components/UserNotificationSettingsCard/TopicRow.esm.js +1 -3
- package/dist/components/UserNotificationSettingsCard/TopicRow.esm.js.map +1 -1
- package/dist/components/UserNotificationSettingsCard/UserNotificationSettingsCard.esm.js +1 -0
- package/dist/components/UserNotificationSettingsCard/UserNotificationSettingsCard.esm.js.map +1 -1
- package/dist/hooks/useNotificationsRefresh.esm.js +24 -0
- package/dist/hooks/useNotificationsRefresh.esm.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/plugins/notifications/package.json.esm.js +2 -1
- package/dist/plugins/notifications/package.json.esm.js.map +1 -1
- package/dist/translation.esm.js +9 -0
- package/dist/translation.esm.js.map +1 -1
- package/dist/utils/notificationLinks.esm.js +11 -0
- package/dist/utils/notificationLinks.esm.js.map +1 -0
- package/dist/utils/plainText.esm.js +30 -0
- package/dist/utils/plainText.esm.js.map +1 -0
- package/package.json +14 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @backstage/plugin-notifications
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cd4a9c7: Added an unread notifications home page card and NFS home widget.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 736d84e: Use locale-insensitive Unicode casing for consistent string handling across environments.
|
|
12
|
+
- b31afcf: Fixed circular dependency warnings when building the notification settings UI.
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
- @backstage/core-components@0.18.14
|
|
15
|
+
- @backstage/ui@0.18.0
|
|
16
|
+
- @backstage/frontend-plugin-api@0.18.1
|
|
17
|
+
- @backstage/plugin-home-react@0.1.42
|
|
18
|
+
- @backstage/core-plugin-api@1.12.10
|
|
19
|
+
- @backstage/plugin-notifications-common@0.2.4
|
|
20
|
+
- @backstage/plugin-signals-react@0.0.26
|
|
21
|
+
|
|
22
|
+
## 0.5.21-next.2
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
- @backstage/ui@0.18.0-next.2
|
|
28
|
+
- @backstage/core-components@0.18.14-next.2
|
|
29
|
+
- @backstage/core-plugin-api@1.12.10-next.1
|
|
30
|
+
- @backstage/frontend-plugin-api@0.18.1-next.1
|
|
31
|
+
- @backstage/plugin-notifications-common@0.2.4-next.0
|
|
32
|
+
|
|
3
33
|
## 0.5.21-next.1
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { HomePageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';
|
|
3
|
+
|
|
4
|
+
const defaultCardLayout = {
|
|
5
|
+
width: {
|
|
6
|
+
minColumns: 4,
|
|
7
|
+
maxColumns: 12,
|
|
8
|
+
defaultColumns: 6
|
|
9
|
+
},
|
|
10
|
+
height: {
|
|
11
|
+
minRows: 5,
|
|
12
|
+
maxRows: 14,
|
|
13
|
+
defaultRows: 10
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function BorderlessHomeWidgetRenderer({ Content }) {
|
|
17
|
+
return /* @__PURE__ */ jsx(Content, {});
|
|
18
|
+
}
|
|
19
|
+
const unreadNotificationsHomeWidget = HomePageWidgetBlueprint.make({
|
|
20
|
+
name: "unread-notifications",
|
|
21
|
+
params: {
|
|
22
|
+
name: "UnreadNotifications",
|
|
23
|
+
description: "Shows the latest unread notifications",
|
|
24
|
+
layout: defaultCardLayout,
|
|
25
|
+
componentProps: {
|
|
26
|
+
Renderer: BorderlessHomeWidgetRenderer
|
|
27
|
+
},
|
|
28
|
+
components: () => import('../../components/UnreadNotificationsCard/index.esm.js').then((m) => ({
|
|
29
|
+
Content: m.UnreadNotificationsCardWithProvider
|
|
30
|
+
})),
|
|
31
|
+
settings: {
|
|
32
|
+
schema: {
|
|
33
|
+
title: "Unread notifications",
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
maxMessages: {
|
|
37
|
+
title: "Maximum messages",
|
|
38
|
+
type: "number",
|
|
39
|
+
default: 5,
|
|
40
|
+
minimum: 1,
|
|
41
|
+
maximum: 20
|
|
42
|
+
},
|
|
43
|
+
charLimit: {
|
|
44
|
+
title: "Title character limit",
|
|
45
|
+
type: "number",
|
|
46
|
+
default: 80,
|
|
47
|
+
minimum: 20,
|
|
48
|
+
maximum: 200
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export { unreadNotificationsHomeWidget };
|
|
57
|
+
//# sourceMappingURL=unreadNotificationsHomeWidget.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unreadNotificationsHomeWidget.esm.js","sources":["../../../src/alpha/extensions/unreadNotificationsHomeWidget.tsx"],"sourcesContent":["/*\n * Copyright 2026 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 { type RendererProps } from '@backstage/plugin-home-react';\nimport { HomePageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';\n\nconst defaultCardLayout = {\n width: {\n minColumns: 4,\n maxColumns: 12,\n defaultColumns: 6,\n },\n height: {\n minRows: 5,\n maxRows: 14,\n defaultRows: 10,\n },\n} as const;\n\nfunction BorderlessHomeWidgetRenderer({ Content }: RendererProps) {\n return <Content />;\n}\n\n/**\n * NFS home page widget for unread notifications.\n *\n * @alpha\n */\nexport const unreadNotificationsHomeWidget = HomePageWidgetBlueprint.make({\n name: 'unread-notifications',\n params: {\n name: 'UnreadNotifications',\n description: 'Shows the latest unread notifications',\n layout: defaultCardLayout,\n componentProps: {\n Renderer: BorderlessHomeWidgetRenderer,\n },\n components: () =>\n import('../../components/UnreadNotificationsCard').then(m => ({\n Content: m.UnreadNotificationsCardWithProvider,\n })),\n settings: {\n schema: {\n title: 'Unread notifications',\n type: 'object',\n properties: {\n maxMessages: {\n title: 'Maximum messages',\n type: 'number',\n default: 5,\n minimum: 1,\n maximum: 20,\n },\n charLimit: {\n title: 'Title character limit',\n type: 'number',\n default: 80,\n minimum: 20,\n maximum: 200,\n },\n },\n },\n },\n },\n});\n"],"names":[],"mappings":";;;AAmBA,MAAM,iBAAA,GAAoB;AAAA,EACxB,KAAA,EAAO;AAAA,IACL,UAAA,EAAY,CAAA;AAAA,IACZ,UAAA,EAAY,EAAA;AAAA,IACZ,cAAA,EAAgB;AAAA,GAClB;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,OAAA,EAAS,CAAA;AAAA,IACT,OAAA,EAAS,EAAA;AAAA,IACT,WAAA,EAAa;AAAA;AAEjB,CAAA;AAEA,SAAS,4BAAA,CAA6B,EAAE,OAAA,EAAQ,EAAkB;AAChE,EAAA,2BAAQ,OAAA,EAAA,EAAQ,CAAA;AAClB;AAOO,MAAM,6BAAA,GAAgC,wBAAwB,IAAA,CAAK;AAAA,EACxE,IAAA,EAAM,sBAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,qBAAA;AAAA,IACN,WAAA,EAAa,uCAAA;AAAA,IACb,MAAA,EAAQ,iBAAA;AAAA,IACR,cAAA,EAAgB;AAAA,MACd,QAAA,EAAU;AAAA,KACZ;AAAA,IACA,YAAY,MACV,OAAO,uDAA0C,CAAA,CAAE,KAAK,CAAA,CAAA,MAAM;AAAA,MAC5D,SAAS,CAAA,CAAE;AAAA,KACb,CAAE,CAAA;AAAA,IACJ,QAAA,EAAU;AAAA,MACR,MAAA,EAAQ;AAAA,QACN,KAAA,EAAO,sBAAA;AAAA,QACP,IAAA,EAAM,QAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,WAAA,EAAa;AAAA,YACX,KAAA,EAAO,kBAAA;AAAA,YACP,IAAA,EAAM,QAAA;AAAA,YACN,OAAA,EAAS,CAAA;AAAA,YACT,OAAA,EAAS,CAAA;AAAA,YACT,OAAA,EAAS;AAAA,WACX;AAAA,UACA,SAAA,EAAW;AAAA,YACT,KAAA,EAAO,uBAAA;AAAA,YACP,IAAA,EAAM,QAAA;AAAA,YACN,OAAA,EAAS,EAAA;AAAA,YACT,OAAA,EAAS,EAAA;AAAA,YACT,OAAA,EAAS;AAAA;AACX;AACF;AACF;AACF;AAEJ,CAAC;;;;"}
|
package/dist/alpha.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import * as _backstage_plugin_home_react_alpha from '@backstage/plugin-home-react/alpha';
|
|
2
3
|
import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
|
|
3
4
|
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
4
5
|
|
|
@@ -15,6 +16,15 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
|
|
|
15
16
|
inputs: {};
|
|
16
17
|
params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
|
|
17
18
|
}>;
|
|
19
|
+
"home-page-widget:notifications/unread-notifications": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
20
|
+
kind: "home-page-widget";
|
|
21
|
+
name: "unread-notifications";
|
|
22
|
+
config: {};
|
|
23
|
+
configInput: {};
|
|
24
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_plugin_home_react_alpha.HomePageWidgetData, "home.widget.data", {}>;
|
|
25
|
+
inputs: {};
|
|
26
|
+
params: _backstage_plugin_home_react_alpha.HomePageWidgetBlueprintParams;
|
|
27
|
+
}>;
|
|
18
28
|
"page:notifications": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
19
29
|
kind: "page";
|
|
20
30
|
name: undefined;
|
|
@@ -121,6 +131,13 @@ declare const notificationsTranslationRef: _backstage_frontend_plugin_api.Transl
|
|
|
121
131
|
readonly "notificationsPage.tableTitle.unread_other": "Unread notifications ({{count}})";
|
|
122
132
|
readonly "notificationsPage.tableTitle.read_one": "Read notifications ({{count}})";
|
|
123
133
|
readonly "notificationsPage.tableTitle.read_other": "Read notifications ({{count}})";
|
|
134
|
+
readonly "unreadCard.emptyState": "All caught up!";
|
|
135
|
+
readonly "unreadCard.title_one": "Last unread notifications ({{count}})";
|
|
136
|
+
readonly "unreadCard.title_other": "Last unread notifications ({{count}})";
|
|
137
|
+
readonly "unreadCard.viewAll": "View All";
|
|
138
|
+
readonly "unreadCard.columnNotification": "Notification";
|
|
139
|
+
readonly "unreadCard.columnActions": "Actions";
|
|
140
|
+
readonly "unreadCard.resultsCount": "{{displayed}} results out of {{total}}";
|
|
124
141
|
}>;
|
|
125
142
|
|
|
126
143
|
export { _default as default, notificationsTranslationRef };
|
package/dist/alpha.esm.js
CHANGED
|
@@ -4,6 +4,7 @@ import { RiNotification3Line } from '@remixicon/react';
|
|
|
4
4
|
import { rootRouteRef } from './routes.esm.js';
|
|
5
5
|
import { notificationsApiRef } from './api/NotificationsApi.esm.js';
|
|
6
6
|
import { NotificationsClient } from './api/NotificationsClient.esm.js';
|
|
7
|
+
import { unreadNotificationsHomeWidget } from './alpha/extensions/unreadNotificationsHomeWidget.esm.js';
|
|
7
8
|
import { notificationsTranslationRef as notificationsTranslationRef$1 } from './translation.esm.js';
|
|
8
9
|
|
|
9
10
|
const page = PageBlueprint.make({
|
|
@@ -29,7 +30,7 @@ var alpha = createFrontendPlugin({
|
|
|
29
30
|
root: rootRouteRef
|
|
30
31
|
},
|
|
31
32
|
// TODO(Rugvip): Nav item (i.e. NotificationsSidebarItem) currently needs to be installed manually
|
|
32
|
-
extensions: [page, api]
|
|
33
|
+
extensions: [page, api, unreadNotificationsHomeWidget]
|
|
33
34
|
});
|
|
34
35
|
const notificationsTranslationRef = notificationsTranslationRef$1;
|
|
35
36
|
|
package/dist/alpha.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.esm.js","sources":["../src/alpha.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 {\n ApiBlueprint,\n PageBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { RiNotification3Line } from '@remixicon/react';\nimport { rootRouteRef } from './routes';\nimport { NotificationsClient, notificationsApiRef } from './api';\n\nconst page = PageBlueprint.make({\n params: {\n path: '/notifications',\n routeRef: rootRouteRef,\n loader: () =>\n import('./components/NotificationsPage').then(m => (\n <m.NfsNotificationsPage />\n )),\n },\n});\n\nconst api = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: notificationsApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new NotificationsClient({ discoveryApi, fetchApi }),\n }),\n});\n\n/** @alpha */\nexport default createFrontendPlugin({\n pluginId: 'notifications',\n title: 'Notifications',\n icon: <RiNotification3Line />,\n info: { packageJson: () => import('../package.json') },\n routes: {\n root: rootRouteRef,\n },\n // TODO(Rugvip): Nav item (i.e. NotificationsSidebarItem) currently needs to be installed manually\n extensions: [page, api],\n});\n\nimport { notificationsTranslationRef as _notificationsTranslationRef } from './translation';\n\n/**\n * @alpha\n * @deprecated Import from `@backstage/plugin-notifications` instead.\n */\nexport const notificationsTranslationRef = _notificationsTranslationRef;\n"],"names":["_notificationsTranslationRef"],"mappings":"
|
|
1
|
+
{"version":3,"file":"alpha.esm.js","sources":["../src/alpha.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 {\n ApiBlueprint,\n PageBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { RiNotification3Line } from '@remixicon/react';\nimport { rootRouteRef } from './routes';\nimport { NotificationsClient, notificationsApiRef } from './api';\nimport { unreadNotificationsHomeWidget } from './alpha/extensions/unreadNotificationsHomeWidget';\n\nconst page = PageBlueprint.make({\n params: {\n path: '/notifications',\n routeRef: rootRouteRef,\n loader: () =>\n import('./components/NotificationsPage').then(m => (\n <m.NfsNotificationsPage />\n )),\n },\n});\n\nconst api = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: notificationsApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new NotificationsClient({ discoveryApi, fetchApi }),\n }),\n});\n\n/** @alpha */\nexport default createFrontendPlugin({\n pluginId: 'notifications',\n title: 'Notifications',\n icon: <RiNotification3Line />,\n info: { packageJson: () => import('../package.json') },\n routes: {\n root: rootRouteRef,\n },\n // TODO(Rugvip): Nav item (i.e. NotificationsSidebarItem) currently needs to be installed manually\n extensions: [page, api, unreadNotificationsHomeWidget],\n});\n\nimport { notificationsTranslationRef as _notificationsTranslationRef } from './translation';\n\n/**\n * @alpha\n * @deprecated Import from `@backstage/plugin-notifications` instead.\n */\nexport const notificationsTranslationRef = _notificationsTranslationRef;\n"],"names":["_notificationsTranslationRef"],"mappings":";;;;;;;;;AA4BA,MAAM,IAAA,GAAO,cAAc,IAAA,CAAK;AAAA,EAC9B,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,gBAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,MAAA,EAAQ,MACN,OAAO,6CAAgC,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAC5C,GAAA,CAAC,CAAA,CAAE,oBAAA,EAAF,EAAuB,CACzB;AAAA;AAEP,CAAC,CAAA;AAED,MAAM,GAAA,GAAM,aAAa,IAAA,CAAK;AAAA,EAC5B,MAAA,EAAQ,kBACN,YAAA,CAAa;AAAA,IACX,GAAA,EAAK,mBAAA;AAAA,IACL,IAAA,EAAM,EAAE,YAAA,EAAc,eAAA,EAAiB,UAAU,WAAA,EAAY;AAAA,IAC7D,OAAA,EAAS,CAAC,EAAE,YAAA,EAAc,QAAA,EAAS,KACjC,IAAI,mBAAA,CAAoB,EAAE,YAAA,EAAc,QAAA,EAAU;AAAA,GACrD;AACL,CAAC,CAAA;AAGD,YAAe,oBAAA,CAAqB;AAAA,EAClC,QAAA,EAAU,eAAA;AAAA,EACV,KAAA,EAAO,eAAA;AAAA,EACP,IAAA,sBAAO,mBAAA,EAAA,EAAoB,CAAA;AAAA,EAC3B,MAAM,EAAE,WAAA,EAAa,MAAM,OAAO,6CAAiB,CAAA,EAAE;AAAA,EACrD,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA;AAAA,EAEA,UAAA,EAAY,CAAC,IAAA,EAAM,GAAA,EAAK,6BAA6B;AACvD,CAAC,CAAA;AAQM,MAAM,2BAAA,GAA8BA;;;;"}
|
|
@@ -5,6 +5,7 @@ import { ResponseErrorPanel, Content, PageWithHeader } from '@backstage/core-com
|
|
|
5
5
|
import { Grid } from '@backstage/ui';
|
|
6
6
|
import { useSignal } from '@backstage/plugin-signals-react';
|
|
7
7
|
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
|
8
|
+
import { useSearchParams } from 'react-router-dom';
|
|
8
9
|
import { notificationsTranslationRef } from '../../translation.esm.js';
|
|
9
10
|
import { NotificationsTable } from '../NotificationsTable/NotificationsTable.esm.js';
|
|
10
11
|
import '../NotificationsTable/NotificationDescription.esm.js';
|
|
@@ -37,6 +38,9 @@ function NotificationsPageContent(props) {
|
|
|
37
38
|
} = props;
|
|
38
39
|
const [refresh, setRefresh] = useState(false);
|
|
39
40
|
const { lastSignal } = useSignal("notifications");
|
|
41
|
+
const [searchParams] = useSearchParams();
|
|
42
|
+
const highlightedNotificationId = searchParams.get("id") || void 0;
|
|
43
|
+
const [highlightedNotification, setHighlightedNotification] = useState();
|
|
40
44
|
const [unreadOnly, setUnreadOnly] = useState(true);
|
|
41
45
|
const [saved, setSaved] = useState(void 0);
|
|
42
46
|
const [pageNumber, setPageNumber] = useState(0);
|
|
@@ -101,13 +105,52 @@ function NotificationsPageContent(props) {
|
|
|
101
105
|
throttledSetRefresh(true);
|
|
102
106
|
}
|
|
103
107
|
}, [lastSignal, throttledSetRefresh]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (!highlightedNotificationId) {
|
|
110
|
+
setHighlightedNotification(void 0);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
setPageNumber(0);
|
|
114
|
+
}, [highlightedNotificationId]);
|
|
115
|
+
const { value: fetchedHighlightedNotification } = useNotificationsApi(
|
|
116
|
+
(api) => {
|
|
117
|
+
if (!highlightedNotificationId) {
|
|
118
|
+
return Promise.resolve(void 0);
|
|
119
|
+
}
|
|
120
|
+
return api.getNotification(highlightedNotificationId);
|
|
121
|
+
},
|
|
122
|
+
[highlightedNotificationId]
|
|
123
|
+
);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (!fetchedHighlightedNotification) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
setHighlightedNotification(fetchedHighlightedNotification);
|
|
129
|
+
if (!fetchedHighlightedNotification.read) {
|
|
130
|
+
setUnreadOnly(true);
|
|
131
|
+
} else {
|
|
132
|
+
setUnreadOnly(false);
|
|
133
|
+
}
|
|
134
|
+
}, [fetchedHighlightedNotification]);
|
|
104
135
|
const onUpdate = () => {
|
|
105
136
|
throttledSetRefresh(true);
|
|
106
137
|
};
|
|
138
|
+
const notifications = value?.[0]?.notifications;
|
|
139
|
+
const displayedNotifications = useMemo(() => {
|
|
140
|
+
if (!highlightedNotification || notifications?.some(
|
|
141
|
+
(notification) => notification.id === highlightedNotification.id
|
|
142
|
+
)) {
|
|
143
|
+
return notifications;
|
|
144
|
+
}
|
|
145
|
+
return [highlightedNotification, ...notifications ?? []];
|
|
146
|
+
}, [notifications, highlightedNotification]);
|
|
147
|
+
const injectedHighlightedNotification = highlightedNotification && !notifications?.some(
|
|
148
|
+
(notification) => notification.id === highlightedNotification.id
|
|
149
|
+
);
|
|
150
|
+
const displayedPageSize = injectedHighlightedNotification ? pageSize + 1 : pageSize;
|
|
107
151
|
if (error) {
|
|
108
152
|
return /* @__PURE__ */ jsx(ResponseErrorPanel, { error });
|
|
109
153
|
}
|
|
110
|
-
const notifications = value?.[0]?.notifications;
|
|
111
154
|
const totalCount = value?.[0]?.totalCount;
|
|
112
155
|
const isUnread = !!value?.[1]?.unread;
|
|
113
156
|
const allTopics = value?.[2]?.topics;
|
|
@@ -153,13 +196,14 @@ function NotificationsPageContent(props) {
|
|
|
153
196
|
isLoading: loading,
|
|
154
197
|
isUnread,
|
|
155
198
|
markAsReadOnLinkOpen,
|
|
156
|
-
notifications,
|
|
199
|
+
notifications: displayedNotifications,
|
|
200
|
+
highlightedNotificationId,
|
|
157
201
|
onUpdate,
|
|
158
202
|
setContainsText,
|
|
159
203
|
onPageChange: setPageNumber,
|
|
160
204
|
onRowsPerPageChange: setPageSize,
|
|
161
205
|
page: pageNumber,
|
|
162
|
-
pageSize,
|
|
206
|
+
pageSize: displayedPageSize,
|
|
163
207
|
totalCount
|
|
164
208
|
}
|
|
165
209
|
) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsPage.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 { useEffect, useMemo, useState } from 'react';\nimport throttle from 'lodash/throttle';\nimport {\n Content,\n PageWithHeader,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { Grid } from '@backstage/ui';\nimport { useSignal } from '@backstage/plugin-signals-react';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { notificationsTranslationRef } from '../../translation';\n\nconst TableTitleKeys = {\n all: 'notificationsPage.tableTitle.all',\n saved: 'notificationsPage.tableTitle.saved',\n unread: 'notificationsPage.tableTitle.unread',\n read: 'notificationsPage.tableTitle.read',\n} as const;\n\nimport { NotificationsTable } from '../NotificationsTable';\nimport { useNotificationsApi } from '../../hooks';\nimport {\n CreatedAfterOptions,\n NotificationsFilters,\n SortBy,\n SortByOptions,\n} from '../NotificationsFilters';\nimport {\n GetNotificationsOptions,\n GetNotificationsResponse,\n GetTopicsResponse,\n} from '../../api';\nimport {\n NotificationSeverity,\n NotificationStatus,\n} from '@backstage/plugin-notifications-common';\n\nconst ThrottleDelayMs = 2000;\n\n/** @public */\nexport type NotificationsPageProps = {\n /** Mark notification as read when opening the link it contains, defaults to false */\n markAsReadOnLinkOpen?: boolean;\n title?: string;\n themeId?: string;\n subtitle?: string;\n tooltip?: string;\n type?: string;\n typeLink?: string;\n};\n\nfunction NotificationsPageContent(\n props: NotificationsPageProps & { headerVariant: 'legacy' | 'bui' },\n) {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const {\n title = t('notificationsPage.title'),\n themeId = 'tool',\n subtitle,\n tooltip,\n type,\n typeLink,\n markAsReadOnLinkOpen,\n headerVariant,\n } = props;\n\n const [refresh, setRefresh] = useState(false);\n const { lastSignal } = useSignal('notifications');\n const [unreadOnly, setUnreadOnly] = useState<boolean | undefined>(true);\n const [saved, setSaved] = useState<boolean | undefined>(undefined);\n const [pageNumber, setPageNumber] = useState(0);\n const [pageSize, setPageSize] = useState(5);\n const [containsText, setContainsText] = useState<string>();\n const [createdAfter, setCreatedAfter] = useState<string>('all');\n const [sorting, setSorting] = useState<SortBy>(SortByOptions.newest.sortBy);\n const [severity, setSeverity] = useState<NotificationSeverity>('low');\n const [topic, setTopic] = useState<string>();\n\n const { error, value, retry, loading } = useNotificationsApi<\n [GetNotificationsResponse, NotificationStatus, GetTopicsResponse]\n >(\n api => {\n const options: GetNotificationsOptions = {\n search: containsText,\n limit: pageSize,\n offset: pageNumber * pageSize,\n minimumSeverity: severity,\n ...(sorting || {}),\n };\n if (unreadOnly !== undefined) {\n options.read = !unreadOnly;\n }\n if (saved !== undefined) {\n options.saved = saved;\n }\n if (topic !== undefined) {\n options.topic = topic;\n }\n\n const createdAfterDate =\n CreatedAfterOptions[\n createdAfter as keyof typeof CreatedAfterOptions\n ].getDate();\n if (createdAfterDate.valueOf() > 0) {\n options.createdAfter = createdAfterDate;\n }\n\n return Promise.all([\n api.getNotifications(options),\n api.getStatus(),\n api.getTopics(options),\n ]);\n },\n [\n containsText,\n unreadOnly,\n createdAfter,\n pageNumber,\n pageSize,\n sorting,\n saved,\n severity,\n topic,\n ],\n );\n\n const throttledSetRefresh = useMemo(\n () => throttle(setRefresh, ThrottleDelayMs),\n [setRefresh],\n );\n\n useEffect(() => {\n if (refresh && !loading) {\n retry();\n setRefresh(false);\n }\n }, [refresh, setRefresh, retry, loading]);\n\n useEffect(() => {\n if (lastSignal && lastSignal.action) {\n throttledSetRefresh(true);\n }\n }, [lastSignal, throttledSetRefresh]);\n\n const onUpdate = () => {\n throttledSetRefresh(true);\n };\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n const notifications = value?.[0]?.notifications;\n const totalCount = value?.[0]?.totalCount;\n const isUnread = !!value?.[1]?.unread;\n const allTopics = value?.[2]?.topics;\n\n let tableTitle: string = t(TableTitleKeys.all, {\n count: totalCount ?? 0,\n });\n if (saved) {\n tableTitle = t(TableTitleKeys.saved, {\n count: totalCount ?? 0,\n });\n } else if (unreadOnly === true) {\n tableTitle = t(TableTitleKeys.unread, {\n count: totalCount ?? 0,\n });\n } else if (unreadOnly === false) {\n tableTitle = t(TableTitleKeys.read, {\n count: totalCount ?? 0,\n });\n }\n\n const pageContent = (\n <Content>\n <Grid.Root columns=\"12\" gap=\"6\">\n <Grid.Item colSpan=\"2\">\n <NotificationsFilters\n unreadOnly={unreadOnly}\n onUnreadOnlyChanged={setUnreadOnly}\n createdAfter={createdAfter}\n onCreatedAfterChanged={setCreatedAfter}\n onSortingChanged={setSorting}\n sorting={sorting}\n saved={saved}\n onSavedChanged={setSaved}\n severity={severity}\n onSeverityChanged={setSeverity}\n topic={topic}\n onTopicChanged={setTopic}\n allTopics={allTopics}\n />\n </Grid.Item>\n <Grid.Item colSpan=\"10\">\n <NotificationsTable\n title={tableTitle}\n isLoading={loading}\n isUnread={isUnread}\n markAsReadOnLinkOpen={markAsReadOnLinkOpen}\n notifications={notifications}\n onUpdate={onUpdate}\n setContainsText={setContainsText}\n onPageChange={setPageNumber}\n onRowsPerPageChange={setPageSize}\n page={pageNumber}\n pageSize={pageSize}\n totalCount={totalCount}\n />\n </Grid.Item>\n </Grid.Root>\n </Content>\n );\n\n if (headerVariant === 'bui') {\n return pageContent;\n }\n\n return (\n <PageWithHeader\n title={title}\n themeId={themeId}\n tooltip={tooltip}\n subtitle={subtitle}\n type={type}\n typeLink={typeLink}\n >\n {pageContent}\n </PageWithHeader>\n );\n}\n\nexport const NotificationsPage = (props?: NotificationsPageProps) => (\n <NotificationsPageContent {...(props ?? {})} headerVariant=\"legacy\" />\n);\n\nexport const NfsNotificationsPage = (props?: NotificationsPageProps) => (\n <NotificationsPageContent {...(props ?? {})} headerVariant=\"bui\" />\n);\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NotificationsPage.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 { useEffect, useMemo, useState } from 'react';\nimport throttle from 'lodash/throttle';\nimport {\n Content,\n PageWithHeader,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { Grid } from '@backstage/ui';\nimport { useSignal } from '@backstage/plugin-signals-react';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { useSearchParams } from 'react-router-dom';\nimport {\n Notification,\n NotificationSeverity,\n NotificationStatus,\n} from '@backstage/plugin-notifications-common';\nimport { notificationsTranslationRef } from '../../translation';\n\nconst TableTitleKeys = {\n all: 'notificationsPage.tableTitle.all',\n saved: 'notificationsPage.tableTitle.saved',\n unread: 'notificationsPage.tableTitle.unread',\n read: 'notificationsPage.tableTitle.read',\n} as const;\n\nimport { NotificationsTable } from '../NotificationsTable';\nimport { useNotificationsApi } from '../../hooks';\nimport {\n CreatedAfterOptions,\n NotificationsFilters,\n SortBy,\n SortByOptions,\n} from '../NotificationsFilters';\nimport {\n GetNotificationsOptions,\n GetNotificationsResponse,\n GetTopicsResponse,\n} from '../../api';\n\nconst ThrottleDelayMs = 2000;\n\n/** @public */\nexport type NotificationsPageProps = {\n /** Mark notification as read when opening the link it contains, defaults to false */\n markAsReadOnLinkOpen?: boolean;\n title?: string;\n themeId?: string;\n subtitle?: string;\n tooltip?: string;\n type?: string;\n typeLink?: string;\n};\n\nfunction NotificationsPageContent(\n props: NotificationsPageProps & { headerVariant: 'legacy' | 'bui' },\n) {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const {\n title = t('notificationsPage.title'),\n themeId = 'tool',\n subtitle,\n tooltip,\n type,\n typeLink,\n markAsReadOnLinkOpen,\n headerVariant,\n } = props;\n\n const [refresh, setRefresh] = useState(false);\n // TODO: Reuse useNotificationsRefresh instead of duplicating signals +\n // polling refresh logic here.\n const { lastSignal } = useSignal('notifications');\n const [searchParams] = useSearchParams();\n const highlightedNotificationId = searchParams.get('id') || undefined;\n const [highlightedNotification, setHighlightedNotification] = useState<\n Notification | undefined\n >();\n const [unreadOnly, setUnreadOnly] = useState<boolean | undefined>(true);\n const [saved, setSaved] = useState<boolean | undefined>(undefined);\n const [pageNumber, setPageNumber] = useState(0);\n const [pageSize, setPageSize] = useState(5);\n const [containsText, setContainsText] = useState<string>();\n const [createdAfter, setCreatedAfter] = useState<string>('all');\n const [sorting, setSorting] = useState<SortBy>(SortByOptions.newest.sortBy);\n const [severity, setSeverity] = useState<NotificationSeverity>('low');\n const [topic, setTopic] = useState<string>();\n\n const { error, value, retry, loading } = useNotificationsApi<\n [GetNotificationsResponse, NotificationStatus, GetTopicsResponse]\n >(\n api => {\n const options: GetNotificationsOptions = {\n search: containsText,\n limit: pageSize,\n offset: pageNumber * pageSize,\n minimumSeverity: severity,\n ...(sorting || {}),\n };\n if (unreadOnly !== undefined) {\n options.read = !unreadOnly;\n }\n if (saved !== undefined) {\n options.saved = saved;\n }\n if (topic !== undefined) {\n options.topic = topic;\n }\n\n const createdAfterDate =\n CreatedAfterOptions[\n createdAfter as keyof typeof CreatedAfterOptions\n ].getDate();\n if (createdAfterDate.valueOf() > 0) {\n options.createdAfter = createdAfterDate;\n }\n\n return Promise.all([\n api.getNotifications(options),\n api.getStatus(),\n api.getTopics(options),\n ]);\n },\n [\n containsText,\n unreadOnly,\n createdAfter,\n pageNumber,\n pageSize,\n sorting,\n saved,\n severity,\n topic,\n ],\n );\n\n const throttledSetRefresh = useMemo(\n () => throttle(setRefresh, ThrottleDelayMs),\n [setRefresh],\n );\n\n useEffect(() => {\n if (refresh && !loading) {\n retry();\n setRefresh(false);\n }\n }, [refresh, setRefresh, retry, loading]);\n\n useEffect(() => {\n if (lastSignal && lastSignal.action) {\n throttledSetRefresh(true);\n }\n }, [lastSignal, throttledSetRefresh]);\n\n useEffect(() => {\n if (!highlightedNotificationId) {\n setHighlightedNotification(undefined);\n return;\n }\n\n setPageNumber(0);\n }, [highlightedNotificationId]);\n\n const { value: fetchedHighlightedNotification } = useNotificationsApi(\n api => {\n if (!highlightedNotificationId) {\n return Promise.resolve(undefined);\n }\n return api.getNotification(highlightedNotificationId);\n },\n [highlightedNotificationId],\n );\n\n useEffect(() => {\n if (!fetchedHighlightedNotification) {\n return;\n }\n\n setHighlightedNotification(fetchedHighlightedNotification);\n if (!fetchedHighlightedNotification.read) {\n setUnreadOnly(true);\n } else {\n setUnreadOnly(false);\n }\n }, [fetchedHighlightedNotification]);\n\n const onUpdate = () => {\n throttledSetRefresh(true);\n };\n\n const notifications = value?.[0]?.notifications;\n const displayedNotifications = useMemo(() => {\n if (\n !highlightedNotification ||\n notifications?.some(\n notification => notification.id === highlightedNotification.id,\n )\n ) {\n return notifications;\n }\n\n // Allow pageSize + 1 so a deep-linked notification can be injected\n // without silently dropping a row that belongs on this page.\n return [highlightedNotification, ...(notifications ?? [])];\n }, [notifications, highlightedNotification]);\n\n const injectedHighlightedNotification =\n highlightedNotification &&\n !notifications?.some(\n notification => notification.id === highlightedNotification.id,\n );\n const displayedPageSize = injectedHighlightedNotification\n ? pageSize + 1\n : pageSize;\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n const totalCount = value?.[0]?.totalCount;\n const isUnread = !!value?.[1]?.unread;\n const allTopics = value?.[2]?.topics;\n\n let tableTitle: string = t(TableTitleKeys.all, {\n count: totalCount ?? 0,\n });\n if (saved) {\n tableTitle = t(TableTitleKeys.saved, {\n count: totalCount ?? 0,\n });\n } else if (unreadOnly === true) {\n tableTitle = t(TableTitleKeys.unread, {\n count: totalCount ?? 0,\n });\n } else if (unreadOnly === false) {\n tableTitle = t(TableTitleKeys.read, {\n count: totalCount ?? 0,\n });\n }\n\n const pageContent = (\n <Content>\n <Grid.Root columns=\"12\" gap=\"6\">\n <Grid.Item colSpan=\"2\">\n <NotificationsFilters\n unreadOnly={unreadOnly}\n onUnreadOnlyChanged={setUnreadOnly}\n createdAfter={createdAfter}\n onCreatedAfterChanged={setCreatedAfter}\n onSortingChanged={setSorting}\n sorting={sorting}\n saved={saved}\n onSavedChanged={setSaved}\n severity={severity}\n onSeverityChanged={setSeverity}\n topic={topic}\n onTopicChanged={setTopic}\n allTopics={allTopics}\n />\n </Grid.Item>\n <Grid.Item colSpan=\"10\">\n <NotificationsTable\n title={tableTitle}\n isLoading={loading}\n isUnread={isUnread}\n markAsReadOnLinkOpen={markAsReadOnLinkOpen}\n notifications={displayedNotifications}\n highlightedNotificationId={highlightedNotificationId}\n onUpdate={onUpdate}\n setContainsText={setContainsText}\n onPageChange={setPageNumber}\n onRowsPerPageChange={setPageSize}\n page={pageNumber}\n pageSize={displayedPageSize}\n totalCount={totalCount}\n />\n </Grid.Item>\n </Grid.Root>\n </Content>\n );\n\n if (headerVariant === 'bui') {\n return pageContent;\n }\n\n return (\n <PageWithHeader\n title={title}\n themeId={themeId}\n tooltip={tooltip}\n subtitle={subtitle}\n type={type}\n typeLink={typeLink}\n >\n {pageContent}\n </PageWithHeader>\n );\n}\n\nexport const NotificationsPage = (props?: NotificationsPageProps) => (\n <NotificationsPageContent {...(props ?? {})} headerVariant=\"legacy\" />\n);\n\nexport const NfsNotificationsPage = (props?: NotificationsPageProps) => (\n <NotificationsPageContent {...(props ?? {})} headerVariant=\"bui\" />\n);\n"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { forwardRef, useMemo, useState, useCallback, useEffect } from 'react';
|
|
3
3
|
import { useNotificationsApi } from '../../hooks/useNotificationsApi.esm.js';
|
|
4
|
+
import { useSignal } from '@backstage/plugin-signals-react';
|
|
4
5
|
import { useWebNotifications } from '../../hooks/useWebNotifications.esm.js';
|
|
5
6
|
import { useTitleCounter } from '../../hooks/useTitleCounter.esm.js';
|
|
6
7
|
import { SidebarItem } from '@backstage/core-components';
|
|
7
8
|
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
|
|
8
9
|
import { toastApiRef } from '@backstage/frontend-plugin-api';
|
|
9
10
|
import { rootRouteRef } from '../../routes.esm.js';
|
|
10
|
-
import { useSignal } from '@backstage/plugin-signals-react';
|
|
11
11
|
import { notificationsApiRef } from '../../api/NotificationsApi.esm.js';
|
|
12
12
|
import '@backstage/errors';
|
|
13
13
|
import { MaterialDesignContent, SnackbarProvider, closeSnackbar, enqueueSnackbar } from 'notistack';
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { useApi, iconsApiRef } from '@backstage/frontend-plugin-api';
|
|
3
3
|
import { SeverityIcon } from './SeverityIcon.esm.js';
|
|
4
4
|
|
|
5
|
+
function NotificationIconWithCustomIcon({
|
|
6
|
+
notification
|
|
7
|
+
}) {
|
|
8
|
+
const iconsApi = useApi(iconsApiRef);
|
|
9
|
+
const Icon = iconsApi.getIcon(notification.payload.icon);
|
|
10
|
+
if (Icon) {
|
|
11
|
+
return /* @__PURE__ */ jsx(Icon, {});
|
|
12
|
+
}
|
|
13
|
+
return /* @__PURE__ */ jsx(SeverityIcon, { severity: notification.payload.severity });
|
|
14
|
+
}
|
|
5
15
|
const NotificationIcon = ({
|
|
6
16
|
notification
|
|
7
17
|
}) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const Icon = app.getSystemIcon(notification.payload.icon);
|
|
11
|
-
if (Icon) {
|
|
12
|
-
return /* @__PURE__ */ jsx(Icon, {});
|
|
13
|
-
}
|
|
18
|
+
if (!notification.payload.icon) {
|
|
19
|
+
return /* @__PURE__ */ jsx(SeverityIcon, { severity: notification.payload.severity });
|
|
14
20
|
}
|
|
15
|
-
return /* @__PURE__ */ jsx(
|
|
21
|
+
return /* @__PURE__ */ jsx(NotificationIconWithCustomIcon, { notification });
|
|
16
22
|
};
|
|
17
23
|
|
|
18
24
|
export { NotificationIcon };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationIcon.esm.js","sources":["../../../src/components/NotificationsTable/NotificationIcon.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 { Notification } from '@backstage/plugin-notifications-common';\nimport {
|
|
1
|
+
{"version":3,"file":"NotificationIcon.esm.js","sources":["../../../src/components/NotificationsTable/NotificationIcon.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 { Notification } from '@backstage/plugin-notifications-common';\nimport { iconsApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { SeverityIcon } from './SeverityIcon';\n\nfunction NotificationIconWithCustomIcon({\n notification,\n}: {\n notification: Notification;\n}) {\n const iconsApi = useApi(iconsApiRef);\n const Icon = iconsApi.getIcon(notification.payload.icon!);\n if (Icon) {\n return <Icon />;\n }\n return <SeverityIcon severity={notification.payload.severity} />;\n}\n\nexport const NotificationIcon = ({\n notification,\n}: {\n notification: Notification;\n}) => {\n if (!notification.payload.icon) {\n return <SeverityIcon severity={notification.payload.severity} />;\n }\n\n return <NotificationIconWithCustomIcon notification={notification} />;\n};\n"],"names":[],"mappings":";;;;AAmBA,SAAS,8BAAA,CAA+B;AAAA,EACtC;AACF,CAAA,EAEG;AACD,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,YAAA,CAAa,QAAQ,IAAK,CAAA;AACxD,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,2BAAQ,IAAA,EAAA,EAAK,CAAA;AAAA,EACf;AACA,EAAA,uBAAO,GAAA,CAAC,YAAA,EAAA,EAAa,QAAA,EAAU,YAAA,CAAa,QAAQ,QAAA,EAAU,CAAA;AAChE;AAEO,MAAM,mBAAmB,CAAC;AAAA,EAC/B;AACF,CAAA,KAEM;AACJ,EAAA,IAAI,CAAC,YAAA,CAAa,OAAA,CAAQ,IAAA,EAAM;AAC9B,IAAA,uBAAO,GAAA,CAAC,YAAA,EAAA,EAAa,QAAA,EAAU,YAAA,CAAa,QAAQ,QAAA,EAAU,CAAA;AAAA,EAChE;AAEA,EAAA,uBAAO,GAAA,CAAC,kCAA+B,YAAA,EAA4B,CAAA;AACrE;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useCallback, useMemo, useEffect } from 'react';
|
|
2
|
+
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
3
3
|
import throttle from 'lodash/throttle';
|
|
4
4
|
import RelativeTime from 'react-relative-time';
|
|
5
5
|
import { Checkbox, Flex, Text, Dialog, DialogHeader, DialogBody, DialogFooter, Button } from '@backstage/ui';
|
|
@@ -23,6 +23,7 @@ const NotificationsTable = ({
|
|
|
23
23
|
markAsReadOnLinkOpen,
|
|
24
24
|
isLoading,
|
|
25
25
|
notifications = [],
|
|
26
|
+
highlightedNotificationId,
|
|
26
27
|
isUnread,
|
|
27
28
|
onUpdate,
|
|
28
29
|
setContainsText,
|
|
@@ -101,6 +102,30 @@ const NotificationsTable = ({
|
|
|
101
102
|
setSelectedNotifications(new Set(intersect));
|
|
102
103
|
}
|
|
103
104
|
}, [notifications, selectedNotifications]);
|
|
105
|
+
const focusedHighlightedIdRef = useRef(void 0);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (!highlightedNotificationId) {
|
|
108
|
+
focusedHighlightedIdRef.current = void 0;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (focusedHighlightedIdRef.current === highlightedNotificationId) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const highlightedElement = document.getElementById(
|
|
115
|
+
`notification-${highlightedNotificationId}`
|
|
116
|
+
);
|
|
117
|
+
if (!highlightedElement) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (typeof highlightedElement.scrollIntoView === "function") {
|
|
121
|
+
highlightedElement.scrollIntoView({
|
|
122
|
+
behavior: "smooth",
|
|
123
|
+
block: "center"
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
highlightedElement.focus({ preventScroll: true });
|
|
127
|
+
focusedHighlightedIdRef.current = highlightedNotificationId;
|
|
128
|
+
}, [highlightedNotificationId, notifications]);
|
|
104
129
|
const compactColumns = useMemo(() => {
|
|
105
130
|
const showToolbar = notifications.length > 0;
|
|
106
131
|
return [
|
|
@@ -133,47 +158,56 @@ const NotificationsTable = ({
|
|
|
133
158
|
customFilterAndSearch: () => true,
|
|
134
159
|
cellStyle: { paddingLeft: 0 },
|
|
135
160
|
render: (notification) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
161
|
+
const isHighlighted = highlightedNotificationId === notification.id;
|
|
162
|
+
return /* @__PURE__ */ jsx(
|
|
163
|
+
"div",
|
|
164
|
+
{
|
|
165
|
+
id: `notification-${notification.id}`,
|
|
166
|
+
tabIndex: isHighlighted ? -1 : void 0,
|
|
167
|
+
className: isHighlighted ? styles.highlightedNotification : void 0,
|
|
168
|
+
children: /* @__PURE__ */ jsxs(Flex, { gap: "4", align: "center", children: [
|
|
169
|
+
/* @__PURE__ */ jsx("div", { className: styles.severityItem, children: /* @__PURE__ */ jsx(NotificationIcon, { notification }) }),
|
|
170
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
|
|
171
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-medium", children: notification.payload.link ? /* @__PURE__ */ jsx(
|
|
172
|
+
Link,
|
|
173
|
+
{
|
|
174
|
+
to: notification.payload.link,
|
|
175
|
+
onClick: () => {
|
|
176
|
+
if (markAsReadOnLinkOpen && !notification.read) {
|
|
177
|
+
onSwitchReadStatus([notification.id], true);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
children: notification.payload.title
|
|
181
|
+
}
|
|
182
|
+
) : notification.payload.title }),
|
|
183
|
+
notification.payload.description ? /* @__PURE__ */ jsx(
|
|
184
|
+
NotificationDescription,
|
|
185
|
+
{
|
|
186
|
+
description: notification.payload.description
|
|
146
187
|
}
|
|
147
|
-
|
|
148
|
-
children:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
] })
|
|
167
|
-
notification.created && /* @__PURE__ */ jsx(
|
|
168
|
-
RelativeTime,
|
|
169
|
-
{
|
|
170
|
-
value: notification.created,
|
|
171
|
-
className: styles.notificationInfoRow
|
|
172
|
-
}
|
|
173
|
-
)
|
|
188
|
+
) : null,
|
|
189
|
+
/* @__PURE__ */ jsxs(Text, { variant: "body-small", color: "secondary", children: [
|
|
190
|
+
!notification.user && /* @__PURE__ */ jsx(RiRssFill, { size: 14, className: styles.broadcastIcon }),
|
|
191
|
+
notification.origin && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
192
|
+
/* @__PURE__ */ jsx("span", { className: styles.notificationInfoRow, children: notification.origin }),
|
|
193
|
+
"\u2022"
|
|
194
|
+
] }),
|
|
195
|
+
notification.payload.topic && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
196
|
+
/* @__PURE__ */ jsx("span", { className: styles.notificationInfoRow, children: notification.payload.topic }),
|
|
197
|
+
"\u2022"
|
|
198
|
+
] }),
|
|
199
|
+
notification.created && /* @__PURE__ */ jsx(
|
|
200
|
+
RelativeTime,
|
|
201
|
+
{
|
|
202
|
+
value: notification.created,
|
|
203
|
+
className: styles.notificationInfoRow
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
] })
|
|
207
|
+
] })
|
|
174
208
|
] })
|
|
175
|
-
|
|
176
|
-
|
|
209
|
+
}
|
|
210
|
+
);
|
|
177
211
|
}
|
|
178
212
|
},
|
|
179
213
|
{
|
|
@@ -209,7 +243,8 @@ const NotificationsTable = ({
|
|
|
209
243
|
onSwitchSavedStatus,
|
|
210
244
|
onMarkAllRead,
|
|
211
245
|
onNotificationsSelectChange,
|
|
212
|
-
markAsReadOnLinkOpen
|
|
246
|
+
markAsReadOnLinkOpen,
|
|
247
|
+
highlightedNotificationId
|
|
213
248
|
]);
|
|
214
249
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
215
250
|
/* @__PURE__ */ jsx(
|