1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-09 01:17:06 +02:00

chore: remove disable notifications UI (#9814)

This commit is contained in:
Mateusz Kwasniewski 2025-04-22 15:03:15 +02:00 committed by GitHub
parent 9d98d0771e
commit da05c7be5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 0 additions and 512 deletions

View File

@ -1,30 +0,0 @@
import { Box, Typography, styled } from '@mui/material';
import NotificationsIcon from '@mui/icons-material/Notifications';
const StyledBox = styled(Box)(() => ({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
minHeight: '150px',
}));
const StyledNotificationsIcon = styled(NotificationsIcon)(({ theme }) => ({
height: '30px',
width: '30px',
color: theme.palette.neutral.main,
marginBottom: theme.spacing(1),
}));
interface IEmptyNotificationsProps {
text: string;
}
export const EmptyNotifications = ({ text }: IEmptyNotificationsProps) => {
return (
<StyledBox>
<StyledNotificationsIcon />
<Typography color='neutral.main'>{text}</Typography>
</StyledBox>
);
};

View File

@ -1,166 +0,0 @@
import {
Box,
Typography,
styled,
Avatar,
ListItemButton,
useTheme,
} from '@mui/material';
import type {
NotificationsSchemaItem,
NotificationsSchemaItemNotificationType,
} from 'openapi';
import { ReactComponent as ChangesAppliedIcon } from 'assets/icons/merge.svg';
import { TimeAgo } from 'component/common/TimeAgo/TimeAgo';
import ToggleOffOutlined from '@mui/icons-material/ToggleOffOutlined';
import { flexRow } from 'themes/themeStyles';
const StyledContainerBox = styled(Box, {
shouldForwardProp: (prop) => prop !== 'readAt',
})<{ readAt: boolean }>(({ theme, readAt }) => ({
padding: theme.spacing(0.5),
marginRight: theme.spacing(1.5),
backgroundColor: readAt
? theme.palette.neutral.light
: theme.palette.secondary.light,
width: '30px',
height: '30px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: `${theme.shape.borderRadius}px`,
top: 3,
left: 7,
}));
const StyledListItemButton = styled(ListItemButton)(({ theme }) => ({
position: 'relative',
cursor: 'pointer',
padding: theme.spacing(2.5, 2, 3, 2),
borderRadius: theme.shape.borderRadiusMedium,
width: '100%',
'&:after': {
content: '""',
width: `calc(100% - ${theme.spacing(4)})`,
height: '1px',
position: 'absolute',
bottom: 0,
backgroundColor: theme.palette.divider,
},
}));
const StyledNotificationMessageBox = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
width: '100%',
}));
const StyledSecondaryInfoBox = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
margin: theme.spacing(1, 0, 0, 5.25),
}));
const StyledMessageTypography = styled(Typography, {
shouldForwardProp: (prop) => prop !== 'readAt',
})<{ readAt: boolean }>(({ theme, readAt }) => ({
display: 'flex',
alignItems: 'center',
fontSize: theme.fontSizes.smallBody,
fontWeight: readAt ? 'normal' : 'bold',
textDecoration: 'none',
color: 'inherit',
wordBreak: 'break-all',
}));
const StyledTimeAgoTypography = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.smallerBody,
color: theme.palette.neutral.main,
}));
const StyledUserContainer = styled(Box)(({ theme }) => ({
...flexRow,
}));
const StyledAvatar = styled(Avatar)(({ theme }) => ({
width: '18px',
height: '18px',
}));
const StyledCreatedBy = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
color: theme.palette.neutral.main,
marginLeft: theme.spacing(1),
}));
interface INotificationProps {
notification: NotificationsSchemaItem;
onNotificationClick: (notification: NotificationsSchemaItem) => void;
}
export const Notification = ({
notification,
onNotificationClick,
}: INotificationProps) => {
const theme = useTheme();
const { readAt } = notification;
const resolveIcon = (type: NotificationsSchemaItemNotificationType) => {
if (type === 'change-request') {
return (
<StyledContainerBox readAt={Boolean(readAt)}>
<ChangesAppliedIcon
color={
notification.readAt
? theme.palette.neutral.main
: theme.palette.primary.main
}
style={{ transform: 'scale(0.8)' }}
/>
</StyledContainerBox>
);
}
if (type === 'toggle') {
return (
<StyledContainerBox readAt={Boolean(readAt)}>
<ToggleOffOutlined
sx={(theme) => ({
height: '20px',
width: '20px',
color: readAt
? theme.palette.neutral.main
: theme.palette.primary.main,
})}
/>
</StyledContainerBox>
);
}
};
return (
<StyledListItemButton onClick={() => onNotificationClick(notification)}>
<StyledNotificationMessageBox>
<StyledMessageTypography readAt={Boolean(readAt)}>
{resolveIcon(notification.notificationType)}{' '}
{notification.message}
</StyledMessageTypography>
<StyledSecondaryInfoBox>
<StyledUserContainer>
<StyledAvatar
src={notification.createdBy.imageUrl || ''}
/>
<StyledCreatedBy>
Created by {notification.createdBy.username}
</StyledCreatedBy>
</StyledUserContainer>
<StyledTimeAgoTypography>
<TimeAgo date={notification.createdAt} />
</StyledTimeAgoTypography>
</StyledSecondaryInfoBox>
</StyledNotificationMessageBox>
</StyledListItemButton>
);
};

