2022-02-19 03:49:51 +13:00
|
|
|
import * as React from 'react';
|
2022-02-20 13:48:33 +13:00
|
|
|
import {useState} from 'react';
|
2022-02-19 03:49:51 +13:00
|
|
|
import Container from '@mui/material/Container';
|
|
|
|
import Typography from '@mui/material/Typography';
|
|
|
|
import Box from '@mui/material/Box';
|
|
|
|
import Link from '@mui/material/Link';
|
2022-02-19 08:41:01 +13:00
|
|
|
import Subscription from './Subscription';
|
|
|
|
import WsConnection from './WsConnection';
|
2022-02-20 13:48:33 +13:00
|
|
|
import {createTheme, styled, ThemeProvider} from '@mui/material/styles';
|
|
|
|
import CssBaseline from '@mui/material/CssBaseline';
|
|
|
|
import MuiDrawer from '@mui/material/Drawer';
|
|
|
|
import MuiAppBar from '@mui/material/AppBar';
|
|
|
|
import Toolbar from '@mui/material/Toolbar';
|
|
|
|
import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline';
|
|
|
|
import List from '@mui/material/List';
|
|
|
|
import Divider from '@mui/material/Divider';
|
|
|
|
import IconButton from '@mui/material/IconButton';
|
|
|
|
import Badge from '@mui/material/Badge';
|
|
|
|
import Grid from '@mui/material/Grid';
|
|
|
|
import Paper from '@mui/material/Paper';
|
|
|
|
import MenuIcon from '@mui/icons-material/Menu';
|
|
|
|
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
|
|
|
import NotificationsIcon from '@mui/icons-material/Notifications';
|
|
|
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
|
|
|
import ListItemText from "@mui/material/ListItemText";
|
|
|
|
import ListItemButton from "@mui/material/ListItemButton";
|
|
|
|
import SettingsIcon from "@mui/icons-material/Settings";
|
|
|
|
import AddIcon from "@mui/icons-material/Add";
|
|
|
|
import Card from "@mui/material/Card";
|
|
|
|
import {Button, CardActions, CardContent, Stack} from "@mui/material";
|
2022-02-19 03:49:51 +13:00
|
|
|
|
2022-02-20 13:48:33 +13:00
|
|
|
function Copyright(props) {
|
|
|
|
return (
|
|
|
|
<Typography variant="body2" color="text.secondary" align="center" {...props}>
|
|
|
|
{'Copyright © '}
|
|
|
|
<Link color="inherit" href="https://mui.com/">
|
|
|
|
Your Website
|
|
|
|
</Link>{' '}
|
|
|
|
{new Date().getFullYear()}
|
|
|
|
{'.'}
|
|
|
|
</Typography>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const drawerWidth = 240;
|
|
|
|
|
|
|
|
const AppBar = styled(MuiAppBar, {
|
|
|
|
shouldForwardProp: (prop) => prop !== 'open',
|
|
|
|
})(({ theme, open }) => ({
|
|
|
|
zIndex: theme.zIndex.drawer + 1,
|
|
|
|
transition: theme.transitions.create(['width', 'margin'], {
|
|
|
|
easing: theme.transitions.easing.sharp,
|
|
|
|
duration: theme.transitions.duration.leavingScreen,
|
|
|
|
}),
|
|
|
|
...(open && {
|
|
|
|
marginLeft: drawerWidth,
|
|
|
|
width: `calc(100% - ${drawerWidth}px)`,
|
|
|
|
transition: theme.transitions.create(['width', 'margin'], {
|
|
|
|
easing: theme.transitions.easing.sharp,
|
|
|
|
duration: theme.transitions.duration.enteringScreen,
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}));
|
|
|
|
|
|
|
|
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
|
|
|
|
({ theme, open }) => ({
|
|
|
|
'& .MuiDrawer-paper': {
|
|
|
|
position: 'relative',
|
|
|
|
whiteSpace: 'nowrap',
|
|
|
|
width: drawerWidth,
|
|
|
|
transition: theme.transitions.create('width', {
|
|
|
|
easing: theme.transitions.easing.sharp,
|
|
|
|
duration: theme.transitions.duration.enteringScreen,
|
|
|
|
}),
|
|
|
|
boxSizing: 'border-box',
|
|
|
|
...(!open && {
|
|
|
|
overflowX: 'hidden',
|
|
|
|
transition: theme.transitions.create('width', {
|
|
|
|
easing: theme.transitions.easing.sharp,
|
|
|
|
duration: theme.transitions.duration.leavingScreen,
|
|
|
|
}),
|
|
|
|
width: theme.spacing(7),
|
|
|
|
[theme.breakpoints.up('sm')]: {
|
|
|
|
width: theme.spacing(9),
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
const mdTheme = createTheme();
|
|
|
|
|
|
|
|
const SubscriptionNav = (props) => {
|
2022-02-19 09:47:25 +13:00
|
|
|
const subscriptions = props.subscriptions;
|
2022-02-19 03:49:51 +13:00
|
|
|
return (
|
|
|
|
<div className="subscriptionList">
|
2022-02-19 09:47:25 +13:00
|
|
|
{Object.keys(subscriptions).map(id =>
|
|
|
|
<SubscriptionItem
|
|
|
|
key={id}
|
|
|
|
subscription={subscriptions[id]}
|
|
|
|
selected={props.selectedSubscription === subscriptions[id]}
|
|
|
|
onClick={() => props.handleSubscriptionClick(id)}
|
|
|
|
/>)
|
|
|
|
}
|
2022-02-19 03:49:51 +13:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-19 09:47:25 +13:00
|
|
|
const SubscriptionItem = (props) => {
|
2022-02-19 08:41:01 +13:00
|
|
|
const subscription = props.subscription;
|
2022-02-19 03:49:51 +13:00
|
|
|
return (
|
2022-02-20 13:48:33 +13:00
|
|
|
<ListItemButton onClick={props.onClick}>
|
|
|
|
<ListItemIcon>
|
|
|
|
<ChatBubbleOutlineIcon />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText primary={subscription.shortUrl()} />
|
|
|
|
</ListItemButton>
|
2022-02-19 03:49:51 +13:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-19 09:47:25 +13:00
|
|
|
const NotificationList = (props) => {
|
2022-02-19 03:49:51 +13:00
|
|
|
return (
|
2022-02-20 13:48:33 +13:00
|
|
|
<Stack spacing={3} className="notificationList">
|
2022-02-19 09:47:25 +13:00
|
|
|
{props.notifications.map(notification =>
|
|
|
|
<NotificationItem key={notification.id} notification={notification}/>)}
|
2022-02-20 13:48:33 +13:00
|
|
|
</Stack>
|
2022-02-19 03:49:51 +13:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-19 08:41:01 +13:00
|
|
|
const NotificationItem = (props) => {
|
2022-02-19 09:47:25 +13:00
|
|
|
const notification = props.notification;
|
2022-02-19 03:49:51 +13:00
|
|
|
return (
|
2022-02-20 13:48:33 +13:00
|
|
|
<Card sx={{ minWidth: 275 }}>
|
|
|
|
<CardContent>
|
|
|
|
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
|
|
|
|
{notification.time}
|
|
|
|
</Typography>
|
|
|
|
{notification.title && <Typography variant="h5" component="div">
|
|
|
|
title: {notification.title}
|
|
|
|
</Typography>}
|
|
|
|
<Typography variant="body1">
|
|
|
|
msg: {notification.message}
|
|
|
|
</Typography>
|
|
|
|
</CardContent>
|
|
|
|
<CardActions>
|
|
|
|
<Button size="small">Learn More</Button>
|
|
|
|
</CardActions>
|
|
|
|
</Card>
|
2022-02-19 03:49:51 +13:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-19 08:41:01 +13:00
|
|
|
const defaultBaseUrl = "https://ntfy.sh"
|
|
|
|
|
|
|
|
const SubscriptionAddForm = (props) => {
|
2022-02-19 05:07:04 +13:00
|
|
|
const [topic, setTopic] = useState("");
|
|
|
|
const handleSubmit = (ev) => {
|
|
|
|
ev.preventDefault();
|
2022-02-19 08:41:01 +13:00
|
|
|
props.onSubmit(new Subscription(defaultBaseUrl, topic));
|
|
|
|
setTopic('');
|
2022-02-19 05:07:04 +13:00
|
|
|
}
|
|
|
|
return (
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
value={topic}
|
|
|
|
onChange={ev => setTopic(ev.target.value)}
|
|
|
|
placeholder="Topic name, e.g. phil_alerts"
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
</form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-19 08:41:01 +13:00
|
|
|
const App = () => {
|
2022-02-20 13:48:33 +13:00
|
|
|
const [open, setOpen] = React.useState(true);
|
2022-02-19 09:47:25 +13:00
|
|
|
const [subscriptions, setSubscriptions] = useState({});
|
|
|
|
const [selectedSubscription, setSelectedSubscription] = useState(null);
|
|
|
|
const [connections, setConnections] = useState({});
|
|
|
|
const subscriptionChanged = (subscription) => {
|
|
|
|
setSubscriptions(prev => ({...prev, [subscription.id]: subscription})); // Fake-replace
|
|
|
|
};
|
|
|
|
const addSubscription = (subscription) => {
|
|
|
|
const connection = new WsConnection(subscription, subscriptionChanged);
|
|
|
|
setSubscriptions(prev => ({...prev, [subscription.id]: subscription}));
|
|
|
|
setConnections(prev => ({...prev, [connection.id]: connection}));
|
2022-02-19 08:41:01 +13:00
|
|
|
connection.start();
|
2022-02-19 09:47:25 +13:00
|
|
|
};
|
|
|
|
const handleSubscriptionClick = (subscriptionId) => {
|
|
|
|
console.log(`handleSubscriptionClick ${subscriptionId}`)
|
|
|
|
setSelectedSubscription(subscriptions[subscriptionId]);
|
|
|
|
};
|
|
|
|
const notifications = (selectedSubscription !== null) ? selectedSubscription.notifications : [];
|
2022-02-20 13:48:33 +13:00
|
|
|
const toggleDrawer = () => {
|
|
|
|
setOpen(!open);
|
|
|
|
};
|
2022-02-19 03:49:51 +13:00
|
|
|
return (
|
2022-02-20 13:48:33 +13:00
|
|
|
<ThemeProvider theme={mdTheme}>
|
|
|
|
<Box sx={{ display: 'flex' }}>
|
|
|
|
<CssBaseline />
|
|
|
|
<AppBar position="absolute" open={open}>
|
|
|
|
<Toolbar
|
|
|
|
sx={{
|
|
|
|
pr: '24px', // keep right padding when drawer closed
|
|
|
|
}}
|
|
|
|
color="primary"
|
|
|
|
>
|
|
|
|
<IconButton
|
|
|
|
edge="start"
|
|
|
|
color="inherit"
|
|
|
|
aria-label="open drawer"
|
|
|
|
onClick={toggleDrawer}
|
|
|
|
sx={{
|
|
|
|
marginRight: '36px',
|
|
|
|
...(open && { display: 'none' }),
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<MenuIcon />
|
|
|
|
</IconButton>
|
|
|
|
<Typography
|
|
|
|
component="h1"
|
|
|
|
variant="h6"
|
|
|
|
color="inherit"
|
|
|
|
noWrap
|
|
|
|
sx={{ flexGrow: 1 }}
|
|
|
|
>
|
|
|
|
ntfy
|
|
|
|
</Typography>
|
|
|
|
<IconButton color="inherit">
|
|
|
|
<Badge badgeContent={4} color="secondary">
|
|
|
|
<NotificationsIcon />
|
|
|
|
</Badge>
|
|
|
|
</IconButton>
|
|
|
|
</Toolbar>
|
|
|
|
</AppBar>
|
|
|
|
<Drawer variant="permanent" open={open}>
|
|
|
|
<Toolbar
|
|
|
|
sx={{
|
|
|
|
display: 'flex',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'flex-end',
|
|
|
|
px: [1],
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<IconButton onClick={toggleDrawer}>
|
|
|
|
<ChevronLeftIcon />
|
|
|
|
</IconButton>
|
|
|
|
</Toolbar>
|
|
|
|
<Divider />
|
|
|
|
<List component="nav">
|
|
|
|
<SubscriptionNav
|
|
|
|
subscriptions={subscriptions}
|
|
|
|
selectedSubscription={selectedSubscription}
|
|
|
|
handleSubscriptionClick={handleSubscriptionClick}
|
|
|
|
/>
|
|
|
|
<Divider sx={{ my: 1 }} />
|
|
|
|
<ListItemButton>
|
|
|
|
<ListItemIcon>
|
|
|
|
<SettingsIcon />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText primary="Settings" />
|
|
|
|
</ListItemButton>
|
|
|
|
<ListItemButton>
|
|
|
|
<ListItemIcon>
|
|
|
|
<AddIcon />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText primary="Add subscription" />
|
|
|
|
</ListItemButton>
|
|
|
|
</List>
|
|
|
|
</Drawer>
|
|
|
|
<Box
|
|
|
|
component="main"
|
|
|
|
sx={{
|
|
|
|
backgroundColor: (theme) =>
|
|
|
|
theme.palette.mode === 'light'
|
|
|
|
? theme.palette.grey[100]
|
|
|
|
: theme.palette.grey[900],
|
|
|
|
flexGrow: 1,
|
|
|
|
height: '100vh',
|
|
|
|
overflow: 'auto',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Toolbar />
|
|
|
|
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
|
|
|
|
|
|
|
|
<Grid container spacing={3}>
|
|
|
|
<SubscriptionAddForm onSubmit={addSubscription}/>
|
|
|
|
<NotificationList notifications={notifications}/>
|
|
|
|
{/* Recent Orders */}
|
|
|
|
<Grid item xs={12}>
|
|
|
|
<Paper sx={{ p: 2, display: 'flex', flexDirection: 'column' }}>
|
|
|
|
|
|
|
|
</Paper>
|
|
|
|
</Grid>
|
|
|
|
</Grid>
|
|
|
|
<Copyright sx={{ pt: 4 }} />
|
|
|
|
</Container>
|
|
|
|
</Box>
|
2022-02-19 03:49:51 +13:00
|
|
|
</Box>
|
2022-02-20 13:48:33 +13:00
|
|
|
</ThemeProvider>
|
2022-02-19 03:49:51 +13:00
|
|
|
);
|
|
|
|
}
|
2022-02-19 08:41:01 +13:00
|
|
|
|
|
|
|
export default App;
|