Refactor to responsive drawer
This commit is contained in:
		
							parent
							
								
									0958c1d527
								
							
						
					
					
						commit
						703f94a25f
					
				
					 6 changed files with 205 additions and 157 deletions
				
			
		
							
								
								
									
										58
									
								
								web/package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										58
									
								
								web/package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -13,6 +13,7 @@ | ||||||
|         "@mui/material": "latest", |         "@mui/material": "latest", | ||||||
|         "react": "latest", |         "react": "latest", | ||||||
|         "react-dom": "latest", |         "react-dom": "latest", | ||||||
|  |         "react-router-dom": "^6.2.1", | ||||||
|         "react-scripts": "^3.0.1" |         "react-scripts": "^3.0.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  | @ -8313,6 +8314,14 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", |       "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", | ||||||
|       "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" |       "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/history": { | ||||||
|  |       "version": "5.3.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", | ||||||
|  |       "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@babel/runtime": "^7.7.6" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/hmac-drbg": { |     "node_modules/hmac-drbg": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", |       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", | ||||||
|  | @ -13673,6 +13682,30 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", |       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", | ||||||
|       "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" |       "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/react-router": { | ||||||
|  |       "version": "6.2.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", | ||||||
|  |       "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "history": "^5.2.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "react": ">=16.8" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/react-router-dom": { | ||||||
|  |       "version": "6.2.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", | ||||||
|  |       "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "history": "^5.2.0", | ||||||
|  |         "react-router": "6.2.1" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "react": ">=16.8", | ||||||
|  |         "react-dom": ">=16.8" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/react-scripts": { |     "node_modules/react-scripts": { | ||||||
|       "version": "3.4.4", |       "version": "3.4.4", | ||||||
|       "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz", |       "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz", | ||||||
|  | @ -24030,6 +24063,14 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", |       "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", | ||||||
|       "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" |       "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" | ||||||
|     }, |     }, | ||||||
|  |     "history": { | ||||||
|  |       "version": "5.3.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", | ||||||
|  |       "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", | ||||||
|  |       "requires": { | ||||||
|  |         "@babel/runtime": "^7.7.6" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "hmac-drbg": { |     "hmac-drbg": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", |       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", | ||||||
|  | @ -28290,6 +28331,23 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", |       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", | ||||||
|       "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" |       "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" | ||||||
|     }, |     }, | ||||||
|  |     "react-router": { | ||||||
|  |       "version": "6.2.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", | ||||||
|  |       "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", | ||||||
|  |       "requires": { | ||||||
|  |         "history": "^5.2.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "react-router-dom": { | ||||||
|  |       "version": "6.2.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", | ||||||
|  |       "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", | ||||||
|  |       "requires": { | ||||||
|  |         "history": "^5.2.0", | ||||||
|  |         "react-router": "6.2.1" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "react-scripts": { |     "react-scripts": { | ||||||
|       "version": "3.4.4", |       "version": "3.4.4", | ||||||
|       "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz", |       "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz", | ||||||
|  |  | ||||||
|  | @ -9,12 +9,13 @@ | ||||||
|     "eject": "react-scripts eject" |     "eject": "react-scripts eject" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "react": "latest", |     "@emotion/styled": "latest", | ||||||
|     "react-dom": "latest", |  | ||||||
|     "react-scripts": "^3.0.1", |  | ||||||
|     "@mui/icons-material": "^5.4.2", |     "@mui/icons-material": "^5.4.2", | ||||||
|     "@mui/material": "latest", |     "@mui/material": "latest", | ||||||
|     "@emotion/styled": "latest" |     "react": "latest", | ||||||
|  |     "react-dom": "latest", | ||||||
|  |     "react-router-dom": "^6.2.1", | ||||||
|  |     "react-scripts": "^3.0.1" | ||||||
|   }, |   }, | ||||||
|   "browserslist": { |   "browserslist": { | ||||||
|     "production": [ |     "production": [ | ||||||
|  |  | ||||||
|  | @ -60,7 +60,6 @@ export async function* fetchLinesIterator(fileURL) { | ||||||
| 
 | 
 | ||||||
|     const re = /\n|\r|\r\n/gm; |     const re = /\n|\r|\r\n/gm; | ||||||
|     let startIndex = 0; |     let startIndex = 0; | ||||||
|     let result; |  | ||||||
| 
 | 
 | ||||||
|     for (;;) { |     for (;;) { | ||||||
|         let result = re.exec(chunk); |         let result = re.exec(chunk); | ||||||
|  |  | ||||||
|  | @ -2,25 +2,24 @@ import * as React from 'react'; | ||||||
| import {useEffect, useState} from 'react'; | import {useEffect, useState} from 'react'; | ||||||
| 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 {styled, ThemeProvider} from '@mui/material/styles'; | import {ThemeProvider} from '@mui/material/styles'; | ||||||
| import CssBaseline from '@mui/material/CssBaseline'; | import CssBaseline from '@mui/material/CssBaseline'; | ||||||
| import MuiDrawer from '@mui/material/Drawer'; | import Drawer from '@mui/material/Drawer'; | ||||||
| import MuiAppBar from '@mui/material/AppBar'; | import AppBar from '@mui/material/AppBar'; | ||||||
| import Toolbar from '@mui/material/Toolbar'; | import Toolbar from '@mui/material/Toolbar'; | ||||||
| import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline'; | import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline'; | ||||||
| import List from '@mui/material/List'; | import List from '@mui/material/List'; | ||||||
| import Divider from '@mui/material/Divider'; | import Divider from '@mui/material/Divider'; | ||||||
| import IconButton from '@mui/material/IconButton'; | import IconButton from '@mui/material/IconButton'; | ||||||
| import MenuIcon from '@mui/icons-material/Menu'; | import MenuIcon from '@mui/icons-material/Menu'; | ||||||
| import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; |  | ||||||
| import ListItemIcon from "@mui/material/ListItemIcon"; | import ListItemIcon from "@mui/material/ListItemIcon"; | ||||||
| import ListItemText from "@mui/material/ListItemText"; | import ListItemText from "@mui/material/ListItemText"; | ||||||
| import ListItemButton from "@mui/material/ListItemButton"; | import ListItemButton from "@mui/material/ListItemButton"; | ||||||
| import SettingsIcon from "@mui/icons-material/Settings"; | import SettingsIcon from "@mui/icons-material/Settings"; | ||||||
| import AddIcon from "@mui/icons-material/Add"; | import AddIcon from "@mui/icons-material/Add"; | ||||||
| import AddDialog from "./AddDialog"; | import SubscribeDialog from "./SubscribeDialog"; | ||||||
| import NotificationList from "./NotificationList"; | import NotificationList from "./NotificationList"; | ||||||
| import SubscriptionSettings from "./SubscriptionSettings"; | import IconSubscribeSettings from "./IconSubscribeSettings"; | ||||||
| import theme from "./theme"; | import theme from "./theme"; | ||||||
| import api from "../app/Api"; | import api from "../app/Api"; | ||||||
| import repository from "../app/Repository"; | import repository from "../app/Repository"; | ||||||
|  | @ -29,68 +28,23 @@ import Subscriptions from "../app/Subscriptions"; | ||||||
| 
 | 
 | ||||||
| const drawerWidth = 240; | const drawerWidth = 240; | ||||||
| 
 | 
 | ||||||
| const AppBar = styled(MuiAppBar, { | const NavSubscriptionList = (props) => { | ||||||
|     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 SubscriptionNav = (props) => { |  | ||||||
|     const subscriptions = props.subscriptions; |     const subscriptions = props.subscriptions; | ||||||
|     return ( |     return ( | ||||||
|         <> |         <> | ||||||
|             {subscriptions.map((id, subscription) => |             {subscriptions.map((id, subscription) => | ||||||
|                 <SubscriptionNavItem |                 <NavSubscriptionItem | ||||||
|                     key={id} |                     key={id} | ||||||
|                     subscription={subscription} |                     subscription={subscription} | ||||||
|                     selected={props.selectedSubscription && props.selectedSubscription.id === id} |                     selected={props.selectedSubscription && props.selectedSubscription.id === id} | ||||||
|                     onClick={() => props.handleSubscriptionClick(id)} |                     onClick={() => props.onSubscriptionClick(id)} | ||||||
|                 />) |                 />) | ||||||
|             } |             } | ||||||
|         </> |         </> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const SubscriptionNavItem = (props) => { | const NavSubscriptionItem = (props) => { | ||||||
|     const subscription = props.subscription; |     const subscription = props.subscription; | ||||||
|     return ( |     return ( | ||||||
|         <ListItemButton onClick={props.onClick} selected={props.selected}> |         <ListItemButton onClick={props.onClick} selected={props.selected}> | ||||||
|  | @ -100,13 +54,115 @@ const SubscriptionNavItem = (props) => { | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const NavList = (props) => { | ||||||
|  |     const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false); | ||||||
|  |     const handleSubscribeSubmit = (subscription) => { | ||||||
|  |         setSubscribeDialogOpen(false); | ||||||
|  |         props.onSubscribeSubmit(subscription); | ||||||
|  |     } | ||||||
|  |     return ( | ||||||
|  |         <> | ||||||
|  |             <Toolbar /> | ||||||
|  |             <Divider /> | ||||||
|  |             <List component="nav"> | ||||||
|  |                 <NavSubscriptionList | ||||||
|  |                     subscriptions={props.subscriptions} | ||||||
|  |                     selectedSubscription={props.selectedSubscription} | ||||||
|  |                     onSubscriptionClick={props.onSubscriptionClick} | ||||||
|  |                 /> | ||||||
|  |                 <Divider sx={{ my: 1 }} /> | ||||||
|  |                 <ListItemButton> | ||||||
|  |                     <ListItemIcon> | ||||||
|  |                         <SettingsIcon /> | ||||||
|  |                     </ListItemIcon> | ||||||
|  |                     <ListItemText primary="Settings" /> | ||||||
|  |                 </ListItemButton> | ||||||
|  |                 <ListItemButton onClick={() => setSubscribeDialogOpen(true)}> | ||||||
|  |                     <ListItemIcon> | ||||||
|  |                         <AddIcon /> | ||||||
|  |                     </ListItemIcon> | ||||||
|  |                     <ListItemText primary="Add subscription" /> | ||||||
|  |                 </ListItemButton> | ||||||
|  |             </List> | ||||||
|  |             <SubscribeDialog | ||||||
|  |                 open={subscribeDialogOpen} | ||||||
|  |                 onCancel={() => setSubscribeDialogOpen(false)} | ||||||
|  |                 onSubmit={handleSubscribeSubmit} | ||||||
|  |             /> | ||||||
|  |         </> | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const ActionBar = (props) => { | ||||||
|  |     const title = (props.selectedSubscription !== null) | ||||||
|  |         ? props.selectedSubscription.shortUrl() | ||||||
|  |         : "ntfy"; | ||||||
|  |     return ( | ||||||
|  |         <AppBar position="fixed" sx={{ width: { sm: `calc(100% - ${drawerWidth}px)` }, ml: { sm: `${drawerWidth}px` } }}> | ||||||
|  |             <Toolbar sx={{pr: '24px'}}> | ||||||
|  |                 <IconButton | ||||||
|  |                     color="inherit" | ||||||
|  |                     edge="start" | ||||||
|  |                     onClick={props.onMobileDrawerToggle} | ||||||
|  |                     sx={{ mr: 2, display: { sm: 'none' } }} | ||||||
|  |                 > | ||||||
|  |                     <MenuIcon /> | ||||||
|  |                 </IconButton> | ||||||
|  |                 <Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>{title}</Typography> | ||||||
|  |                 {props.selectedSubscription !== null && <IconSubscribeSettings | ||||||
|  |                     subscription={props.selectedSubscription} | ||||||
|  |                     onClearAll={props.onClearAll} | ||||||
|  |                     onUnsubscribe={props.onUnsubscribe} | ||||||
|  |                 />} | ||||||
|  |             </Toolbar> | ||||||
|  |         </AppBar> | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const Sidebar = (props) => { | ||||||
|  |     const navigationList = | ||||||
|  |         <NavList | ||||||
|  |             subscriptions={props.subscriptions} | ||||||
|  |             selectedSubscription={props.selectedSubscription} | ||||||
|  |             onSubscriptionClick={props.onSubscriptionClick} | ||||||
|  |             onSubscribeSubmit={props.onSubscribeSubmit} | ||||||
|  |         />; | ||||||
|  |     return ( | ||||||
|  |         <> | ||||||
|  |             {/* Mobile drawer; only shown if menu icon clicked (mobile open) and display is small */} | ||||||
|  |             <Drawer | ||||||
|  |                 variant="temporary" | ||||||
|  |                 open={props.mobileDrawerOpen} | ||||||
|  |                 onClose={props.onMobileDrawerToggle} | ||||||
|  |                 ModalProps={{ keepMounted: true }} // Better open performance on mobile.
 | ||||||
|  |                 sx={{ | ||||||
|  |                     display: { xs: 'block', sm: 'none' }, | ||||||
|  |                     '& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth }, | ||||||
|  |                 }} | ||||||
|  |             > | ||||||
|  |                 {navigationList} | ||||||
|  |             </Drawer> | ||||||
|  |             {/* Big screen drawer; persistent, shown if screen is big */} | ||||||
|  |             <Drawer | ||||||
|  |                 open | ||||||
|  |                 variant="permanent" | ||||||
|  |                 sx={{ | ||||||
|  |                     display: { xs: 'none', sm: 'block' }, | ||||||
|  |                     '& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth }, | ||||||
|  |                 }} | ||||||
|  |             > | ||||||
|  |                 {navigationList} | ||||||
|  |             </Drawer> | ||||||
|  |         </> | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const App = () => { | const App = () => { | ||||||
|     console.log(`[App] Rendering main view`); |     console.log(`[App] Rendering main view`); | ||||||
| 
 | 
 | ||||||
|     const [drawerOpen, setDrawerOpen] = useState(true); |     const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); | ||||||
|     const [subscriptions, setSubscriptions] = useState(new Subscriptions()); |     const [subscriptions, setSubscriptions] = useState(new Subscriptions()); | ||||||
|     const [selectedSubscription, setSelectedSubscription] = useState(null); |     const [selectedSubscription, setSelectedSubscription] = useState(null); | ||||||
|     const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false); |  | ||||||
|     const handleNotification = (subscriptionId, notification) => { |     const handleNotification = (subscriptionId, notification) => { | ||||||
|         setSubscriptions(prev => { |         setSubscriptions(prev => { | ||||||
|             const newSubscription = prev.get(subscriptionId).addNotification(notification); |             const newSubscription = prev.get(subscriptionId).addNotification(notification); | ||||||
|  | @ -115,7 +171,6 @@ const App = () => { | ||||||
|     }; |     }; | ||||||
|     const handleSubscribeSubmit = (subscription) => { |     const handleSubscribeSubmit = (subscription) => { | ||||||
|         console.log(`[App] New subscription: ${subscription.id}`); |         console.log(`[App] New subscription: ${subscription.id}`); | ||||||
|         setSubscribeDialogOpen(false); |  | ||||||
|         setSubscriptions(prev => prev.add(subscription).clone()); |         setSubscriptions(prev => prev.add(subscription).clone()); | ||||||
|         setSelectedSubscription(subscription); |         setSelectedSubscription(subscription); | ||||||
|         api.poll(subscription.baseUrl, subscription.topic) |         api.poll(subscription.baseUrl, subscription.topic) | ||||||
|  | @ -126,10 +181,6 @@ const App = () => { | ||||||
|                 }); |                 }); | ||||||
|             }); |             }); | ||||||
|     }; |     }; | ||||||
|     const handleSubscribeCancel = () => { |  | ||||||
|         console.log(`[App] Cancel clicked`); |  | ||||||
|         setSubscribeDialogOpen(false); |  | ||||||
|     }; |  | ||||||
|     const handleClearAll = (subscriptionId) => { |     const handleClearAll = (subscriptionId) => { | ||||||
|         console.log(`[App] Deleting all notifications from ${subscriptionId}`); |         console.log(`[App] Deleting all notifications from ${subscriptionId}`); | ||||||
|         setSubscriptions(prev => { |         setSubscriptions(prev => { | ||||||
|  | @ -145,14 +196,7 @@ const App = () => { | ||||||
|             return newSubscriptions; |             return newSubscriptions; | ||||||
|         }); |         }); | ||||||
|     }; |     }; | ||||||
|     const handleSubscriptionClick = (subscriptionId) => { |  | ||||||
|         console.log(`[App] Selected ${subscriptionId}`); |  | ||||||
|         setSelectedSubscription(subscriptions.get(subscriptionId)); |  | ||||||
|     }; |  | ||||||
|     const notifications = (selectedSubscription !== null) ? selectedSubscription.getNotifications() : []; |     const notifications = (selectedSubscription !== null) ? selectedSubscription.getNotifications() : []; | ||||||
|     const toggleDrawer = () => { |  | ||||||
|         setDrawerOpen(!drawerOpen); |  | ||||||
|     }; |  | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|         setSubscriptions(repository.loadSubscriptions()); |         setSubscriptions(repository.loadSubscriptions()); | ||||||
|     }, [/* initial render only */]); |     }, [/* initial render only */]); | ||||||
|  | @ -160,96 +204,42 @@ const App = () => { | ||||||
|         connectionManager.refresh(subscriptions, handleNotification); |         connectionManager.refresh(subscriptions, handleNotification); | ||||||
|         repository.saveSubscriptions(subscriptions); |         repository.saveSubscriptions(subscriptions); | ||||||
|     }, [subscriptions]); |     }, [subscriptions]); | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|         <ThemeProvider theme={theme}> |         <ThemeProvider theme={theme}> | ||||||
|             <CssBaseline /> |             <CssBaseline/> | ||||||
|             <Box sx={{ display: 'flex' }}> |             <Box sx={{display: 'flex'}}> | ||||||
|                 <AppBar position="absolute" open={drawerOpen}> |                 <CssBaseline/> | ||||||
|                     <Toolbar sx={{pr: '24px'}} color="primary"> |                 <ActionBar | ||||||
|                         <IconButton |                     selectedSubscription={selectedSubscription} | ||||||
|                             edge="start" |                     onClearAll={handleClearAll} | ||||||
|                             color="inherit" |                     onUnsubscribe={handleUnsubscribe} | ||||||
|                             aria-label="open drawer" |                     onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} | ||||||
|                             onClick={toggleDrawer} |                 /> | ||||||
|                             sx={{ |                 <Box component="nav" sx={{width: {sm: drawerWidth}, flexShrink: {sm: 0}}}> | ||||||
|                                 marginRight: '36px', |                     <Sidebar | ||||||
|                                 ...(drawerOpen && { display: 'none' }), |                         subscriptions={subscriptions} | ||||||
|                             }} |                         selectedSubscription={selectedSubscription} | ||||||
|                         > |                         mobileDrawerOpen={mobileDrawerOpen} | ||||||
|                             <MenuIcon /> |                         onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} | ||||||
|                         </IconButton> |                         onSubscriptionClick={(subscriptionId) => setSelectedSubscription(subscriptions.get(subscriptionId))} | ||||||
|                         <Typography |                         onSubscribeSubmit={handleSubscribeSubmit} | ||||||
|                             component="h1" |                     /> | ||||||
|                             variant="h6" |                 </Box> | ||||||
|                             color="inherit" |  | ||||||
|                             noWrap |  | ||||||
|                             sx={{ flexGrow: 1 }} |  | ||||||
|                         > |  | ||||||
|                             {(selectedSubscription !== null) ? selectedSubscription.shortUrl() : "ntfy"} |  | ||||||
|                         </Typography> |  | ||||||
|                         {selectedSubscription !== null && <SubscriptionSettings |  | ||||||
|                             subscription={selectedSubscription} |  | ||||||
|                             onClearAll={handleClearAll} |  | ||||||
|                             onUnsubscribe={handleUnsubscribe} |  | ||||||
|                         />} |  | ||||||
|                     </Toolbar> |  | ||||||
|                 </AppBar> |  | ||||||
|                 <Drawer variant="permanent" open={drawerOpen}> |  | ||||||
|                     <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 onClick={() => setSubscribeDialogOpen(true)}> |  | ||||||
|                             <ListItemIcon> |  | ||||||
|                                 <AddIcon /> |  | ||||||
|                             </ListItemIcon> |  | ||||||
|                             <ListItemText primary="Add subscription" /> |  | ||||||
|                         </ListItemButton> |  | ||||||
|                     </List> |  | ||||||
|                 </Drawer> |  | ||||||
|                 <Box |                 <Box | ||||||
|                     component="main" |                     component="main" | ||||||
|                     sx={{ |                     sx={{ | ||||||
|                         backgroundColor: (theme) => |  | ||||||
|                             theme.palette.mode === 'light' |  | ||||||
|                                 ? theme.palette.grey[100] |  | ||||||
|                                 : theme.palette.grey[900], |  | ||||||
|                         flexGrow: 1, |                         flexGrow: 1, | ||||||
|  |                         p: 3, | ||||||
|  |                         width: {sm: `calc(100% - ${drawerWidth}px)`}, | ||||||
|                         height: '100vh', |                         height: '100vh', | ||||||
|                         overflow: 'auto', |                         overflow: 'auto', | ||||||
|                     }} |                         backgroundColor: (theme) => theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900] | ||||||
|                 > |                 }}> | ||||||
|                     <Toolbar /> |                     <Toolbar/> | ||||||
|                     <NotificationList notifications={notifications}/> |                     <NotificationList notifications={notifications}/> | ||||||
|                 </Box> |                 </Box> | ||||||
|             </Box> |             </Box> | ||||||
|             <AddDialog |  | ||||||
|                 open={subscribeDialogOpen} |  | ||||||
|                 onCancel={handleSubscribeCancel} |  | ||||||
|                 onSubmit={handleSubscribeSubmit} |  | ||||||
|             /> |  | ||||||
|         </ThemeProvider> |         </ThemeProvider> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import MoreVertIcon from "@mui/icons-material/MoreVert"; | ||||||
| import api from "../app/Api"; | import api from "../app/Api"; | ||||||
| 
 | 
 | ||||||
| // Originally from https://mui.com/components/menus/#MenuListComposition.js
 | // Originally from https://mui.com/components/menus/#MenuListComposition.js
 | ||||||
| const SubscriptionSettings = (props) => { | const IconSubscribeSettings = (props) => { | ||||||
|     const [open, setOpen] = useState(false); |     const [open, setOpen] = useState(false); | ||||||
|     const anchorRef = useRef(null); |     const anchorRef = useRef(null); | ||||||
| 
 | 
 | ||||||
|  | @ -114,4 +114,4 @@ const SubscriptionSettings = (props) => { | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default SubscriptionSettings; | export default IconSubscribeSettings; | ||||||
|  | @ -12,7 +12,7 @@ import Subscription from "../app/Subscription"; | ||||||
| const defaultBaseUrl = "http://127.0.0.1" | const defaultBaseUrl = "http://127.0.0.1" | ||||||
| //const defaultBaseUrl = "https://ntfy.sh"
 | //const defaultBaseUrl = "https://ntfy.sh"
 | ||||||
| 
 | 
 | ||||||
| const AddDialog = (props) => { | const SubscribeDialog = (props) => { | ||||||
|     const [topic, setTopic] = useState(""); |     const [topic, setTopic] = useState(""); | ||||||
|     const handleCancel = () => { |     const handleCancel = () => { | ||||||
|         setTopic(''); |         setTopic(''); | ||||||
|  | @ -53,4 +53,4 @@ const AddDialog = (props) => { | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default AddDialog; | export default SubscribeDialog; | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue