Add dialog

This commit is contained in:
Philipp Heckel 2022-02-19 22:26:58 -05:00
parent c859f866b8
commit 8c0f3b2304
4 changed files with 86 additions and 88 deletions

55
web/src/AddDialog.js Normal file
View file

@ -0,0 +1,55 @@
import * as React from 'react';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import {useState} from "react";
import Subscription from "./Subscription";
const defaultBaseUrl = "https://ntfy.sh"
const AddDialog = (props) => {
const [topic, setTopic] = useState("");
const handleCancel = () => {
setTopic('');
props.onCancel();
}
const handleSubmit = () => {
const subscription = new Subscription(defaultBaseUrl, topic);
props.onSubmit(subscription);
setTopic('');
}
return (
<>
<Dialog open={props.open} onClose={props.onClose}>
<DialogTitle>Subscribe to topic</DialogTitle>
<DialogContent>
<DialogContentText>
Topics may not be password-protected, so choose a name that's not easy to guess.
Once subscribed, you can PUT/POST notifications.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Topic name, e.g. phil_alerts"
value={topic}
onChange={ev => setTopic(ev.target.value)}
type="text"
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={handleCancel}>Cancel</Button>
<Button onClick={handleSubmit} autoFocus disabled={topic === ""}>Subscribe</Button>
</DialogActions>
</Dialog>
</>
);
};
export default AddDialog;

View file