View File

@ -1,259 +0,0 @@
import { type KeyboardEvent, useState } from 'react';
import {
Paper,
Typography,
Box,
IconButton,
styled,
ClickAwayListener,
Button,
Switch,
Tooltip,
} from '@mui/material';
import { useNotifications } from 'hooks/api/getters/useNotifications/useNotifications';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import NotificationsIcon from '@mui/icons-material/Notifications';
import { NotificationsHeader } from './NotificationsHeader';
import { NotificationsList } from './NotificationsList';
import { Notification } from './Notification';
import { EmptyNotifications } from './EmptyNotifications';
import type { NotificationsSchemaItem } from 'openapi';
import { useNavigate } from 'react-router-dom';
import { useNotificationsApi } from 'hooks/api/actions/useNotificationsApi/useNotificationsApi';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { flexRow } from 'themes/themeStyles';
import { Feedback } from 'component/common/Feedback/Feedback';
const StyledPrimaryContainerBox = styled(Box)(() => ({
position: 'relative',
}));
const StyledInnerContainerBox = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.background.elevation1,
display: 'flex',
justifyContent: 'center',
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
fontWeight: 'bold',
fontSize: theme.fontSizes.smallBody,
color: theme.palette.primary.main,
textAlign: 'center',
}));
const StyledPaper = styled(Paper)(({ theme }) => ({
width: '420px',
boxShadow: theme.boxShadows.popup,
borderRadius: `${theme.shape.borderRadiusLarge}px`,
position: 'absolute',
right: -100,
top: 60,
maxHeight: '80vh',
overflowY: 'auto',
}));
const StyledDotBox = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.primary.main,
borderRadius: '100%',
width: '7px',
height: '7px',
position: 'absolute',
top: 11,
right: 11,
}));
const StyledHeaderBox = styled(Box)(() => ({
...flexRow,
}));
const StyledHeaderTypography = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
}));
const StyledIconButton = styled(IconButton)(({ theme }) => ({
'&:focus-visible': {
outlineStyle: 'solid',
outlineWidth: 2,
outlineColor: theme.palette.primary.main,
borderRadius: '100%',
},
}));
export const Notifications = () => {
const [showNotifications, setShowNotifications] = useState(false);
const { notifications, refetchNotifications } = useNotifications({
refreshInterval: 15000,
});
const navigate = useNavigate();
const { markAsRead } = useNotificationsApi();
const { uiConfig } = useUiConfig();
const { trackEvent } = usePlausibleTracker();
const [showUnread, setShowUnread] = useState(false);
const onNotificationClick = async (
notification: NotificationsSchemaItem,
) => {
if (notification.link) {
navigate(notification.link);
}
if (uiConfig?.flags?.T) {
trackEvent('notifications', {
props: { eventType: notification.notificationType },
});
}
setShowNotifications(false);
try {
await markAsRead({
notifications: [notification.id],
});
refetchNotifications();
} catch (e) {
// No need to display this in the UI. Minor inconvinence if this call fails.
console.error('Error marking notification as read: ', e);
}
};
const onMarkAllAsRead = async () => {
try {
if (notifications && notifications.length > 0) {
await markAsRead({
notifications: notifications.map(
(notification) => notification.id,
),
});
refetchNotifications();
}
} catch (e) {
// No need to display this in the UI. Minor inconvinence if this call fails.
console.error('Error marking all notification as read: ', e);
}
};
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setShowNotifications(false);
}
};
const unreadNotifications = notifications?.filter(
(notification) => notification.readAt === null,
);
const hasUnreadNotifications = Boolean(
unreadNotifications && unreadNotifications.length > 0,
);
const filterUnread = (notification: NotificationsSchemaItem) => {
if (showUnread) {
return !notification.readAt;
}
return true;
};
const notificationComponents = notifications
?.filter(filterUnread)
.map((notification) => (
<Notification
notification={notification}
key={notification.id}
onNotificationClick={onNotificationClick}
/>
));
const shouldShowFeedback = Boolean(
notifications && notifications.length > 0 && !showUnread,
);
return (
<StyledPrimaryContainerBox>
<Tooltip title='Notifications' arrow>
<StyledIconButton
onClick={() => setShowNotifications(!showNotifications)}
data-testid='NOTIFICATIONS_BUTTON'
size='large'
>
<ConditionallyRender
condition={hasUnreadNotifications}
show={<StyledDotBox />}
/>
<NotificationsIcon />
</StyledIconButton>
</Tooltip>
<ConditionallyRender
condition={showNotifications}
show={
<ClickAwayListener
onClickAway={() => setShowNotifications(false)}
>
<StyledPaper
className='dropdown-outline'
onKeyDown={onKeyDown}
data-testid='NOTIFICATIONS_MODAL'
>
<NotificationsHeader>
<StyledHeaderBox>
<StyledHeaderTypography>
Show only unread
</StyledHeaderTypography>
<Switch
onClick={() =>
setShowUnread(!showUnread)
}
checked={showUnread}
/>
</StyledHeaderBox>
</NotificationsHeader>
<ConditionallyRender
condition={hasUnreadNotifications}
show={
<StyledInnerContainerBox data-testid='UNREAD_NOTIFICATIONS'>
<Button onClick={onMarkAllAsRead}>
<StyledTypography>
Mark all as read (
{unreadNotifications?.length})
</StyledTypography>
</Button>
</StyledInnerContainerBox>
}
/>{' '}
<ConditionallyRender
condition={notificationComponents?.length === 0}
show={
<EmptyNotifications
text={
showUnread
? 'No unread notifications'
: 'No new notifications'
}
/>
}
/>
<NotificationsList>
{notificationComponents}
</NotificationsList>
<ConditionallyRender
condition={shouldShowFeedback}
show={
<>
<Feedback
eventName='notifications'
id='useful'
localStorageKey='NotificationsUsefulPrompt'
/>
<br />
</>
}
/>
</StyledPaper>
</ClickAwayListener>
}
/>
</StyledPrimaryContainerBox>
);
};

