/* * SonarQube * Copyright (C) 2009-2023 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { uniqWith } from 'lodash'; import * as React from 'react'; import { addNotification, getNotifications, removeNotification } from '../../api/notifications'; import { Notification, NotificationGlobalType, NotificationProjectType, } from '../../types/notifications'; import { getWrappedDisplayName } from './utils'; interface State { channels: string[]; globalTypes: NotificationGlobalType[]; loading: boolean; notifications: Notification[]; perProjectTypes: NotificationProjectType[]; } export interface WithNotificationsProps { addNotification: (added: Notification) => void; channels: string[]; globalTypes: NotificationGlobalType[]; loading: boolean; notifications: Notification[]; perProjectTypes: NotificationProjectType[]; removeNotification: (removed: Notification) => void; } export function withNotifications

( WrappedComponent: React.ComponentType

) { class Wrapper extends React.Component { mounted = false; static displayName = getWrappedDisplayName(WrappedComponent, 'withNotifications'); state: State = { channels: [], globalTypes: [], loading: true, notifications: [], perProjectTypes: [], }; componentDidMount() { this.mounted = true; this.fetchNotifications(); } componentWillUnmount() { this.mounted = false; } fetchNotifications = () => { getNotifications().then( (response) => { if (this.mounted) { this.setState({ channels: response.channels, globalTypes: response.globalTypes, loading: false, notifications: response.notifications, perProjectTypes: response.perProjectTypes, }); } }, () => { if (this.mounted) { this.setState({ loading: false }); } } ); }; addNotificationToState = (added: Notification) => { this.setState((state) => { const notifications = uniqWith([...state.notifications, added], this.areNotificationsEqual); return { notifications }; }); }; removeNotificationFromState = (removed: Notification) => { this.setState((state) => { const notifications = state.notifications.filter( (notification) => !this.areNotificationsEqual(notification, removed) ); return { notifications }; }); }; addNotification = (added: Notification) => { // optimistic update this.addNotificationToState(added); // recreate `data` to omit `projectName` and `organization` from `Notification` const data = { channel: added.channel, project: added.project, type: added.type }; addNotification(data).catch(() => { this.removeNotificationFromState(added); }); }; removeNotification = (removed: Notification) => { // optimistic update this.removeNotificationFromState(removed); // recreate `data` to omit `projectName` and `organization` from `Notification` const data = { channel: removed.channel, project: removed.project, type: removed.type }; removeNotification(data).catch(() => { this.addNotificationToState(removed); }); }; areNotificationsEqual = (a: Notification, b: Notification) => { return a.channel === b.channel && a.type === b.type && a.project === b.project; }; render() { const { channels, globalTypes, loading, notifications, perProjectTypes } = this.state; return ( ); } } return Wrapper; }