@ -4,9 +4,8 @@ import Container from '@mui/material/Container';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Link from '@mui/material/Link'; import Link from '@mui/material/Link';
import Subscription from './Subscription';
import WsConnection from './WsConnection'; import WsConnection from './WsConnection';
import {createTheme, styled, ThemeProvider} from '@mui/material/styles'; import {styled, ThemeProvider} from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline'; import CssBaseline from '@mui/material/CssBaseline';
import MuiDrawer from '@mui/material/Drawer'; import MuiDrawer from '@mui/material/Drawer';
import MuiAppBar from '@mui/material/AppBar'; import MuiAppBar from '@mui/material/AppBar';
@ -17,7 +16,6 @@ import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import Badge from '@mui/material/Badge'; import Badge from '@mui/material/Badge';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import MenuIcon from '@mui/icons-material/Menu'; import MenuIcon from '@mui/icons-material/Menu';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import NotificationsIcon from '@mui/icons-material/Notifications'; import NotificationsIcon from '@mui/icons-material/Notifications';
@ -28,19 +26,8 @@ import SettingsIcon from "@mui/icons-material/Settings";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import Card from "@mui/material/Card"; import Card from "@mui/material/Card";
import {Button, CardActions, CardContent, Stack} from "@mui/material"; import {Button, CardActions, CardContent, Stack} from "@mui/material";
import AddDialog from "./AddDialog";
function Copyright(props) { import theme from "./theme";
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 drawerWidth = 240;
@ -88,32 +75,29 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open'
}), }),
); );
const mdTheme = createTheme();
const SubscriptionNav = (props) => { const SubscriptionNav = (props) => {
const subscriptions = props.subscriptions; const subscriptions = props.subscriptions;
return ( return (
<div className="subscriptionList"> <>
{Object.keys(subscriptions).map(id => {Object.keys(subscriptions).map(id =>
<SubscriptionItem <SubscriptionNavItem
key={id} key={id}
subscription={subscriptions[id]} subscription={subscriptions[id]}
selected={props.selectedSubscription === subscriptions[id]} selected={props.selectedSubscription === subscriptions[id]}
onClick={() => props.handleSubscriptionClick(id)} onClick={() => props.handleSubscriptionClick(id)}
/>) />)
} }
</div> </>
); );
} }
const SubscriptionItem = (props) => { const SubscriptionNavItem = (props) => {
const subscription = props.subscription; const subscription = props.subscription;
return ( return (
<ListItemButton onClick={props.onClick}> <ListItemButton onClick={props.onClick} selected={props.selected}>
<ListItemIcon> <ListItemIcon><ChatBubbleOutlineIcon /></ListItemIcon>
<ChatBubbleOutlineIcon /> <ListItemText primary={subscription.shortUrl()}/>
</ListItemIcon>
<ListItemText primary={subscription.shortUrl()} />
</ListItemButton> </ListItemButton>
); );
} }
@ -136,68 +120,48 @@ const NotificationItem = (props) => {
{notification.time} {notification.time}
</Typography> </Typography>
{notification.title && <Typography variant="h5" component="div"> {notification.title && <Typography variant="h5" component="div">
title: {notification.title} {notification.title}
</Typography>} </Typography>}
<Typography variant="body1"> <Typography variant="body1">
msg: {notification.message} {notification.message}
</Typography> </Typography>
</CardContent> </CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card> </Card>
); );
} }
const defaultBaseUrl = "https://ntfy.sh"
const SubscriptionAddForm = (props) => {
const [topic, setTopic] = useState("");
const handleSubmit = (ev) => {
ev.preventDefault();
props.onSubmit(new Subscription(defaultBaseUrl, topic));
setTopic('');
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={topic}
onChange={ev => setTopic(ev.target.value)}
placeholder="Topic name, e.g. phil_alerts"
required
/>
</form>
);
}
const App = () => { const App = () => {
const [open, setOpen] = React.useState(true); const [drawerOpen, setDrawerOpen] = useState(true);
const [subscriptions, setSubscriptions] = useState({}); const [subscriptions, setSubscriptions] = useState({});
const [selectedSubscription, setSelectedSubscription] = useState(null); const [selectedSubscription, setSelectedSubscription] = useState(null);
const [connections, setConnections] = useState({}); const [connections, setConnections] = useState({});
const [addDialogOpen, setAddDialogOpen] = useState(false);
const subscriptionChanged = (subscription) => { const subscriptionChanged = (subscription) => {
setSubscriptions(prev => ({...prev, [subscription.id]: subscription})); // Fake-replace setSubscriptions(prev => ({...prev, [subscription.id]: subscription})); // Fake-replace
}; };
const addSubscription = (subscription) => { const handleAddSubmit = (subscription) => {
setAddDialogOpen(false);
const connection = new WsConnection(subscription, subscriptionChanged); const connection = new WsConnection(subscription, subscriptionChanged);
setSubscriptions(prev => ({...prev, [subscription.id]: subscription})); setSubscriptions(prev => ({...prev, [subscription.id]: subscription}));
setConnections(prev => ({...prev, [connection.id]: connection})); setConnections(prev => ({...prev, [connection.id]: connection}));
connection.start(); connection.start();
}; };
const handleAddCancel = () => {
setAddDialogOpen(false);
}
const handleSubscriptionClick = (subscriptionId) => { const handleSubscriptionClick = (subscriptionId) => {
console.log(`handleSubscriptionClick ${subscriptionId}`) console.log(`handleSubscriptionClick ${subscriptionId}`)
setSelectedSubscription(subscriptions[subscriptionId]); setSelectedSubscription(subscriptions[subscriptionId]);
}; };
const notifications = (selectedSubscription !== null) ? selectedSubscription.notifications : []; const notifications = (selectedSubscription !== null) ? selectedSubscription.notifications : [];
const toggleDrawer = () => { const toggleDrawer = () => {
setOpen(!open); setDrawerOpen(!drawerOpen);
}; };
return ( return (
<ThemeProvider theme={mdTheme}> <ThemeProvider theme={theme}>
<Box sx={{ display: 'flex' }}> <Box sx={{ display: 'flex' }}>
<CssBaseline /> <CssBaseline />
<AppBar position="absolute" open={open}> <AppBar position="absolute" open={drawerOpen}>
<Toolbar <Toolbar
sx={{ sx={{
pr: '24px', // keep right padding when drawer closed pr: '24px', // keep right padding when drawer closed
@ -211,7 +175,7 @@ const App = () => {
onClick={toggleDrawer} onClick={toggleDrawer}
sx={{ sx={{
marginRight: '36px', marginRight: '36px',
...(open && { display: 'none' }), ...(drawerOpen && { display: 'none' }),
}} }}
> >
<MenuIcon /> <MenuIcon />
@ -232,7 +196,7 @@ const App = () => {
</IconButton> </IconButton>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<Drawer variant="permanent" open={open}> <Drawer variant="permanent" open={drawerOpen}>
<Toolbar <Toolbar
sx={{ sx={{
display: 'flex', display: 'flex',
@ -259,7 +223,7 @@ const App = () => {
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Settings" /> <ListItemText primary="Settings" />
</ListItemButton> </ListItemButton>
<ListItemButton> <ListItemButton onClick={() => setAddDialogOpen(true)}>
<ListItemIcon> <ListItemIcon>
<AddIcon /> <AddIcon />
</ListItemIcon> </ListItemIcon>
@ -281,21 +245,17 @@ const App = () => {
> >
<Toolbar /> <Toolbar />
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}> <Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
<Grid container spacing={3}> <Grid container spacing={3}>
<SubscriptionAddForm onSubmit={addSubscription}/>
<NotificationList notifications={notifications}/> <NotificationList notifications={notifications}/>
{/* Recent Orders */}
<Grid item xs={12}>
<Paper sx={{ p: 2, display: 'flex', flexDirection: 'column' }}>
</Paper>
</Grid> </Grid>
</Grid>
<Copyright sx={{ pt: 4 }} />
</Container> </Container>
</Box> </Box>
</Box> </Box>
<AddDialog
open={addDialogOpen}
onCancel={handleAddCancel}
onSubmit={handleAddSubmit}
/>
</ThemeProvider> </ThemeProvider>
); );
} }

View file

@ -1,17 +0,0 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import Typography from '@mui/material/Typography';
function Title(props) {
return (
<Typography component="h2" variant="h6" color="primary" gutterBottom>
{props.children}
</Typography>
);
}
Title.propTypes = {
children: PropTypes.node,
};
export default Title;

View file

@ -8,7 +8,7 @@ const theme = createTheme({
main: '#338574', main: '#338574',
}, },
secondary: { secondary: {
main: '#338574', main: '#6cead0',
}, },
error: { error: {
main: red.A400, main: red.A400,