View File

@ -1,25 +0,0 @@
import { Typography, styled, Box } from '@mui/material';
import type React from 'react';
import type { FC } from 'react';
const StyledOuterContainerBox = styled(Box)(({ theme }) => ({
padding: theme.spacing(1, 1.5, 1, 3),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
position: 'relative',
boxShadow: theme.boxShadows.separator,
}));
export const NotificationsHeader: FC<{ children?: React.ReactNode }> = ({
children,
}) => {
return (
<>
<StyledOuterContainerBox>
<Typography fontWeight='bold'>Notifications</Typography>
{children}
</StyledOuterContainerBox>
</>
);
};

View File

@ -1,17 +0,0 @@
import { List, styled } from '@mui/material';
import type React from 'react';
import type { FC } from 'react';
const StyledListContainer = styled(List)(({ theme }) => ({
padding: theme.spacing(1, 1, 3, 1),
}));
export const NotificationsList: FC<{ children?: React.ReactNode }> = ({
children,
}) => {
return (
<StyledListContainer data-testid='NOTIFICATIONS_LIST'>
{children}
</StyledListContainer>
);
};

View File

@ -15,13 +15,10 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import MenuBookIcon from '@mui/icons-material/MenuBook';
import { DrawerMenu } from './DrawerMenu/DrawerMenu';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined';
import LightModeOutlined from '@mui/icons-material/LightModeOutlined';
import { useThemeMode } from 'hooks/useThemeMode';
import { Notifications } from 'component/common/Notifications/Notifications';
import InviteLinkButton from './InviteLink/InviteLinkButton/InviteLinkButton';
import { useUiFlag } from 'hooks/useUiFlag';
import { CommandBar } from 'component/commandBar/CommandBar';
const HeaderComponent = styled(AppBar)(({ theme }) => ({
@ -81,8 +78,6 @@ const Header = () => {
const { onSetThemeMode, themeMode } = useThemeMode();
const theme = useTheme();
const disableNotifications = useUiFlag('disableNotifications');
const { isOss } = useUiConfig();
const smallScreen = useMediaQuery(theme.breakpoints.down('lg'));
const [openDrawer, setOpenDrawer] = useState(false);
const toggleDrawer = () => setOpenDrawer((prev) => !prev);
@ -148,10 +143,6 @@ const Header = () => {
/>
</StyledIconButton>
</Tooltip>
<ConditionallyRender
condition={!isOss() && !disableNotifications}
show={<Notifications />}
/>
<Tooltip title='Documentation' arrow>
<StyledIconButton
component='a'

View File

@ -63,7 +63,6 @@ export type UiFlags = {
demo?: boolean;
googleAuthEnabled?: boolean;
disableBulkToggle?: boolean;
disableNotifications?: boolean;
advancedPlayground?: boolean;
strategyVariant?: boolean;
doraMetrics?: boolean;

View File

@ -20,7 +20,6 @@ export type IFlagKey =
| 'demo'
| 'googleAuthEnabled'
| 'disableBulkToggle'
| 'disableNotifications'
| 'advancedPlayground'
| 'filterInvalidClientMetrics'
| 'filterExistingFlagNames'
@ -124,10 +123,6 @@ const flags: IFlags = {
process.env.DISABLE_BULK_TOGGLE,
false,
),
disableNotifications: parseEnvVarBoolean(
process.env.DISABLE_NOTIFICATIONS,
false,
),
filterInvalidClientMetrics: parseEnvVarBoolean(
process.env.FILTER_INVALID_CLIENT_METRICS,
false,