diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts
index da5fd5d349..5a1cc3e662 100644
--- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts
+++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts
@@ -621,7 +621,7 @@ export class DatabaseNotificationsStore implements NotificationsStore {
channel: channel.id,
origin: origin.id,
topic: topic.id,
- enabled: topic.enabled,
+ enabled: origin.enabled && topic.enabled,
});
});
});
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx
new file mode 100644
index 0000000000..71cd52f8a8
--- /dev/null
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { withStyles } from '@material-ui/core/styles';
+import MuiTableCell from '@material-ui/core/TableCell';
+
+export const NoBorderTableCell = withStyles({
+ root: {
+ borderBottom: 'none',
+ },
+})(MuiTableCell);
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx
new file mode 100644
index 0000000000..5d42e54757
--- /dev/null
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import {
+ ChannelSetting,
+ isNotificationsEnabledFor,
+ NotificationSettings,
+ OriginSetting,
+} from '@backstage/plugin-notifications-common';
+import IconButton from '@material-ui/core/IconButton';
+import Switch from '@material-ui/core/Switch';
+import TableRow from '@material-ui/core/TableRow';
+import Tooltip from '@material-ui/core/Tooltip';
+import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
+import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
+import { NoBorderTableCell } from './NoBorderTableCell';
+import { useNotificationFormat } from './UserNotificationSettingsCard';
+
+export const OriginRow = (props: {
+ channel: ChannelSetting;
+ origin: OriginSetting;
+ settings: NotificationSettings;
+ handleChange: (
+ channel: string,
+ origin: string,
+ topic: string | null,
+ enabled: boolean,
+ ) => void;
+ open: boolean;
+ handleRowToggle: (originId: string) => void;
+}) => {
+ const { channel, origin, settings, handleChange, open, handleRowToggle } =
+ props;
+ const { formatOriginName } = useNotificationFormat();
+ return (
+
+
+ {origin.topics && origin.topics.length > 0 && (
+
+ handleRowToggle(origin.id)}
+ >
+ {open ? : }
+
+
+ )}
+
+ {formatOriginName(origin.id)}
+ *
+ {settings.channels.map(ch => (
+
+
+ ) => {
+ handleChange(ch.id, origin.id, null, event.target.checked);
+ }}
+ />
+
+
+ ))}
+
+ );
+};
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx
new file mode 100644
index 0000000000..5c9c3cbacc
--- /dev/null
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import {
+ isNotificationsEnabledFor,
+ NotificationSettings,
+ OriginSetting,
+ TopicSetting,
+} from '@backstage/plugin-notifications-common';
+import TableRow from '@material-ui/core/TableRow';
+import Tooltip from '@material-ui/core/Tooltip';
+import Switch from '@material-ui/core/Switch';
+import { withStyles } from '@material-ui/core/styles';
+import { NoBorderTableCell } from './NoBorderTableCell';
+import { useNotificationFormat } from './UserNotificationSettingsCard';
+
+const TopicTableRow = withStyles({
+ root: {
+ paddingLeft: '4px',
+ },
+})(TableRow);
+
+export const TopicRow = (props: {
+ topic: TopicSetting;
+ origin: OriginSetting;
+ settings: NotificationSettings;
+ handleChange: (
+ channel: string,
+ origin: string,
+ topic: string | null,
+ enabled: boolean,
+ ) => void;
+}) => {
+ const { topic, origin, settings, handleChange } = props;
+ const { formatOriginName, formatTopicName } = useNotificationFormat();
+ return (
+
+
+
+ {formatTopicName(topic.id)}
+ {settings.channels.map(ch => (
+
+
+ ) => {
+ handleChange(ch.id, origin.id, topic.id, event.target.checked);
+ }}
+ />
+
+
+ ))}
+
+ );
+};
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsCard.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsCard.tsx
index f74c7882e3..5fa9adbc68 100644
--- a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsCard.tsx
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsCard.tsx
@@ -14,13 +14,69 @@
* limitations under the License.
*/
-import React, { useEffect } from 'react';
+import React, { createContext, useContext, useEffect } from 'react';
import { ErrorPanel, InfoCard, Progress } from '@backstage/core-components';
import { useNotificationsApi } from '../../hooks';
import { NotificationSettings } from '@backstage/plugin-notifications-common';
import { notificationsApiRef } from '../../api';
import { useApi } from '@backstage/core-plugin-api';
import { UserNotificationSettingsPanel } from './UserNotificationSettingsPanel';
+import { capitalize } from 'lodash';
+
+type FormatContextType = {
+ formatOriginName: (id: string) => string;
+ formatTopicName: (id: string) => string;
+};
+
+const NotificationFormatContext = createContext(
+ undefined,
+);
+
+export const useNotificationFormat = () => {
+ const context = useContext(NotificationFormatContext);
+ if (!context)
+ throw new Error(
+ 'useNotificationFormat must be used within a NotificationFormatProvider',
+ );
+ return context;
+};
+
+type Props = {
+ children: React.ReactNode;
+ originMap: Record | undefined;
+ topicMap: Record | undefined;
+};
+
+export const NotificationFormatProvider = ({
+ children,
+ originMap,
+ topicMap,
+}: Props) => {
+ const formatName = (
+ id: string,
+ nameMap: Record | undefined,
+ ) => {
+ if (nameMap && id in nameMap) {
+ return nameMap[id];
+ }
+ return capitalize(id.replaceAll(/[-_:]/g, ' '));
+ };
+
+ const formatOriginName = (originId: string) => {
+ return formatName(originId, originMap);
+ };
+
+ const formatTopicName = (topicId: string) => {
+ return formatName(topicId, topicMap);
+ };
+ return (
+
+ {children}
+
+ );
+};
/** @public */
export const UserNotificationSettingsCard = (props: {
@@ -53,12 +109,15 @@ export const UserNotificationSettingsCard = (props: {
{loading && }
{error && }
{settings && (
-
+
+
+
)}
);
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx
index e6ed892fe5..61982ba41a 100644
--- a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx
@@ -15,23 +15,16 @@
*/
import React, { useState } from 'react';
-import {
- isNotificationsEnabledFor,
- NotificationSettings,
-} from '@backstage/plugin-notifications-common';
-import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
-import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
+import { NotificationSettings } from '@backstage/plugin-notifications-common';
import Table from '@material-ui/core/Table';
import MuiTableCell from '@material-ui/core/TableCell';
import { withStyles } from '@material-ui/core/styles';
-import IconButton from '@material-ui/core/IconButton';
import TableHead from '@material-ui/core/TableHead';
import Typography from '@material-ui/core/Typography';
import TableBody from '@material-ui/core/TableBody';
import TableRow from '@material-ui/core/TableRow';
-import Switch from '@material-ui/core/Switch';
-import { capitalize } from 'lodash';
-import Tooltip from '@material-ui/core/Tooltip';
+import { TopicRow } from './TopicRow';
+import { OriginRow } from './OriginRow';
const TableCell = withStyles({
root: {
@@ -39,12 +32,6 @@ const TableCell = withStyles({
},
})(MuiTableCell);
-const TopicTableRow = withStyles({
- root: {
- paddingLeft: '4px',
- },
-})(TableRow);
-
export const UserNotificationSettingsPanel = (props: {
settings: NotificationSettings;
onChange: (settings: NotificationSettings) => void;
@@ -113,23 +100,6 @@ export const UserNotificationSettingsPanel = (props: {
};
onChange(updatedSettings);
};
- const formatName = (
- id: string,
- nameMap: Record | undefined,
- ) => {
- if (nameMap && id in nameMap) {
- return nameMap[id];
- }
- return capitalize(id.replaceAll(/[-_:]/g, ' '));
- };
-
- const formatOriginName = (originId: string) => {
- return formatName(originId, props.originNames);
- };
-
- const formatTopicName = (topicId: string) => {
- return formatName(topicId, props.topicNames);
- };
if (settings.channels.length === 0) {
return (
@@ -162,96 +132,24 @@ export const UserNotificationSettingsPanel = (props: {
{settings.channels.map(channel =>
channel.origins.flatMap(origin => [
-
-
- {origin.topics && origin.topics.length > 0 && (
-
- handleRowToggle(origin.id)}
- >
- {expandedRows.has(origin.id) ? (
-
- ) : (
-
- )}
-
-
- )}
-
- {formatOriginName(origin.id)}
- *
- {settings.channels.map(ch => (
-
-
- ,
- ) => {
- handleChange(
- ch.id,
- origin.id,
- null,
- event.target.checked,
- );
- }}
- />
-
-
- ))}
- ,
+ ,
...(expandedRows.has(origin.id)
? origin.topics?.map(topic => (
-
-
-
- {formatTopicName(topic.id)}
- {settings.channels.map(ch => (
-
-
- ,
- ) => {
- handleChange(
- ch.id,
- origin.id,
- topic.id,
- event.target.checked,
- );
- }}
- />
-
-
- ))}
-
+
)) || []
: []),
]),