Skip to content

Commit

Permalink
[docs] Revamp the notifications (mui#19615)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrookes authored and EsoterikStare committed Feb 13, 2020
1 parent 21ebc6a commit b1e18d3
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 50 deletions.
19 changes: 12 additions & 7 deletions docs/notifications.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
[
{
"id": 27,
"text": "You can <a style=\"color: inherit;\" target=\"_blank\" rel=\"noopener\" href=\"https://twitter.com/MaterialUI\">follow us on Twitter</a> to receive exclusive tips and updates about Material-UI and the React ecosystem."
},
{
"id": 35,
"text": "Let's translate! <a style=\"color: inherit;\" target=\"_blank\" rel=\"noopener\" data-ga-event-category=\"l10n\" data-ga-event-action=\"notification\" data-ga-event-label=\"zh\" href=\"https://translate.material-ui.com/\">帮助 Material-UI 将文档翻译成中文</a>. 🇨🇳",
"title": "Let's translate!",
"text": "<a style=\"color: inherit;\" target=\"_blank\" rel=\"noopener\" data-ga-event-category=\"l10n\" data-ga-event-action=\"notification\" data-ga-event-label=\"zh\" href=\"https://translate.material-ui.com/\">帮助 Material-UI 将文档翻译成中文</a>. 🇨🇳",
"userLanguage": "zh"
},
{
"id": 47,
"date": "2020-01-25",
"title": "New blog post",
"text": "<a style=\"color: inherit;\" target=\"_blank\" rel=\"noopener\" href=\"https://medium.com/material-ui/2019-in-review-and-beyond-34f85e29926\">2019 in review and beyond</a>. 2019 was a great year for Material-UI. It puts us on an exciting path to solve even greater challenges in the coming years!"
},
{
"id": 48,
"text": "Material-UI is hiring <a style=\"color: inherit;\" target=\"_blank\" rel=\"noopener\" href=\"https://material-ui.com/company/software-engineer/\">a senior software developer</a>."
"date": "2020-02-08",
"title": "Material-UI is hiring",
"text": "We're looking for <a style=\"color: inherit;\" target=\"_blank\" rel=\"noopener\" href=\"https://material-ui.com/company/software-engineer/\">a senior software developer</a> to join the team."
}
]
]
2 changes: 1 addition & 1 deletion docs/src/modules/components/AppFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ function AppFrame(props) {
<MuiLink color="secondary" className={classes.skipNav} href="#main-content">
{t('skipToContent')}
</MuiLink>
<Notifications />
<MarkdownLinks />
<AppBar className={appBarClassName}>
<Toolbar>
Expand Down Expand Up @@ -320,6 +319,7 @@ function AppFrame(props) {
)}
</IconButton>
</Tooltip>
<Notifications />
<Tooltip title={t('github')} enterDelay={300}>
<IconButton
edge="end"
Expand Down
182 changes: 140 additions & 42 deletions docs/src/modules/components/Notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,37 @@
import fetch from 'cross-fetch';
import React from 'react';
import { useSelector } from 'react-redux';
import Button from '@material-ui/core/Button';
import { useRouter } from 'next/router';
import Snackbar from '@material-ui/core/Snackbar';
import { makeStyles } from '@material-ui/core/styles';
import NotificationsIcon from '@material-ui/icons/Notifications';
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import Badge from '@material-ui/core/Badge';
import Popper from '@material-ui/core/Popper';
import Grow from '@material-ui/core/Grow';
import Paper from '@material-ui/core/Paper';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import Divider from '@material-ui/core/Divider';
import sleep from 'modules/waterfall/sleep';
import { getCookie } from 'docs/src/modules/utils/helpers';
import notifications from '../../../notifications.json';

const useStyles = makeStyles(theme => ({
paper: {
transformOrigin: 'top right',
},
list: {
maxWidth: theme.spacing(40),
maxHeight: theme.spacing(40),
overflow: 'auto',
},
listItem: {
display: 'flex',
flexDirection: 'column',
},
}));

