@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsTable.esm.js","sources":["../../../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 { useCallback, useEffect, useMemo, useState } from 'react';\nimport throttle from 'lodash/throttle';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport {\n Button,\n Checkbox,\n Dialog,\n DialogBody,\n DialogFooter,\n DialogHeader,\n Flex,\n Text,\n} from '@backstage/ui';\nimport { RiRssFill } from '@remixicon/react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { toastApiRef } from '@backstage/frontend-plugin-api';\nimport {\n Link,\n Table,\n TableColumn,\n TableProps,\n} from '@backstage/core-components';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { notificationsTranslationRef } from '../../translation';\n\nimport { notificationsApiRef } from '../../api';\nimport { SelectAll } from './SelectAll';\nimport { BulkActions } from './BulkActions';\nimport { NotificationIcon } from './NotificationIcon';\nimport { NotificationDescription } from './NotificationDescription';\n\nimport styles from './NotificationsTable.module.css';\n\nconst ThrottleDelayMs = 1000;\n\n/** @public */\nexport type NotificationsTableProps = Pick<\n TableProps,\n 'onPageChange' | 'onRowsPerPageChange' | 'page' | 'totalCount' | 'title'\n> & {\n markAsReadOnLinkOpen?: boolean;\n isLoading?: boolean;\n isUnread: boolean;\n notifications?: Notification[];\n onUpdate: () => void;\n setContainsText: (search: string) => void;\n pageSize: number;\n};\n\n/** @public */\nexport const NotificationsTable = ({\n title,\n markAsReadOnLinkOpen,\n isLoading,\n notifications = [],\n isUnread,\n onUpdate,\n setContainsText,\n onPageChange,\n onRowsPerPageChange,\n page,\n pageSize,\n totalCount,\n}: NotificationsTableProps) => {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const notificationsApi = useApi(notificationsApiRef);\n const toastApi = useApi(toastApiRef);\n const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);\n\n const [selectedNotifications, setSelectedNotifications] = useState(\n new Set<Notification['id']>(),\n );\n\n const onNotificationsSelectChange = useCallback(\n (ids: Notification['id'][], checked: boolean) => {\n let newSelect: Set<Notification['id']>;\n if (checked) {\n newSelect = new Set([...selectedNotifications, ...ids]);\n } else {\n newSelect = new Set(selectedNotifications);\n ids.forEach(id => newSelect.delete(id));\n }\n setSelectedNotifications(newSelect);\n },\n [selectedNotifications, setSelectedNotifications],\n );\n\n const onSwitchReadStatus = useCallback(\n (ids: Notification['id'][], newStatus: boolean) => {\n notificationsApi\n .updateNotifications({\n ids,\n read: newStatus,\n })\n .then(onUpdate);\n },\n [notificationsApi, onUpdate],\n );\n\n const onSwitchSavedStatus = useCallback(\n (ids: Notification['id'][], newStatus: boolean) => {\n notificationsApi\n .updateNotifications({\n ids,\n saved: newStatus,\n })\n .then(onUpdate);\n },\n [notificationsApi, onUpdate],\n );\n\n const doMarkAllRead = useCallback(async () => {\n setConfirmDialogOpen(false);\n try {\n const result = await notificationsApi.getNotifications({ read: false });\n const ids =\n result.notifications?.map(notification => notification.id) ?? [];\n if (ids.length === 0) return;\n await notificationsApi.updateNotifications({ ids, read: true });\n onUpdate();\n } catch {\n toastApi.post({\n title: t('table.errors.markAllReadFailed'),\n status: 'danger',\n });\n }\n }, [notificationsApi, onUpdate, toastApi, t]);\n\n const onMarkAllRead = useCallback(() => {\n setConfirmDialogOpen(true);\n }, []);\n\n const throttledContainsTextHandler = useMemo(\n () => throttle(setContainsText, ThrottleDelayMs),\n [setContainsText],\n );\n\n useEffect(() => {\n const allShownIds = new Set(notifications.map(n => n.id));\n const intersect = [...selectedNotifications].filter(id =>\n allShownIds.has(id),\n );\n if (selectedNotifications.size !== intersect.length) {\n setSelectedNotifications(new Set(intersect));\n }\n }, [notifications, selectedNotifications]);\n\n const compactColumns = useMemo((): TableColumn<Notification>[] => {\n const showToolbar = notifications.length > 0;\n return [\n {\n /* selection column */\n width: '1rem',\n cellStyle: { paddingRight: '2.5rem' },\n title: showToolbar ? (\n <SelectAll\n count={selectedNotifications.size}\n totalCount={notifications.length}\n onSelectAll={() =>\n onNotificationsSelectChange(\n notifications.map(notification => notification.id),\n selectedNotifications.size !== notifications.length,\n )\n }\n />\n ) : undefined,\n render: (notification: Notification) => (\n <Checkbox\n aria-label=\"Select notification\"\n isSelected={selectedNotifications.has(notification.id)}\n onChange={checked =>\n onNotificationsSelectChange([notification.id], checked)\n }\n />\n ),\n },\n {\n /* compact-data column */\n customFilterAndSearch: () =>\n true /* Keep sorting&filtering on backend due to pagination. */,\n cellStyle: { paddingLeft: 0 },\n render: (notification: Notification) => {\n // Compact content\n return (\n <Flex gap=\"4\" align=\"center\">\n <div className={styles.severityItem}>\n <NotificationIcon notification={notification} />\n </div>\n <Flex direction=\"column\" gap=\"1\">\n <Text variant=\"body-medium\">\n {notification.payload.link ? (\n <Link\n to={notification.payload.link}\n onClick={() => {\n if (markAsReadOnLinkOpen && !notification.read) {\n onSwitchReadStatus([notification.id], true);\n }\n }}\n >\n {notification.payload.title}\n </Link>\n ) : (\n notification.payload.title\n )}\n </Text>\n {notification.payload.description ? (\n <NotificationDescription\n description={notification.payload.description}\n />\n ) : null}\n\n <Text variant=\"body-small\" color=\"secondary\">\n {!notification.user && (\n <RiRssFill size={14} className={styles.broadcastIcon} />\n )}\n {notification.origin && (\n <>\n <span className={styles.notificationInfoRow}>\n {notification.origin}\n </span>\n •\n </>\n )}\n {notification.payload.topic && (\n <>\n <span className={styles.notificationInfoRow}>\n {notification.payload.topic}\n </span>\n •\n </>\n )}\n {notification.created && (\n <RelativeTime\n value={notification.created}\n className={styles.notificationInfoRow}\n />\n )}\n </Text>\n </Flex>\n </Flex>\n );\n },\n },\n {\n /* actions column */\n width: '1rem',\n title: showToolbar ? (\n <BulkActions\n notifications={notifications}\n selectedNotifications={selectedNotifications}\n isUnread={isUnread}\n onSwitchReadStatus={onSwitchReadStatus}\n onSwitchSavedStatus={onSwitchSavedStatus}\n onMarkAllRead={onMarkAllRead}\n />\n ) : undefined,\n render: (notification: Notification) => (\n <BulkActions\n notifications={[notification]}\n selectedNotifications={new Set([notification.id])}\n onSwitchReadStatus={onSwitchReadStatus}\n onSwitchSavedStatus={onSwitchSavedStatus}\n />\n ),\n },\n ];\n }, [\n notifications,\n selectedNotifications,\n isUnread,\n onSwitchReadStatus,\n onSwitchSavedStatus,\n onMarkAllRead,\n onNotificationsSelectChange,\n markAsReadOnLinkOpen,\n ]);\n\n return (\n <>\n <Table<Notification>\n isLoading={isLoading}\n options={{\n padding: 'dense',\n search: true,\n paging: true,\n pageSize,\n header: true,\n sorting: false,\n }}\n title={title}\n onPageChange={onPageChange}\n onRowsPerPageChange={onRowsPerPageChange}\n page={page}\n totalCount={totalCount}\n onSearchChange={throttledContainsTextHandler}\n data={notifications}\n columns={compactColumns}\n localization={{\n body: {\n emptyDataSourceMessage: t('table.emptyMessage'),\n },\n pagination: {\n firstTooltip: t('table.pagination.firstTooltip'),\n labelDisplayedRows: t('table.pagination.labelDisplayedRows'),\n labelRowsSelect: t('table.pagination.labelRowsSelect'),\n lastTooltip: t('table.pagination.lastTooltip'),\n nextTooltip: t('table.pagination.nextTooltip'),\n previousTooltip: t('table.pagination.previousTooltip'),\n },\n }}\n />\n <Dialog\n isOpen={confirmDialogOpen}\n onOpenChange={setConfirmDialogOpen}\n isDismissable\n >\n <DialogHeader>{t('table.confirmDialog.title')}</DialogHeader>\n <DialogBody>\n <Text variant=\"body-medium\">\n {t('table.confirmDialog.markAllReadDescription')}\n </Text>\n </DialogBody>\n <DialogFooter>\n <Button variant=\"primary\" onPress={doMarkAllRead}>\n {t('table.confirmDialog.markAllReadConfirmation')}\n </Button>\n <Button variant=\"secondary\" slot=\"close\">\n {t('table.confirmDialog.cancel')}\n </Button>\n </DialogFooter>\n </Dialog>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NotificationsTable.esm.js","sources":["../../../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 { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport throttle from 'lodash/throttle';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport {\n Button,\n Checkbox,\n Dialog,\n DialogBody,\n DialogFooter,\n DialogHeader,\n Flex,\n Text,\n} from '@backstage/ui';\nimport { RiRssFill } from '@remixicon/react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { toastApiRef } from '@backstage/frontend-plugin-api';\nimport {\n Link,\n Table,\n TableColumn,\n TableProps,\n} from '@backstage/core-components';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { notificationsTranslationRef } from '../../translation';\n\nimport { notificationsApiRef } from '../../api';\nimport { SelectAll } from './SelectAll';\nimport { BulkActions } from './BulkActions';\nimport { NotificationIcon } from './NotificationIcon';\nimport { NotificationDescription } from './NotificationDescription';\n\nimport styles from './NotificationsTable.module.css';\n\nconst ThrottleDelayMs = 1000;\n\n/** @public */\nexport type NotificationsTableProps = Pick<\n TableProps,\n 'onPageChange' | 'onRowsPerPageChange' | 'page' | 'totalCount' | 'title'\n> & {\n markAsReadOnLinkOpen?: boolean;\n isLoading?: boolean;\n isUnread: boolean;\n notifications?: Notification[];\n highlightedNotificationId?: string;\n onUpdate: () => void;\n setContainsText: (search: string) => void;\n pageSize: number;\n};\n\n/** @public */\nexport const NotificationsTable = ({\n title,\n markAsReadOnLinkOpen,\n isLoading,\n notifications = [],\n highlightedNotificationId,\n isUnread,\n onUpdate,\n setContainsText,\n onPageChange,\n onRowsPerPageChange,\n page,\n pageSize,\n totalCount,\n}: NotificationsTableProps) => {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const notificationsApi = useApi(notificationsApiRef);\n const toastApi = useApi(toastApiRef);\n const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);\n\n const [selectedNotifications, setSelectedNotifications] = useState(\n new Set<Notification['id']>(),\n );\n\n const onNotificationsSelectChange = useCallback(\n (ids: Notification['id'][], checked: boolean) => {\n let newSelect: Set<Notification['id']>;\n if (checked) {\n newSelect = new Set([...selectedNotifications, ...ids]);\n } else {\n newSelect = new Set(selectedNotifications);\n ids.forEach(id => newSelect.delete(id));\n }\n setSelectedNotifications(newSelect);\n },\n [selectedNotifications, setSelectedNotifications],\n );\n\n const onSwitchReadStatus = useCallback(\n (ids: Notification['id'][], newStatus: boolean) => {\n notificationsApi\n .updateNotifications({\n ids,\n read: newStatus,\n })\n .then(onUpdate);\n },\n [notificationsApi, onUpdate],\n );\n\n const onSwitchSavedStatus = useCallback(\n (ids: Notification['id'][], newStatus: boolean) => {\n notificationsApi\n .updateNotifications({\n ids,\n saved: newStatus,\n })\n .then(onUpdate);\n },\n [notificationsApi, onUpdate],\n );\n\n const doMarkAllRead = useCallback(async () => {\n setConfirmDialogOpen(false);\n try {\n const result = await notificationsApi.getNotifications({ read: false });\n const ids =\n result.notifications?.map(notification => notification.id) ?? [];\n if (ids.length === 0) return;\n await notificationsApi.updateNotifications({ ids, read: true });\n onUpdate();\n } catch {\n toastApi.post({\n title: t('table.errors.markAllReadFailed'),\n status: 'danger',\n });\n }\n }, [notificationsApi, onUpdate, toastApi, t]);\n\n const onMarkAllRead = useCallback(() => {\n setConfirmDialogOpen(true);\n }, []);\n\n const throttledContainsTextHandler = useMemo(\n () => throttle(setContainsText, ThrottleDelayMs),\n [setContainsText],\n );\n\n useEffect(() => {\n const allShownIds = new Set(notifications.map(n => n.id));\n const intersect = [...selectedNotifications].filter(id =>\n allShownIds.has(id),\n );\n if (selectedNotifications.size !== intersect.length) {\n setSelectedNotifications(new Set(intersect));\n }\n }, [notifications, selectedNotifications]);\n\n const focusedHighlightedIdRef = useRef<string | undefined>(undefined);\n\n useEffect(() => {\n if (!highlightedNotificationId) {\n focusedHighlightedIdRef.current = undefined;\n return;\n }\n\n // Only scroll/focus once per deep-linked id so refreshes don't steal focus.\n if (focusedHighlightedIdRef.current === highlightedNotificationId) {\n return;\n }\n\n const highlightedElement = document.getElementById(\n `notification-${highlightedNotificationId}`,\n );\n if (!highlightedElement) {\n return;\n }\n\n if (typeof highlightedElement.scrollIntoView === 'function') {\n highlightedElement.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n });\n }\n highlightedElement.focus({ preventScroll: true });\n focusedHighlightedIdRef.current = highlightedNotificationId;\n }, [highlightedNotificationId, notifications]);\n\n const compactColumns = useMemo((): TableColumn<Notification>[] => {\n const showToolbar = notifications.length > 0;\n return [\n {\n /* selection column */\n width: '1rem',\n cellStyle: { paddingRight: '2.5rem' },\n title: showToolbar ? (\n <SelectAll\n count={selectedNotifications.size}\n totalCount={notifications.length}\n onSelectAll={() =>\n onNotificationsSelectChange(\n notifications.map(notification => notification.id),\n selectedNotifications.size !== notifications.length,\n )\n }\n />\n ) : undefined,\n render: (notification: Notification) => (\n <Checkbox\n aria-label=\"Select notification\"\n isSelected={selectedNotifications.has(notification.id)}\n onChange={checked =>\n onNotificationsSelectChange([notification.id], checked)\n }\n />\n ),\n },\n {\n /* compact-data column */\n customFilterAndSearch: () =>\n true /* Keep sorting&filtering on backend due to pagination. */,\n cellStyle: { paddingLeft: 0 },\n render: (notification: Notification) => {\n const isHighlighted = highlightedNotificationId === notification.id;\n // Compact content\n return (\n <div\n id={`notification-${notification.id}`}\n tabIndex={isHighlighted ? -1 : undefined}\n className={\n isHighlighted ? styles.highlightedNotification : undefined\n }\n >\n <Flex gap=\"4\" align=\"center\">\n <div className={styles.severityItem}>\n <NotificationIcon notification={notification} />\n </div>\n <Flex direction=\"column\" gap=\"1\">\n <Text variant=\"body-medium\">\n {notification.payload.link ? (\n <Link\n to={notification.payload.link}\n onClick={() => {\n if (markAsReadOnLinkOpen && !notification.read) {\n onSwitchReadStatus([notification.id], true);\n }\n }}\n >\n {notification.payload.title}\n </Link>\n ) : (\n notification.payload.title\n )}\n </Text>\n {notification.payload.description ? (\n <NotificationDescription\n description={notification.payload.description}\n />\n ) : null}\n\n <Text variant=\"body-small\" color=\"secondary\">\n {!notification.user && (\n <RiRssFill size={14} className={styles.broadcastIcon} />\n )}\n {notification.origin && (\n <>\n <span className={styles.notificationInfoRow}>\n {notification.origin}\n </span>\n •\n </>\n )}\n {notification.payload.topic && (\n <>\n <span className={styles.notificationInfoRow}>\n {notification.payload.topic}\n </span>\n •\n </>\n )}\n {notification.created && (\n <RelativeTime\n value={notification.created}\n className={styles.notificationInfoRow}\n />\n )}\n </Text>\n </Flex>\n </Flex>\n </div>\n );\n },\n },\n {\n /* actions column */\n width: '1rem',\n title: showToolbar ? (\n <BulkActions\n notifications={notifications}\n selectedNotifications={selectedNotifications}\n isUnread={isUnread}\n onSwitchReadStatus={onSwitchReadStatus}\n onSwitchSavedStatus={onSwitchSavedStatus}\n onMarkAllRead={onMarkAllRead}\n />\n ) : undefined,\n render: (notification: Notification) => (\n <BulkActions\n notifications={[notification]}\n selectedNotifications={new Set([notification.id])}\n onSwitchReadStatus={onSwitchReadStatus}\n onSwitchSavedStatus={onSwitchSavedStatus}\n />\n ),\n },\n ];\n }, [\n notifications,\n selectedNotifications,\n isUnread,\n onSwitchReadStatus,\n onSwitchSavedStatus,\n onMarkAllRead,\n onNotificationsSelectChange,\n markAsReadOnLinkOpen,\n highlightedNotificationId,\n ]);\n\n return (\n <>\n <Table<Notification>\n isLoading={isLoading}\n options={{\n padding: 'dense',\n search: true,\n paging: true,\n pageSize,\n header: true,\n sorting: false,\n }}\n title={title}\n onPageChange={onPageChange}\n onRowsPerPageChange={onRowsPerPageChange}\n page={page}\n totalCount={totalCount}\n onSearchChange={throttledContainsTextHandler}\n data={notifications}\n columns={compactColumns}\n localization={{\n body: {\n emptyDataSourceMessage: t('table.emptyMessage'),\n },\n pagination: {\n firstTooltip: t('table.pagination.firstTooltip'),\n labelDisplayedRows: t('table.pagination.labelDisplayedRows'),\n labelRowsSelect: t('table.pagination.labelRowsSelect'),\n lastTooltip: t('table.pagination.lastTooltip'),\n nextTooltip: t('table.pagination.nextTooltip'),\n previousTooltip: t('table.pagination.previousTooltip'),\n },\n }}\n />\n <Dialog\n isOpen={confirmDialogOpen}\n onOpenChange={setConfirmDialogOpen}\n isDismissable\n >\n <DialogHeader>{t('table.confirmDialog.title')}</DialogHeader>\n <DialogBody>\n <Text variant=\"body-medium\">\n {t('table.confirmDialog.markAllReadDescription')}\n </Text>\n </DialogBody>\n <DialogFooter>\n <Button variant=\"primary\" onPress={doMarkAllRead}>\n {t('table.confirmDialog.markAllReadConfirmation')}\n </Button>\n <Button variant=\"secondary\" slot=\"close\">\n {t('table.confirmDialog.cancel')}\n </Button>\n </DialogFooter>\n </Dialog>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
2
|
|
|
3
|
-
var css_248z = "/*\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\n@layer components {\n .
|
|
4
|
-
var styles = {"severityItem":"
|
|
3
|
+
var css_248z = "/*\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\n@layer components {\n .NotificationsTable_severityItem__ec8df2aec9 {\n align-content: center;\n display: flex;\n align-items: center;\n }\n\n .NotificationsTable_broadcastIcon__ec8df2aec9 {\n font-size: 1rem;\n vertical-align: text-bottom;\n }\n\n .NotificationsTable_notificationInfoRow__ec8df2aec9 {\n margin-right: var(--bui-space-1);\n }\n\n .NotificationsTable_notificationInfoRow__ec8df2aec9:not(:first-child) {\n margin-left: var(--bui-space-1);\n }\n\n .NotificationsTable_highlightedNotification__ec8df2aec9 {\n background-color: var(--bui-color-bg-surface-hover, rgba(0, 0, 0, 0.04));\n border-radius: var(--bui-radius-2, 4px);\n padding: var(--bui-space-2, 8px);\n margin: calc(var(--bui-space-2, 8px) * -1);\n outline: 2px solid var(--bui-color-border-primary, #1f5493);\n outline-offset: var(--bui-space-1, 4px);\n }\n}\n";
|
|
4
|
+
var styles = {"severityItem":"NotificationsTable_severityItem__ec8df2aec9","broadcastIcon":"NotificationsTable_broadcastIcon__ec8df2aec9","notificationInfoRow":"NotificationsTable_notificationInfoRow__ec8df2aec9","highlightedNotification":"NotificationsTable_highlightedNotification__ec8df2aec9"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { styles as default };
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useMemo, useEffect, useCallback } from 'react';
|
|
3
|
+
import throttle from 'lodash/throttle';
|
|
4
|
+
import RelativeTime from 'react-relative-time';
|
|
5
|
+
import { RiCheckDoubleLine, RiRssFill } from '@remixicon/react';
|
|
6
|
+
import { InfoCard, Progress, ErrorPanel, LinkButton, Link } from '@backstage/core-components';
|
|
7
|
+
import { Text } from '@backstage/ui';
|
|
8
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
9
|
+
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
|
10
|
+
import { notificationsApiRef } from '../../api/NotificationsApi.esm.js';
|
|
11
|
+
import '@backstage/errors';
|
|
12
|
+
import { useNotificationsApi } from '../../hooks/useNotificationsApi.esm.js';
|
|
13
|
+
import { useNotificationsRefresh } from '../../hooks/useNotificationsRefresh.esm.js';
|
|
14
|
+
import '../../routes.esm.js';
|
|
15
|
+
import '../../hooks/useTitleCounter.esm.js';
|
|
16
|
+
import { notificationsTranslationRef } from '../../translation.esm.js';
|
|
17
|
+
import { getNotificationsPageLink } from '../../utils/notificationLinks.esm.js';
|
|
18
|
+
import { truncateText } from '../../utils/plainText.esm.js';
|
|
19
|
+
import { BulkActions } from '../NotificationsTable/BulkActions.esm.js';
|
|
20
|
+
import { NotificationIcon } from '../NotificationsTable/NotificationIcon.esm.js';
|
|
21
|
+
import styles from './UnreadNotificationsCard.module.css.esm.js';
|
|
22
|
+
|
|
23
|
+
const ThrottleDelayMs = 2e3;
|
|
24
|
+
const resolveMaxMessages = ({
|
|
25
|
+
maxMessages,
|
|
26
|
+
initialCount
|
|
27
|
+
}) => maxMessages ?? initialCount ?? 5;
|
|
28
|
+
const resolveCharLimit = ({
|
|
29
|
+
charLimit,
|
|
30
|
+
maxChars
|
|
31
|
+
}) => charLimit ?? maxChars ?? 80;
|
|
32
|
+
const NotificationListItem = ({
|
|
33
|
+
notification,
|
|
34
|
+
maxChars,
|
|
35
|
+
descriptionMaxChars,
|
|
36
|
+
onSwitchReadStatus,
|
|
37
|
+
onSwitchSavedStatus
|
|
38
|
+
}) => {
|
|
39
|
+
const {
|
|
40
|
+
display: titleDisplay,
|
|
41
|
+
truncated: titleTruncated,
|
|
42
|
+
full: titleFull
|
|
43
|
+
} = truncateText(notification.payload.title, maxChars);
|
|
44
|
+
const description = notification.payload.description ? truncateText(notification.payload.description, descriptionMaxChars) : void 0;
|
|
45
|
+
return /* @__PURE__ */ jsxs("div", { className: styles.notificationRow, children: [
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: styles.notificationContent, children: [
|
|
47
|
+
/* @__PURE__ */ jsx("div", { className: styles.severityItem, children: /* @__PURE__ */ jsx(NotificationIcon, { notification }) }),
|
|
48
|
+
/* @__PURE__ */ jsxs("div", { className: styles.notificationText, children: [
|
|
49
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-medium", className: styles.notificationTitle, children: /* @__PURE__ */ jsx(
|
|
50
|
+
Link,
|
|
51
|
+
{
|
|
52
|
+
to: getNotificationsPageLink(notification.id),
|
|
53
|
+
title: titleTruncated ? titleFull : void 0,
|
|
54
|
+
children: titleDisplay
|
|
55
|
+
}
|
|
56
|
+
) }),
|
|
57
|
+
description ? /* @__PURE__ */ jsx(
|
|
58
|
+
Text,
|
|
59
|
+
{
|
|
60
|
+
variant: "body-small",
|
|
61
|
+
color: "secondary",
|
|
62
|
+
className: styles.notificationDescription,
|
|
63
|
+
title: description.truncated ? description.full : void 0,
|
|
64
|
+
children: description.display
|
|
65
|
+
}
|
|
66
|
+
) : null,
|
|
67
|
+
/* @__PURE__ */ jsxs(
|
|
68
|
+
Text,
|
|
69
|
+
{
|
|
70
|
+
variant: "body-small",
|
|
71
|
+
color: "secondary",
|
|
72
|
+
className: styles.metaRow,
|
|
73
|
+
children: [
|
|
74
|
+
!notification.user && /* @__PURE__ */ jsx(RiRssFill, { size: 14, className: styles.broadcastIcon }),
|
|
75
|
+
notification.origin && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: styles.notificationInfoRow, children: notification.origin }),
|
|
77
|
+
"\u2022"
|
|
78
|
+
] }),
|
|
79
|
+
notification.payload.topic && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: styles.notificationInfoRow, children: notification.payload.topic }),
|
|
81
|
+
"\u2022"
|
|
82
|
+
] }),
|
|
83
|
+
notification.created && /* @__PURE__ */ jsx(
|
|
84
|
+
RelativeTime,
|
|
85
|
+
{
|
|
86
|
+
value: notification.created,
|
|
87
|
+
className: styles.notificationInfoRow
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
] })
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ jsx("div", { className: styles.actionsColumn, children: /* @__PURE__ */ jsx(
|
|
96
|
+
BulkActions,
|
|
97
|
+
{
|
|
98
|
+
notifications: [notification],
|
|
99
|
+
selectedNotifications: /* @__PURE__ */ new Set([notification.id]),
|
|
100
|
+
onSwitchReadStatus,
|
|
101
|
+
onSwitchSavedStatus
|
|
102
|
+
}
|
|
103
|
+
) })
|
|
104
|
+
] });
|
|
105
|
+
};
|
|
106
|
+
const UnreadNotificationsCard = ({
|
|
107
|
+
maxMessages,
|
|
108
|
+
charLimit,
|
|
109
|
+
initialCount,
|
|
110
|
+
maxChars,
|
|
111
|
+
descriptionMaxChars = 120
|
|
112
|
+
}) => {
|
|
113
|
+
const { t } = useTranslationRef(notificationsTranslationRef);
|
|
114
|
+
const resolvedMaxMessages = resolveMaxMessages({ maxMessages, initialCount });
|
|
115
|
+
const resolvedCharLimit = resolveCharLimit({ charLimit, maxChars });
|
|
116
|
+
const [refresh, setRefresh] = useState(false);
|
|
117
|
+
const { lastSignal, pollTick } = useNotificationsRefresh();
|
|
118
|
+
const notificationsApi = useApi(notificationsApiRef);
|
|
119
|
+
const { error, value, retry, loading } = useNotificationsApi(
|
|
120
|
+
(api) => api.getNotifications({
|
|
121
|
+
limit: resolvedMaxMessages,
|
|
122
|
+
read: false,
|
|
123
|
+
sort: "created",
|
|
124
|
+
sortOrder: "desc"
|
|
125
|
+
}),
|
|
126
|
+
[resolvedMaxMessages]
|
|
127
|
+
);
|
|
128
|
+
const throttledSetRefresh = useMemo(
|
|
129
|
+
() => throttle(() => setRefresh(true), ThrottleDelayMs),
|
|
130
|
+
[]
|
|
131
|
+
);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
return () => {
|
|
134
|
+
throttledSetRefresh.cancel();
|
|
135
|
+
};
|
|
136
|
+
}, [throttledSetRefresh]);
|
|
137
|
+
const onUpdate = useCallback(() => {
|
|
138
|
+
retry();
|
|
139
|
+
}, [retry]);
|
|
140
|
+
const onSwitchReadStatus = useCallback(
|
|
141
|
+
(ids, newStatus) => {
|
|
142
|
+
notificationsApi.updateNotifications({
|
|
143
|
+
ids,
|
|
144
|
+
read: newStatus
|
|
145
|
+
}).then(onUpdate);
|
|
146
|
+
},
|
|
147
|
+
[notificationsApi, onUpdate]
|
|
148
|
+
);
|
|
149
|
+
const onSwitchSavedStatus = useCallback(
|
|
150
|
+
(ids, newStatus) => {
|
|
151
|
+
notificationsApi.updateNotifications({
|
|
152
|
+
ids,
|
|
153
|
+
saved: newStatus
|
|
154
|
+
}).then(onUpdate);
|
|
155
|
+
},
|
|
156
|
+
[notificationsApi, onUpdate]
|
|
157
|
+
);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (refresh && !loading) {
|
|
160
|
+
retry();
|
|
161
|
+
setRefresh(false);
|
|
162
|
+
}
|
|
163
|
+
}, [refresh, retry, loading]);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!lastSignal?.action) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (lastSignal.action === "new_notification" || lastSignal.action === "notification_read" || lastSignal.action === "notification_unread") {
|
|
169
|
+
throttledSetRefresh();
|
|
170
|
+
}
|
|
171
|
+
}, [lastSignal, throttledSetRefresh]);
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (pollTick > 0) {
|
|
174
|
+
throttledSetRefresh();
|
|
175
|
+
}
|
|
176
|
+
}, [pollTick, throttledSetRefresh]);
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
const handleVisibilityChange = () => {
|
|
179
|
+
if (document.visibilityState === "visible") {
|
|
180
|
+
throttledSetRefresh();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
184
|
+
return () => {
|
|
185
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
186
|
+
};
|
|
187
|
+
}, [throttledSetRefresh]);
|
|
188
|
+
const notifications = value?.notifications ?? [];
|
|
189
|
+
const totalCount = value?.totalCount ?? notifications.length;
|
|
190
|
+
const displayedCount = notifications.length;
|
|
191
|
+
return /* @__PURE__ */ jsx(
|
|
192
|
+
InfoCard,
|
|
193
|
+
{
|
|
194
|
+
title: t("unreadCard.title", { count: totalCount }),
|
|
195
|
+
action: /* @__PURE__ */ jsx(LinkButton, { to: getNotificationsPageLink(), color: "primary", children: t("unreadCard.viewAll") }),
|
|
196
|
+
variant: "gridItem",
|
|
197
|
+
children: /* @__PURE__ */ jsxs("div", { className: styles.content, children: [
|
|
198
|
+
loading && /* @__PURE__ */ jsx(Progress, {}),
|
|
199
|
+
error && /* @__PURE__ */ jsx(ErrorPanel, { error }),
|
|
200
|
+
!loading && !error && /* @__PURE__ */ jsx(Fragment, { children: notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: styles.emptyState, children: [
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
RiCheckDoubleLine,
|
|
203
|
+
{
|
|
204
|
+
size: 32,
|
|
205
|
+
className: styles.emptyStateIcon,
|
|
206
|
+
"aria-hidden": true,
|
|
207
|
+
"data-testid": "unread-notifications-empty-icon"
|
|
208
|
+
}
|
|
209
|
+
),
|
|
210
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-medium", children: t("unreadCard.emptyState") })
|
|
211
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
212
|
+
/* @__PURE__ */ jsxs("div", { className: styles.columnHeader, children: [
|
|
213
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-x-small", color: "secondary", children: t("unreadCard.columnNotification") }),
|
|
214
|
+
/* @__PURE__ */ jsx(
|
|
215
|
+
Text,
|
|
216
|
+
{
|
|
217
|
+
variant: "body-x-small",
|
|
218
|
+
color: "secondary",
|
|
219
|
+
className: styles.columnHeaderActions,
|
|
220
|
+
children: t("unreadCard.columnActions")
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
] }),
|
|
224
|
+
notifications.map((notification) => /* @__PURE__ */ jsx(
|
|
225
|
+
NotificationListItem,
|
|
226
|
+
{
|
|
227
|
+
notification,
|
|
228
|
+
maxChars: resolvedCharLimit,
|
|
229
|
+
descriptionMaxChars,
|
|
230
|
+
onSwitchReadStatus,
|
|
231
|
+
onSwitchSavedStatus
|
|
232
|
+
},
|
|
233
|
+
notification.id
|
|
234
|
+
)),
|
|
235
|
+
/* @__PURE__ */ jsx(
|
|
236
|
+
Text,
|
|
237
|
+
{
|
|
238
|
+
variant: "body-small",
|
|
239
|
+
color: "secondary",
|
|
240
|
+
className: styles.resultsCount,
|
|
241
|
+
children: t("unreadCard.resultsCount", {
|
|
242
|
+
displayed: String(displayedCount),
|
|
243
|
+
total: String(totalCount)
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
] }) })
|
|
248
|
+
] })
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
};
|
|
252
|
+
const UnreadNotificationsCardWithProvider = (props) => /* @__PURE__ */ jsx(UnreadNotificationsCard, { ...props });
|
|
253
|
+
|
|
254
|
+
export { UnreadNotificationsCard, UnreadNotificationsCardWithProvider };
|
|
255
|
+
//# sourceMappingURL=UnreadNotificationsCard.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnreadNotificationsCard.esm.js","sources":["../../../src/components/UnreadNotificationsCard/UnreadNotificationsCard.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 { useCallback, useEffect, useMemo, useState } from 'react';\nimport throttle from 'lodash/throttle';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { RiCheckDoubleLine, RiRssFill } from '@remixicon/react';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport {\n ErrorPanel,\n InfoCard,\n Link,\n LinkButton,\n Progress,\n} from '@backstage/core-components';\nimport { Text } from '@backstage/ui';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\n\nimport { notificationsApiRef } from '../../api';\nimport { useNotificationsApi, useNotificationsRefresh } from '../../hooks';\nimport { notificationsTranslationRef } from '../../translation';\nimport { getNotificationsPageLink } from '../../utils/notificationLinks';\nimport { truncateText } from '../../utils/plainText';\nimport { BulkActions } from '../NotificationsTable/BulkActions';\nimport { NotificationIcon } from '../NotificationsTable/NotificationIcon';\nimport styles from './UnreadNotificationsCard.module.css';\n\nconst ThrottleDelayMs = 2000;\n\n/** @internal */\nexport type UnreadNotificationsCardProps = {\n /** Maximum number of unread notifications to fetch and display */\n maxMessages?: number;\n /** Maximum number of plain-text characters to show per notification title */\n charLimit?: number;\n /**\n * @deprecated Use `maxMessages` instead.\n */\n initialCount?: number;\n /**\n * @deprecated Use `charLimit` instead.\n */\n maxChars?: number;\n /** Maximum number of plain-text characters to show per notification description */\n descriptionMaxChars?: number;\n};\n\nconst resolveMaxMessages = ({\n maxMessages,\n initialCount,\n}: Pick<UnreadNotificationsCardProps, 'maxMessages' | 'initialCount'>) =>\n maxMessages ?? initialCount ?? 5;\n\nconst resolveCharLimit = ({\n charLimit,\n maxChars,\n}: Pick<UnreadNotificationsCardProps, 'charLimit' | 'maxChars'>) =>\n charLimit ?? maxChars ?? 80;\n\nconst NotificationListItem = ({\n notification,\n maxChars,\n descriptionMaxChars,\n onSwitchReadStatus,\n onSwitchSavedStatus,\n}: {\n notification: Notification;\n maxChars: number;\n descriptionMaxChars: number;\n onSwitchReadStatus: (ids: Notification['id'][], newStatus: boolean) => void;\n onSwitchSavedStatus: (ids: Notification['id'][], newStatus: boolean) => void;\n}) => {\n const {\n display: titleDisplay,\n truncated: titleTruncated,\n full: titleFull,\n } = truncateText(notification.payload.title, maxChars);\n const description = notification.payload.description\n ? truncateText(notification.payload.description, descriptionMaxChars)\n : undefined;\n\n return (\n <div className={styles.notificationRow}>\n <div className={styles.notificationContent}>\n <div className={styles.severityItem}>\n <NotificationIcon notification={notification} />\n </div>\n <div className={styles.notificationText}>\n <Text variant=\"body-medium\" className={styles.notificationTitle}>\n <Link\n to={getNotificationsPageLink(notification.id)}\n title={titleTruncated ? titleFull : undefined}\n >\n {titleDisplay}\n </Link>\n </Text>\n {description ? (\n <Text\n variant=\"body-small\"\n color=\"secondary\"\n className={styles.notificationDescription}\n title={description.truncated ? description.full : undefined}\n >\n {description.display}\n </Text>\n ) : null}\n <Text\n variant=\"body-small\"\n color=\"secondary\"\n className={styles.metaRow}\n >\n {!notification.user && (\n <RiRssFill size={14} className={styles.broadcastIcon} />\n )}\n {notification.origin && (\n <>\n <span className={styles.notificationInfoRow}>\n {notification.origin}\n </span>\n •\n </>\n )}\n {notification.payload.topic && (\n <>\n <span className={styles.notificationInfoRow}>\n {notification.payload.topic}\n </span>\n •\n </>\n )}\n {notification.created && (\n <RelativeTime\n value={notification.created}\n className={styles.notificationInfoRow}\n />\n )}\n </Text>\n </div>\n </div>\n <div className={styles.actionsColumn}>\n <BulkActions\n notifications={[notification]}\n selectedNotifications={new Set([notification.id])}\n onSwitchReadStatus={onSwitchReadStatus}\n onSwitchSavedStatus={onSwitchSavedStatus}\n />\n </div>\n </div>\n );\n};\n\n/** @internal */\nexport const UnreadNotificationsCard = ({\n maxMessages,\n charLimit,\n initialCount,\n maxChars,\n descriptionMaxChars = 120,\n}: UnreadNotificationsCardProps) => {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const resolvedMaxMessages = resolveMaxMessages({ maxMessages, initialCount });\n const resolvedCharLimit = resolveCharLimit({ charLimit, maxChars });\n const [refresh, setRefresh] = useState(false);\n const { lastSignal, pollTick } = useNotificationsRefresh();\n const notificationsApi = useApi(notificationsApiRef);\n\n const { error, value, retry, loading } = useNotificationsApi(\n api =>\n api.getNotifications({\n limit: resolvedMaxMessages,\n read: false,\n sort: 'created',\n sortOrder: 'desc',\n }),\n [resolvedMaxMessages],\n );\n\n const throttledSetRefresh = useMemo(\n () => throttle(() => setRefresh(true), ThrottleDelayMs),\n [],\n );\n\n useEffect(() => {\n return () => {\n throttledSetRefresh.cancel();\n };\n }, [throttledSetRefresh]);\n\n const onUpdate = useCallback(() => {\n retry();\n }, [retry]);\n\n const onSwitchReadStatus = useCallback(\n (ids: Notification['id'][], newStatus: boolean) => {\n notificationsApi\n .updateNotifications({\n ids,\n read: newStatus,\n })\n .then(onUpdate);\n },\n [notificationsApi, onUpdate],\n );\n\n const onSwitchSavedStatus = useCallback(\n (ids: Notification['id'][], newStatus: boolean) => {\n notificationsApi\n .updateNotifications({\n ids,\n saved: newStatus,\n })\n .then(onUpdate);\n },\n [notificationsApi, onUpdate],\n );\n\n useEffect(() => {\n if (refresh && !loading) {\n retry();\n setRefresh(false);\n }\n }, [refresh, retry, loading]);\n\n useEffect(() => {\n if (!lastSignal?.action) {\n return;\n }\n\n if (\n lastSignal.action === 'new_notification' ||\n lastSignal.action === 'notification_read' ||\n lastSignal.action === 'notification_unread'\n ) {\n throttledSetRefresh();\n }\n }, [lastSignal, throttledSetRefresh]);\n\n useEffect(() => {\n if (pollTick > 0) {\n throttledSetRefresh();\n }\n }, [pollTick, throttledSetRefresh]);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n throttledSetRefresh();\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [throttledSetRefresh]);\n\n const notifications = value?.notifications ?? [];\n const totalCount = value?.totalCount ?? notifications.length;\n const displayedCount = notifications.length;\n\n return (\n <InfoCard\n title={t('unreadCard.title', { count: totalCount })}\n action={\n <LinkButton to={getNotificationsPageLink()} color=\"primary\">\n {t('unreadCard.viewAll')}\n </LinkButton>\n }\n variant=\"gridItem\"\n >\n <div className={styles.content}>\n {loading && <Progress />}\n {error && <ErrorPanel error={error} />}\n {!loading && !error && (\n <>\n {notifications.length === 0 ? (\n <div className={styles.emptyState}>\n <RiCheckDoubleLine\n size={32}\n className={styles.emptyStateIcon}\n aria-hidden\n data-testid=\"unread-notifications-empty-icon\"\n />\n <Text variant=\"body-medium\">{t('unreadCard.emptyState')}</Text>\n </div>\n ) : (\n <>\n <div className={styles.columnHeader}>\n <Text variant=\"body-x-small\" color=\"secondary\">\n {t('unreadCard.columnNotification')}\n </Text>\n <Text\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.columnHeaderActions}\n >\n {t('unreadCard.columnActions')}\n </Text>\n </div>\n {notifications.map(notification => (\n <NotificationListItem\n key={notification.id}\n notification={notification}\n maxChars={resolvedCharLimit}\n descriptionMaxChars={descriptionMaxChars}\n onSwitchReadStatus={onSwitchReadStatus}\n onSwitchSavedStatus={onSwitchSavedStatus}\n />\n ))}\n <Text\n variant=\"body-small\"\n color=\"secondary\"\n className={styles.resultsCount}\n >\n {t('unreadCard.resultsCount', {\n displayed: String(displayedCount),\n total: String(totalCount),\n })}\n </Text>\n </>\n )}\n </>\n )}\n </div>\n </InfoCard>\n );\n};\n\n/**\n * UnreadNotificationsCard used by the NFS home page widget.\n *\n * @internal\n */\nexport const UnreadNotificationsCardWithProvider = (\n props: UnreadNotificationsCardProps,\n) => <UnreadNotificationsCard {...props} />;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = "/*\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\n@layer components {\n .UnreadNotificationsCard_content__55d55e8dda {\n min-height: 9rem;\n display: flex;\n flex-direction: column;\n }\n\n .UnreadNotificationsCard_columnHeader__55d55e8dda {\n display: flex;\n align-items: center;\n justify-content: space-between;\n background-color: var(--bui-color-bg-surface-hover, rgba(0, 0, 0, 0.04));\n border-radius: var(--bui-radius-2, 4px);\n padding: var(--bui-space-2, 8px) var(--bui-space-5, 24px)\n var(--bui-space-2, 8px) var(--bui-space-7, 36px);\n margin-bottom: var(--bui-space-2, 8px);\n color: var(--bui-color-fg-secondary, rgba(0, 0, 0, 0.54));\n font-size: 0.75rem;\n text-transform: uppercase;\n }\n\n .UnreadNotificationsCard_columnHeaderActions__55d55e8dda {\n min-width: 5rem;\n text-align: right;\n }\n\n .UnreadNotificationsCard_notificationRow__55d55e8dda {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--bui-space-2, 8px);\n padding: var(--bui-space-3, 12px) 0;\n border-bottom: 1px solid var(--bui-color-border, rgba(0, 0, 0, 0.12));\n }\n\n .UnreadNotificationsCard_notificationRow__55d55e8dda:last-child {\n border-bottom: none;\n }\n\n .UnreadNotificationsCard_notificationContent__55d55e8dda {\n display: flex;\n flex: 1;\n min-width: 0;\n gap: var(--bui-space-3, 12px);\n }\n\n .UnreadNotificationsCard_severityItem__55d55e8dda {\n align-content: center;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n }\n\n .UnreadNotificationsCard_notificationText__55d55e8dda {\n min-width: 0;\n }\n\n .UnreadNotificationsCard_notificationTitle__55d55e8dda {\n font-weight: 500;\n font-size: 0.875rem;\n line-height: 1.43;\n }\n\n .UnreadNotificationsCard_notificationDescription__55d55e8dda {\n color: var(--bui-color-fg-secondary, rgba(0, 0, 0, 0.54));\n font-size: 0.875rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n\n .UnreadNotificationsCard_metaRow__55d55e8dda {\n color: var(--bui-color-fg-secondary, rgba(0, 0, 0, 0.54));\n font-size: 0.75rem;\n }\n\n .UnreadNotificationsCard_notificationInfoRow__55d55e8dda {\n margin-left: var(--bui-space-1, 4px);\n margin-right: var(--bui-space-1, 4px);\n }\n\n .UnreadNotificationsCard_broadcastIcon__55d55e8dda {\n vertical-align: text-bottom;\n }\n\n .UnreadNotificationsCard_actionsColumn__55d55e8dda {\n flex-shrink: 0;\n }\n\n .UnreadNotificationsCard_emptyState__55d55e8dda {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: var(--bui-space-8, 32px) 0;\n gap: var(--bui-space-2, 8px);\n }\n\n .UnreadNotificationsCard_emptyStateIcon__55d55e8dda {\n color: var(--bui-color-fg-disabled, rgba(0, 0, 0, 0.38));\n }\n\n .UnreadNotificationsCard_resultsCount__55d55e8dda {\n margin-top: var(--bui-space-4, 16px);\n text-align: right;\n color: var(--bui-color-fg-secondary, rgba(0, 0, 0, 0.54));\n font-size: 0.875rem;\n }\n}\n";
|
|
4
|
+
var styles = {"content":"UnreadNotificationsCard_content__55d55e8dda","columnHeader":"UnreadNotificationsCard_columnHeader__55d55e8dda","columnHeaderActions":"UnreadNotificationsCard_columnHeaderActions__55d55e8dda","notificationRow":"UnreadNotificationsCard_notificationRow__55d55e8dda","notificationContent":"UnreadNotificationsCard_notificationContent__55d55e8dda","severityItem":"UnreadNotificationsCard_severityItem__55d55e8dda","notificationText":"UnreadNotificationsCard_notificationText__55d55e8dda","notificationTitle":"UnreadNotificationsCard_notificationTitle__55d55e8dda","notificationDescription":"UnreadNotificationsCard_notificationDescription__55d55e8dda","metaRow":"UnreadNotificationsCard_metaRow__55d55e8dda","notificationInfoRow":"UnreadNotificationsCard_notificationInfoRow__55d55e8dda","broadcastIcon":"UnreadNotificationsCard_broadcastIcon__55d55e8dda","actionsColumn":"UnreadNotificationsCard_actionsColumn__55d55e8dda","emptyState":"UnreadNotificationsCard_emptyState__55d55e8dda","emptyStateIcon":"UnreadNotificationsCard_emptyStateIcon__55d55e8dda","resultsCount":"UnreadNotificationsCard_resultsCount__55d55e8dda"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { styles as default };
|
|
8
|
+
//# sourceMappingURL=UnreadNotificationsCard.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnreadNotificationsCard.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
|
|
@@ -40,9 +40,9 @@ const OriginRow = (props) => {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
),
|
|
43
|
-
/* @__PURE__ */ jsx(Tooltip, { children: `Enable or disable ${ch.id.
|
|
44
|
-
|
|
45
|
-
)}
|
|
43
|
+
/* @__PURE__ */ jsx(Tooltip, { children: `Enable or disable ${ch.id.toLowerCase()} notifications from ${formatOriginName(
|
|
44
|
+
origin.id
|
|
45
|
+
)}` })
|
|
46
46
|
] }) }, ch.id))
|
|
47
47
|
] });
|
|
48
48
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OriginRow.esm.js","sources":["../../../src/components/UserNotificationSettingsCard/OriginRow.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 */\n\nimport {\n isNotificationsEnabledFor,\n NotificationSettings,\n OriginSetting,\n} from '@backstage/plugin-notifications-common';\nimport { ButtonIcon, Switch, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react';\nimport { NoBorderTableCell } from './NoBorderTableCell';\nimport { useNotificationFormat } from './NotificationFormatContext';\n\nexport const OriginRow = (props: {\n origin: OriginSetting;\n settings: NotificationSettings;\n handleChange: (\n channel: string,\n origin: string,\n topic: string | null,\n enabled: boolean,\n ) => void;\n open: boolean;\n handleRowToggle: (originId: string) => void;\n}) => {\n const { origin, settings, handleChange, open, handleRowToggle } = props;\n const { formatOriginName } = useNotificationFormat();\n return (\n <tr>\n <NoBorderTableCell>\n {origin.topics && origin.topics.length > 0 && (\n <TooltipTrigger>\n <ButtonIcon\n aria-label=\"expand row\"\n onPress={() => handleRowToggle(origin.id)}\n icon={\n open ? (\n <RiArrowUpSLine size={16} />\n ) : (\n <RiArrowDownSLine size={16} />\n )\n }\n variant=\"secondary\"\n />\n <Tooltip>{`Show Topics for the ${formatOriginName(\n origin.id,\n )} origin`}</Tooltip>\n </TooltipTrigger>\n )}\n </NoBorderTableCell>\n <NoBorderTableCell>{formatOriginName(origin.id)}</NoBorderTableCell>\n <NoBorderTableCell>all</NoBorderTableCell>\n {settings.channels.map(ch => (\n <NoBorderTableCell key={ch.id} align=\"center\">\n <TooltipTrigger>\n <Switch\n isSelected={isNotificationsEnabledFor(\n settings,\n ch.id,\n origin.id,\n null,\n )}\n onChange={(isSelected: boolean) => {\n handleChange(ch.id, origin.id, null, isSelected);\n }}\n />\n <Tooltip>{`Enable or disable ${ch.id.
|
|
1
|
+
{"version":3,"file":"OriginRow.esm.js","sources":["../../../src/components/UserNotificationSettingsCard/OriginRow.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 */\n\nimport {\n isNotificationsEnabledFor,\n NotificationSettings,\n OriginSetting,\n} from '@backstage/plugin-notifications-common';\nimport { ButtonIcon, Switch, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react';\nimport { NoBorderTableCell } from './NoBorderTableCell';\nimport { useNotificationFormat } from './NotificationFormatContext';\n\nexport const OriginRow = (props: {\n origin: OriginSetting;\n settings: NotificationSettings;\n handleChange: (\n channel: string,\n origin: string,\n topic: string | null,\n enabled: boolean,\n ) => void;\n open: boolean;\n handleRowToggle: (originId: string) => void;\n}) => {\n const { origin, settings, handleChange, open, handleRowToggle } = props;\n const { formatOriginName } = useNotificationFormat();\n return (\n <tr>\n <NoBorderTableCell>\n {origin.topics && origin.topics.length > 0 && (\n <TooltipTrigger>\n <ButtonIcon\n aria-label=\"expand row\"\n onPress={() => handleRowToggle(origin.id)}\n icon={\n open ? (\n <RiArrowUpSLine size={16} />\n ) : (\n <RiArrowDownSLine size={16} />\n )\n }\n variant=\"secondary\"\n />\n <Tooltip>{`Show Topics for the ${formatOriginName(\n origin.id,\n )} origin`}</Tooltip>\n </TooltipTrigger>\n )}\n </NoBorderTableCell>\n <NoBorderTableCell>{formatOriginName(origin.id)}</NoBorderTableCell>\n <NoBorderTableCell>all</NoBorderTableCell>\n {settings.channels.map(ch => (\n <NoBorderTableCell key={ch.id} align=\"center\">\n <TooltipTrigger>\n <Switch\n isSelected={isNotificationsEnabledFor(\n settings,\n ch.id,\n origin.id,\n null,\n )}\n onChange={(isSelected: boolean) => {\n handleChange(ch.id, origin.id, null, isSelected);\n }}\n />\n <Tooltip>{`Enable or disable ${ch.id.toLowerCase()} notifications from ${formatOriginName(\n origin.id,\n )}`}</Tooltip>\n </TooltipTrigger>\n </NoBorderTableCell>\n ))}\n </tr>\n );\n};\n"],"names":[],"mappings":";;;;;;;AA0BO,MAAM,SAAA,GAAY,CAAC,KAAA,KAWpB;AACJ,EAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,YAAA,EAAc,IAAA,EAAM,iBAAgB,GAAI,KAAA;AAClE,EAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,qBAAA,EAAsB;AACnD,EAAA,4BACG,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,iBAAA,EAAA,EACE,iBAAO,MAAA,IAAU,MAAA,CAAO,OAAO,MAAA,GAAS,CAAA,yBACtC,cAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,YAAA,EAAW,YAAA;AAAA,UACX,OAAA,EAAS,MAAM,eAAA,CAAgB,MAAA,CAAO,EAAE,CAAA;AAAA,UACxC,IAAA,EACE,IAAA,mBACE,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAM,IAAI,CAAA,mBAE1B,GAAA,CAAC,gBAAA,EAAA,EAAiB,IAAA,EAAM,EAAA,EAAI,CAAA;AAAA,UAGhC,OAAA,EAAQ;AAAA;AAAA,OACV;AAAA,sBACA,GAAA,CAAC,WAAS,QAAA,EAAA,CAAA,oBAAA,EAAuB,gBAAA;AAAA,QAC/B,MAAA,CAAO;AAAA,OACR,CAAA,OAAA,CAAA,EAAU;AAAA,KAAA,EACb,CAAA,EAEJ,CAAA;AAAA,oBACA,GAAA,CAAC,iBAAA,EAAA,EAAmB,QAAA,EAAA,gBAAA,CAAiB,MAAA,CAAO,EAAE,CAAA,EAAE,CAAA;AAAA,oBAChD,GAAA,CAAC,qBAAkB,QAAA,EAAA,KAAA,EAAG,CAAA;AAAA,IACrB,QAAA,CAAS,SAAS,GAAA,CAAI,CAAA,EAAA,yBACpB,iBAAA,EAAA,EAA8B,KAAA,EAAM,QAAA,EACnC,QAAA,kBAAA,IAAA,CAAC,cAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,UAAA,EAAY,yBAAA;AAAA,YACV,QAAA;AAAA,YACA,EAAA,CAAG,EAAA;AAAA,YACH,MAAA,CAAO,EAAA;AAAA,YACP;AAAA,WACF;AAAA,UACA,QAAA,EAAU,CAAC,UAAA,KAAwB;AACjC,YAAA,YAAA,CAAa,EAAA,CAAG,EAAA,EAAI,MAAA,CAAO,EAAA,EAAI,MAAM,UAAU,CAAA;AAAA,UACjD;AAAA;AAAA,OACF;AAAA,0BACC,OAAA,EAAA,EAAS,QAAA,EAAA,CAAA,kBAAA,EAAqB,GAAG,EAAA,CAAG,WAAA,EAAa,CAAA,oBAAA,EAAuB,gBAAA;AAAA,QACvE,MAAA,CAAO;AAAA,OACR,CAAA,CAAA,EAAG;AAAA,KAAA,EACN,CAAA,EAAA,EAhBsB,EAAA,CAAG,EAiB3B,CACD;AAAA,GAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -26,9 +26,7 @@ const TopicRow = (props) => {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
),
|
|
29
|
-
/* @__PURE__ */ jsx(Tooltip, { children: `Enable or disable ${ch.id.
|
|
30
|
-
"en-US"
|
|
31
|
-
)} notifications for the ${formatTopicName(
|
|
29
|
+
/* @__PURE__ */ jsx(Tooltip, { children: `Enable or disable ${ch.id.toLowerCase()} notifications for the ${formatTopicName(
|
|
32
30
|
topic.id
|
|
33
31
|
)} topic from ${formatOriginName(origin.id)}` })
|
|
34
32
|
] }) }, `${ch.id}-${topic.id}`))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopicRow.esm.js","sources":["../../../src/components/UserNotificationSettingsCard/TopicRow.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 */\n\nimport {\n isNotificationsEnabledFor,\n NotificationSettings,\n OriginSetting,\n TopicSetting,\n} from '@backstage/plugin-notifications-common';\nimport { Switch, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport { NoBorderTableCell } from './NoBorderTableCell';\nimport { useNotificationFormat } from './NotificationFormatContext';\n\nexport const TopicRow = (props: {\n topic: TopicSetting;\n origin: OriginSetting;\n settings: NotificationSettings;\n handleChange: (\n channel: string,\n origin: string,\n topic: string | null,\n enabled: boolean,\n ) => void;\n}) => {\n const { topic, origin, settings, handleChange } = props;\n const { formatOriginName, formatTopicName } = useNotificationFormat();\n return (\n <tr>\n <NoBorderTableCell />\n <NoBorderTableCell />\n <NoBorderTableCell>{formatTopicName(topic.id)}</NoBorderTableCell>\n {settings.channels.map(ch => (\n <NoBorderTableCell key={`${ch.id}-${topic.id}`} align=\"center\">\n <TooltipTrigger>\n <Switch\n isSelected={isNotificationsEnabledFor(\n settings,\n ch.id,\n origin.id,\n topic.id,\n )}\n onChange={(isSelected: boolean) => {\n handleChange(ch.id, origin.id, topic.id, isSelected);\n }}\n />\n <Tooltip>{`Enable or disable ${ch.id.
|
|
1
|
+
{"version":3,"file":"TopicRow.esm.js","sources":["../../../src/components/UserNotificationSettingsCard/TopicRow.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 */\n\nimport {\n isNotificationsEnabledFor,\n NotificationSettings,\n OriginSetting,\n TopicSetting,\n} from '@backstage/plugin-notifications-common';\nimport { Switch, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport { NoBorderTableCell } from './NoBorderTableCell';\nimport { useNotificationFormat } from './NotificationFormatContext';\n\nexport const TopicRow = (props: {\n topic: TopicSetting;\n origin: OriginSetting;\n settings: NotificationSettings;\n handleChange: (\n channel: string,\n origin: string,\n topic: string | null,\n enabled: boolean,\n ) => void;\n}) => {\n const { topic, origin, settings, handleChange } = props;\n const { formatOriginName, formatTopicName } = useNotificationFormat();\n return (\n <tr>\n <NoBorderTableCell />\n <NoBorderTableCell />\n <NoBorderTableCell>{formatTopicName(topic.id)}</NoBorderTableCell>\n {settings.channels.map(ch => (\n <NoBorderTableCell key={`${ch.id}-${topic.id}`} align=\"center\">\n <TooltipTrigger>\n <Switch\n isSelected={isNotificationsEnabledFor(\n settings,\n ch.id,\n origin.id,\n topic.id,\n )}\n onChange={(isSelected: boolean) => {\n handleChange(ch.id, origin.id, topic.id, isSelected);\n }}\n />\n <Tooltip>{`Enable or disable ${ch.id.toLowerCase()} notifications for the ${formatTopicName(\n topic.id,\n )} topic from ${formatOriginName(origin.id)}`}</Tooltip>\n </TooltipTrigger>\n </NoBorderTableCell>\n ))}\n </tr>\n );\n};\n"],"names":[],"mappings":";;;;;;AA0BO,MAAM,QAAA,GAAW,CAAC,KAAA,KAUnB;AACJ,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,cAAa,GAAI,KAAA;AAClD,EAAA,MAAM,EAAE,gBAAA,EAAkB,eAAA,EAAgB,GAAI,qBAAA,EAAsB;AACpE,EAAA,4BACG,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,iBAAA,EAAA,EAAkB,CAAA;AAAA,wBAClB,iBAAA,EAAA,EAAkB,CAAA;AAAA,oBACnB,GAAA,CAAC,iBAAA,EAAA,EAAmB,QAAA,EAAA,eAAA,CAAgB,KAAA,CAAM,EAAE,CAAA,EAAE,CAAA;AAAA,IAC7C,QAAA,CAAS,SAAS,GAAA,CAAI,CAAA,EAAA,yBACpB,iBAAA,EAAA,EAA+C,KAAA,EAAM,QAAA,EACpD,QAAA,kBAAA,IAAA,CAAC,cAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,UAAA,EAAY,yBAAA;AAAA,YACV,QAAA;AAAA,YACA,EAAA,CAAG,EAAA;AAAA,YACH,MAAA,CAAO,EAAA;AAAA,YACP,KAAA,CAAM;AAAA,WACR;AAAA,UACA,QAAA,EAAU,CAAC,UAAA,KAAwB;AACjC,YAAA,YAAA,CAAa,GAAG,EAAA,EAAI,MAAA,CAAO,EAAA,EAAI,KAAA,CAAM,IAAI,UAAU,CAAA;AAAA,UACrD;AAAA;AAAA,OACF;AAAA,0BACC,OAAA,EAAA,EAAS,QAAA,EAAA,CAAA,kBAAA,EAAqB,GAAG,EAAA,CAAG,WAAA,EAAa,CAAA,uBAAA,EAA0B,eAAA;AAAA,QAC1E,KAAA,CAAM;AAAA,OACP,CAAA,YAAA,EAAe,gBAAA,CAAiB,MAAA,CAAO,EAAE,CAAC,CAAA,CAAA,EAAG;AAAA,KAAA,EAChD,CAAA,EAAA,EAhBsB,GAAG,EAAA,CAAG,EAAE,IAAI,KAAA,CAAM,EAAE,EAiB5C,CACD;AAAA,GAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -2,6 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
3
|
import { InfoCard, Progress, ErrorPanel } from '@backstage/core-components';
|
|
4
4
|
import { useNotificationsApi } from '../../hooks/useNotificationsApi.esm.js';
|
|
5
|
+
import '@backstage/plugin-signals-react';
|
|
5
6
|
import { useApi } from '@backstage/core-plugin-api';
|
|
6
7
|
import { notificationsApiRef } from '../../api/NotificationsApi.esm.js';
|
|
7
8
|
import '@backstage/errors';
|
package/dist/components/UserNotificationSettingsCard/UserNotificationSettingsCard.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserNotificationSettingsCard.esm.js","sources":["../../../src/components/UserNotificationSettingsCard/UserNotificationSettingsCard.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 */\n\nimport { useState, useEffect } from 'react';\nimport { ErrorPanel, InfoCard, Progress } from '@backstage/core-components';\nimport { useNotificationsApi } from '../../hooks';\nimport { NotificationSettings } from '@backstage/plugin-notifications-common';\nimport { notificationsApiRef } from '../../api';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { notificationsTranslationRef } from '../../translation';\nimport { UserNotificationSettingsPanel } from './UserNotificationSettingsPanel';\nimport { NotificationFormatProvider } from './NotificationFormatContext';\n\n/** @public */\nexport const UserNotificationSettingsCard = (props: {\n originNames?: Record<string, string>;\n topicNames?: Record<string, string>;\n}) => {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const [settings, setNotificationSettings] = useState<\n NotificationSettings | undefined\n >(undefined);\n\n const client = useApi(notificationsApiRef);\n const { error, value, loading } = useNotificationsApi(api => {\n return api.getNotificationSettings();\n });\n\n useEffect(() => {\n if (!loading && !error) {\n setNotificationSettings(value);\n }\n }, [loading, value, error]);\n\n const onUpdate = (newSettings: NotificationSettings) => {\n client\n .updateNotificationSettings(newSettings)\n .then(updatedSettings => setNotificationSettings(updatedSettings));\n };\n\n return (\n <InfoCard title={t('settings.title')} variant=\"gridItem\">\n {loading && <Progress />}\n {error && <ErrorPanel title={t('settings.errorTitle')} error={error} />}\n {settings && (\n <NotificationFormatProvider\n originMap={props.originNames}\n topicMap={props.topicNames}\n >\n <UserNotificationSettingsPanel\n settings={settings}\n onChange={onUpdate}\n />\n </NotificationFormatProvider>\n )}\n </InfoCard>\n );\n};\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"UserNotificationSettingsCard.esm.js","sources":["../../../src/components/UserNotificationSettingsCard/UserNotificationSettingsCard.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 */\n\nimport { useState, useEffect } from 'react';\nimport { ErrorPanel, InfoCard, Progress } from '@backstage/core-components';\nimport { useNotificationsApi } from '../../hooks';\nimport { NotificationSettings } from '@backstage/plugin-notifications-common';\nimport { notificationsApiRef } from '../../api';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { notificationsTranslationRef } from '../../translation';\nimport { UserNotificationSettingsPanel } from './UserNotificationSettingsPanel';\nimport { NotificationFormatProvider } from './NotificationFormatContext';\n\n/** @public */\nexport const UserNotificationSettingsCard = (props: {\n originNames?: Record<string, string>;\n topicNames?: Record<string, string>;\n}) => {\n const { t } = useTranslationRef(notificationsTranslationRef);\n const [settings, setNotificationSettings] = useState<\n NotificationSettings | undefined\n >(undefined);\n\n const client = useApi(notificationsApiRef);\n const { error, value, loading } = useNotificationsApi(api => {\n return api.getNotificationSettings();\n });\n\n useEffect(() => {\n if (!loading && !error) {\n setNotificationSettings(value);\n }\n }, [loading, value, error]);\n\n const onUpdate = (newSettings: NotificationSettings) => {\n client\n .updateNotificationSettings(newSettings)\n .then(updatedSettings => setNotificationSettings(updatedSettings));\n };\n\n return (\n <InfoCard title={t('settings.title')} variant=\"gridItem\">\n {loading && <Progress />}\n {error && <ErrorPanel title={t('settings.errorTitle')} error={error} />}\n {settings && (\n <NotificationFormatProvider\n originMap={props.originNames}\n topicMap={props.topicNames}\n >\n <UserNotificationSettingsPanel\n settings={settings}\n onChange={onUpdate}\n />\n </NotificationFormatProvider>\n )}\n </InfoCard>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA4BO,MAAM,4BAAA,GAA+B,CAAC,KAAA,KAGvC;AACJ,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,iBAAA,CAAkB,2BAA2B,CAAA;AAC3D,EAAA,MAAM,CAAC,QAAA,EAAU,uBAAuB,CAAA,GAAI,SAE1C,MAAS,CAAA;AAEX,EAAA,MAAM,MAAA,GAAS,OAAO,mBAAmB,CAAA;AACzC,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAQ,GAAI,oBAAoB,CAAA,GAAA,KAAO;AAC3D,IAAA,OAAO,IAAI,uBAAA,EAAwB;AAAA,EACrC,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,KAAA,EAAO;AACtB,MAAA,uBAAA,CAAwB,KAAK,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,KAAA,EAAO,KAAK,CAAC,CAAA;AAE1B,EAAA,MAAM,QAAA,GAAW,CAAC,WAAA,KAAsC;AACtD,IAAA,MAAA,CACG,2BAA2B,WAAW,CAAA,CACtC,KAAK,CAAA,eAAA,KAAmB,uBAAA,CAAwB,eAAe,CAAC,CAAA;AAAA,EACrE,CAAA;AAEA,EAAA,4BACG,QAAA,EAAA,EAAS,KAAA,EAAO,EAAE,gBAAgB,CAAA,EAAG,SAAQ,UAAA,EAC3C,QAAA,EAAA;AAAA,IAAA,OAAA,wBAAY,QAAA,EAAA,EAAS,CAAA;AAAA,IACrB,yBAAS,GAAA,CAAC,UAAA,EAAA,EAAW,OAAO,CAAA,CAAE,qBAAqB,GAAG,KAAA,EAAc,CAAA;AAAA,IACpE,QAAA,oBACC,GAAA;AAAA,MAAC,0BAAA;AAAA,MAAA;AAAA,QACC,WAAW,KAAA,CAAM,WAAA;AAAA,QACjB,UAAU,KAAA,CAAM,UAAA;AAAA,QAEhB,QAAA,kBAAA,GAAA;AAAA,UAAC,6BAAA;AAAA,UAAA;AAAA,YACC,QAAA;AAAA,YACA,QAAA,EAAU;AAAA;AAAA;AACZ;AAAA;AACF,GAAA,EAEJ,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useSignal } from '@backstage/plugin-signals-react';
|
|
3
|
+
|
|
4
|
+
const DefaultPollIntervalMs = 3e4;
|
|
5
|
+
function useNotificationsRefresh(options) {
|
|
6
|
+
const { lastSignal, isSignalsAvailable } = useSignal("notifications");
|
|
7
|
+
const [pollTick, setPollTick] = useState(0);
|
|
8
|
+
const pollIntervalMs = DefaultPollIntervalMs;
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (isSignalsAvailable) {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
const interval = setInterval(() => {
|
|
14
|
+
if (document.visibilityState === "visible") {
|
|
15
|
+
setPollTick((tick) => tick + 1);
|
|
16
|
+
}
|
|
17
|
+
}, pollIntervalMs);
|
|
18
|
+
return () => clearInterval(interval);
|
|
19
|
+
}, [isSignalsAvailable, pollIntervalMs]);
|
|
20
|
+
return { lastSignal, isSignalsAvailable, pollTick };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { useNotificationsRefresh };
|
|
24
|
+
//# sourceMappingURL=useNotificationsRefresh.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNotificationsRefresh.esm.js","sources":["../../src/hooks/useNotificationsRefresh.ts"],"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 { useEffect, useState } from 'react';\nimport { useSignal } from '@backstage/plugin-signals-react';\nimport { NotificationSignal } from '@backstage/plugin-notifications-common';\n\nconst DefaultPollIntervalMs = 30_000;\n\n/**\n * Listens for notification signals and falls back to polling when the signals\n * plugin is unavailable (for example in module federation remotes without a\n * shared signals API).\n *\n * @internal\n */\nexport function useNotificationsRefresh(options?: { pollIntervalMs?: number }) {\n const { lastSignal, isSignalsAvailable } =\n useSignal<NotificationSignal>('notifications');\n const [pollTick, setPollTick] = useState(0);\n const pollIntervalMs = options?.pollIntervalMs ?? DefaultPollIntervalMs;\n\n useEffect(() => {\n if (isSignalsAvailable) {\n return undefined;\n }\n\n const interval = setInterval(() => {\n if (document.visibilityState === 'visible') {\n setPollTick(tick => tick + 1);\n }\n }, pollIntervalMs);\n\n return () => clearInterval(interval);\n }, [isSignalsAvailable, pollIntervalMs]);\n\n return { lastSignal, isSignalsAvailable, pollTick };\n}\n"],"names":[],"mappings":";;;AAoBA,MAAM,qBAAA,GAAwB,GAAA;AASvB,SAAS,wBAAwB,OAAA,EAAuC;AAC7E,EAAA,MAAM,EAAE,UAAA,EAAY,kBAAA,EAAmB,GACrC,UAA8B,eAAe,CAAA;AAC/C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,CAAC,CAAA;AAC1C,EAAA,MAAM,cAAA,GAA4C,qBAAA;AAElD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,kBAAA,EAAoB;AACtB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,QAAA,GAAW,YAAY,MAAM;AACjC,MAAA,IAAI,QAAA,CAAS,oBAAoB,SAAA,EAAW;AAC1C,QAAA,WAAA,CAAY,CAAA,IAAA,KAAQ,OAAO,CAAC,CAAA;AAAA,MAC9B;AAAA,IACF,GAAG,cAAc,CAAA;AAEjB,IAAA,OAAO,MAAM,cAAc,QAAQ,CAAA;AAAA,EACrC,CAAA,EAAG,CAAC,kBAAA,EAAoB,cAAc,CAAC,CAAA;AAEvC,EAAA,OAAO,EAAE,UAAA,EAAY,kBAAA,EAAoB,QAAA,EAAS;AACpD;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -77,12 +77,13 @@ type NotificationsTableProps = Pick<TableProps, 'onPageChange' | 'onRowsPerPageC
|
|
|
77
77
|
isLoading?: boolean;
|
|
78
78
|
isUnread: boolean;
|
|
79
79
|
notifications?: Notification[];
|
|
80
|
+
highlightedNotificationId?: string;
|
|
80
81
|
onUpdate: () => void;
|
|
81
82
|
setContainsText: (search: string) => void;
|
|
82
83
|
pageSize: number;
|
|
83
84
|
};
|
|
84
85
|
/** @public */
|
|
85
|
-
declare const NotificationsTable: ({ title, markAsReadOnLinkOpen, isLoading, notifications, isUnread, onUpdate, setContainsText, onPageChange, onRowsPerPageChange, page, pageSize, totalCount, }: NotificationsTableProps) => react_jsx_runtime.JSX.Element;
|
|
86
|
+
declare const NotificationsTable: ({ title, markAsReadOnLinkOpen, isLoading, notifications, highlightedNotificationId, isUnread, onUpdate, setContainsText, onPageChange, onRowsPerPageChange, page, pageSize, totalCount, }: NotificationsTableProps) => react_jsx_runtime.JSX.Element;
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* Props for the {@link NotificationDescription} swappable component.
|
|
@@ -281,6 +282,13 @@ declare const notificationsTranslationRef: _backstage_frontend_plugin_api.Transl
|
|
|
281
282
|
readonly "notificationsPage.tableTitle.unread_other": "Unread notifications ({{count}})";
|
|
282
283
|
readonly "notificationsPage.tableTitle.read_one": "Read notifications ({{count}})";
|
|
283
284
|
readonly "notificationsPage.tableTitle.read_other": "Read notifications ({{count}})";
|
|
285
|
+
readonly "unreadCard.emptyState": "All caught up!";
|
|
286
|
+
readonly "unreadCard.title_one": "Last unread notifications ({{count}})";
|
|
287
|
+
readonly "unreadCard.title_other": "Last unread notifications ({{count}})";
|
|
288
|
+
readonly "unreadCard.viewAll": "View All";
|
|
289
|
+
readonly "unreadCard.columnNotification": "Notification";
|
|
290
|
+
readonly "unreadCard.columnActions": "Actions";
|
|
291
|
+
readonly "unreadCard.resultsCount": "{{displayed}} results out of {{total}}";
|
|
284
292
|
}>;
|
|
285
293
|
|
|
286
294
|
export { NotificationDescription, NotificationsClient, NotificationsPage, NotificationsSidebarItem, NotificationsTable, UserNotificationSettingsCard, notificationsApiRef, notificationsPlugin, notificationsTranslationRef, useNotificationsApi };
|