function getLastSeenNotification() {
const seen = getCookie('lastSeenNotification');
Expand All @@ -15,6 +41,10 @@ function getLastSeenNotification() {

let messages = null;

if (process.env.NODE_ENV !== 'production') {
messages = notifications;
}

async function getMessages() {
try {
if (!messages) {
Expand All @@ -32,43 +62,58 @@ async function getMessages() {
}

export default function Notifications() {
const classes = useStyles();
const [messageList, setMessageList] = React.useState([]);
const [unseenNotificationsCount, setUnseenNotificationsCount] = React.useState(0);
const [open, setOpen] = React.useState(false);
const [message, setMessage] = React.useState({});
const { route } = useRouter();
const [tooltipOpen, setTooltipOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const t = useSelector(state => state.options.t);
const userLanguage = useSelector(state => state.options.userLanguage);

const handleToggle = () => {
setOpen(prevOpen => !prevOpen);
setTooltipOpen(false);
setUnseenNotificationsCount(0);
document.cookie = `lastSeenNotification=${messageList[0].id};path=/;max-age=31536000`;
};

const handleClose = () => {
setOpen(false);
};

const handleTooltipOpen = () => {
setTooltipOpen(!open);
};

const handleTooltipClose = () => {
setTooltipOpen(false);
};

const handleMessage = () => {
const lastSeen = getLastSeenNotification();
const unseenMessages = messages.filter(message2 => {
if (message2.id <= lastSeen) {
return false;
}

const userMessages = messages.filter(message => {
if (
message2.userLanguage &&
message2.userLanguage !== userLanguage &&
message2.userLanguage !== navigator.language.substring(0, 2)
message.userLanguage &&
message.userLanguage !== userLanguage &&
message.userLanguage !== navigator.language.substring(0, 2)
) {
return false;
}

if (message2.route && message2.route !== route) {
return false;
}

return true;
});

if (unseenMessages.length > 0) {
setMessage(unseenMessages[0]);
setOpen(true);
const unseenCount = userMessages.reduce(
(count, message) => (message.id > lastSeen ? count + 1 : count),
0,
);

if (unseenCount > 0) {
setUnseenNotificationsCount(unseenCount);
}
};

const handleClose = () => {
setOpen(false);
document.cookie = `lastSeenNotification=${message.id};path=/;max-age=31536000`;
setMessageList(userMessages.reverse());
};

React.useEffect(() => {
Expand All @@ -89,25 +134,78 @@ export default function Notifications() {
return () => {
active = false;
};
}, [route]);
}, []);

return (
<Snackbar
key={message.id}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
ContentProps={{ 'aria-describedby': 'notification-message' }}
message={
<span id="notification-message" dangerouslySetInnerHTML={{ __html: message.text }} />
}
action={
<Button size="small" color="secondary" onClick={handleClose}>
{t('close')}
</Button>
}
open={open}
autoHideDuration={20e3}
onClose={handleClose}
onExited={handleMessage}
/>
<React.Fragment>
<Tooltip
open={tooltipOpen}
onOpen={handleTooltipOpen}
onClose={handleTooltipClose}
title={t('toggleNotifications')}
enterDelay={300}
>
<IconButton
color="inherit"
ref={anchorRef}
aria-controls={open ? 'notifications-popup' : undefined}
aria-haspopup="true"
aria-label={t('toggleNotifications')}
onClick={handleToggle}
data-ga-event-category="AppBar"
data-ga-event-action="toggleNotifications"
>
<Badge color="secondary" badgeContent={unseenNotificationsCount}>
<NotificationsIcon />
</Badge>
</IconButton>
</Tooltip>
<Popper
id="notifications-popup"
anchorEl={anchorRef.current}
open={open}
placement="bottom-end"
transition
disablePortal
role={undefined}
>
{({ TransitionProps }) => (
<ClickAwayListener onClickAway={handleClose}>
<Grow in={open} {...TransitionProps}>
<Paper className={classes.paper}>
<List className={classes.list}>
{messageList.map((message, index) => (
<React.Fragment key={message.id}>
<ListItem alignItems="flex-start" className={classes.listItem}>
{message.date && (
<ListItemText
secondary={new Date(message.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
/>
)}
<ListItemText
primary={message.title}
secondary={
<span
id="notification-message"
dangerouslySetInnerHTML={{ __html: message.text }}
/>
}
secondaryTypographyProps={{ color: 'textPrimary' }}
/>
</ListItem>
{index < messageList.length - 1 ? <Divider variant="middle" /> : null}
</React.Fragment>
))}
</List>
</Paper>
</Grow>
</ClickAwayListener>
)}
</Popper>
</React.Fragment>
);
}
1 change: 1 addition & 0 deletions docs/translations/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"useDarkTheme": "Use dark theme",
"toggleTheme": "Toggle light/dark theme",
"toggleRTL": "Toggle right-to-left/left-to-right",
"toggleNotifications": "Toggle notifications panel",
"github": "GitHub repository",
"strapline": "React components for faster and easier web development. Build your own design system, or start with Material Design.",
"getStarted": "Get Started",
Expand Down

0 comments on commit b1e18d3

Please sign in to comment.