mirror of
				https://github.com/binwiederhier/ntfy.git
				synced 2025-10-31 13:02:24 +01:00 
			
		
		
		
	Attempt to use react router the way it was meant to
This commit is contained in:
		
							parent
							
								
									4aad98256a
								
							
						
					
					
						commit
						55c021796e
					
				
					 3 changed files with 63 additions and 57 deletions
				
			
		web/src
|  | @ -10,6 +10,7 @@ export const topicUrlJsonPollWithSince = (baseUrl, topic, since) => `${topicUrlJ | ||||||
| export const topicUrlAuth = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/auth`; | export const topicUrlAuth = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/auth`; | ||||||
| export const topicShortUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic)); | export const topicShortUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic)); | ||||||
| export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, ""); | export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, ""); | ||||||
|  | export const expandUrl = (url) => [`https://${url}`, `http://${url}`]; | ||||||
| 
 | 
 | ||||||
| export const validUrl = (url) => { | export const validUrl = (url) => { | ||||||
|     return url.match(/^https?:\/\//); |     return url.match(/^https?:\/\//); | ||||||
|  |  | ||||||
|  | @ -22,8 +22,8 @@ import subscriptionManager from "../app/SubscriptionManager"; | ||||||
| const ActionBar = (props) => { | const ActionBar = (props) => { | ||||||
|     const location = useLocation(); |     const location = useLocation(); | ||||||
|     let title = "ntfy"; |     let title = "ntfy"; | ||||||
|     if (props.selectedSubscription) { |     if (props.selected) { | ||||||
|         title = topicShortUrl(props.selectedSubscription.baseUrl, props.selectedSubscription.topic); |         title = topicShortUrl(props.selected.baseUrl, props.selected.topic); | ||||||
|     } else if (location.pathname === "/settings") { |     } else if (location.pathname === "/settings") { | ||||||
|         title = "Settings"; |         title = "Settings"; | ||||||
|     } |     } | ||||||
|  | @ -50,8 +50,8 @@ const ActionBar = (props) => { | ||||||
|                 <Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}> |                 <Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}> | ||||||
|                     {title} |                     {title} | ||||||
|                 </Typography> |                 </Typography> | ||||||
|                 {props.selectedSubscription && <SettingsIcon |                 {props.selected && <SettingsIcon | ||||||
|                     subscription={props.selectedSubscription} |                     subscription={props.selected} | ||||||
|                     onUnsubscribe={props.onUnsubscribe} |                     onUnsubscribe={props.onUnsubscribe} | ||||||
|                 />} |                 />} | ||||||
|             </Toolbar> |             </Toolbar> | ||||||
|  |  | ||||||
|  | @ -12,16 +12,12 @@ import ActionBar from "./ActionBar"; | ||||||
| import notifier from "../app/Notifier"; | import notifier from "../app/Notifier"; | ||||||
| import Preferences from "./Preferences"; | import Preferences from "./Preferences"; | ||||||
| import {useLiveQuery} from "dexie-react-hooks"; | import {useLiveQuery} from "dexie-react-hooks"; | ||||||
| import poller from "../app/Poller"; |  | ||||||
| import pruner from "../app/Pruner"; |  | ||||||
| import subscriptionManager from "../app/SubscriptionManager"; | import subscriptionManager from "../app/SubscriptionManager"; | ||||||
| import userManager from "../app/UserManager"; | import userManager from "../app/UserManager"; | ||||||
| import {BrowserRouter, Route, Routes, useLocation, useNavigate} from "react-router-dom"; | import {BrowserRouter, Route, Routes, useNavigate, useParams} from "react-router-dom"; | ||||||
| import {subscriptionRoute} from "../app/utils"; | import {expandUrl, subscriptionRoute} from "../app/utils"; | ||||||
| 
 | 
 | ||||||
| // TODO support unsubscribed routes
 | // TODO support unsubscribed routes
 | ||||||
| // TODO add "home" route that is selected when nothing else fits
 |  | ||||||
| // TODO new notification indicator
 |  | ||||||
| // TODO "copy url" toast
 | // TODO "copy url" toast
 | ||||||
| // TODO "copy link url" button
 | // TODO "copy link url" button
 | ||||||
| // TODO races when two tabs are open
 | // TODO races when two tabs are open
 | ||||||
|  | @ -32,42 +28,78 @@ const App = () => { | ||||||
|         <BrowserRouter> |         <BrowserRouter> | ||||||
|             <ThemeProvider theme={theme}> |             <ThemeProvider theme={theme}> | ||||||
|                 <CssBaseline/> |                 <CssBaseline/> | ||||||
|                 <Root/> |                 <Content/> | ||||||
|             </ThemeProvider> |             </ThemeProvider> | ||||||
|         </BrowserRouter> |         </BrowserRouter> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Root = () => { | const Content = () => { | ||||||
|  |     const subscriptions = useLiveQuery(() => subscriptionManager.all()); | ||||||
|  |     // const context = { subscriptions };
 | ||||||
|  |     return ( | ||||||
|  |         <Routes> | ||||||
|  |             <Route path="settings" element={<PrefLayout subscriptions={subscriptions}/>} /> | ||||||
|  |             <Route path="settings" element={<PrefLayout subscriptions={subscriptions}/>} /> | ||||||
|  |             <Route path="/" element={<AllSubscriptions subscriptions={subscriptions}/>} /> | ||||||
|  |             <Route path=":baseUrl/:topic" element={<SingleSubscription subscriptions={subscriptions}/>} /> | ||||||
|  |             <Route path=":topic" element={<SingleSubscription subscriptions={subscriptions}/>} /> | ||||||
|  |         </Routes> | ||||||
|  |     ) | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const AllSubscriptions = (props) => { | ||||||
|  |     return ( | ||||||
|  |         <Layout subscriptions={props.subscriptions}> | ||||||
|  |             <Notifications mode="all" subscriptions={props.subscriptions}/> | ||||||
|  |         </Layout> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const SingleSubscription = (props) => { | ||||||
|  |     const params = useParams(); | ||||||
|  |     const [selected] = (props.subscriptions || []).filter(s => { | ||||||
|  |         return (params.baseUrl && expandUrl(params.baseUrl).includes(s.baseUrl) && params.topic === s.topic) | ||||||
|  |             || (window.location.origin === s.baseUrl && params.topic === s.topic) | ||||||
|  |     }); | ||||||
|  |     return ( | ||||||
|  |         <Layout subscriptions={props.subscriptions} selected={selected}> | ||||||
|  |             <Notifications mode="one" subscription={selected}/> | ||||||
|  |         </Layout> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const PrefLayout = (props) => { | ||||||
|  |     return ( | ||||||
|  |         <Layout subscriptions={props.subscriptions}> | ||||||
|  |             <Preferences/> | ||||||
|  |         </Layout> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Layout = (props) => { | ||||||
|  |     const subscriptions = props.subscriptions; // May be null/undefined
 | ||||||
|  |     const selected = props.selected; // May be null/undefined
 | ||||||
|     const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); |     const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); | ||||||
|     const [notificationsGranted, setNotificationsGranted] = useState(notifier.granted()); |     const [notificationsGranted, setNotificationsGranted] = useState(notifier.granted()); | ||||||
|     const location = useLocation(); |  | ||||||
|     const users = useLiveQuery(() => userManager.all()); |     const users = useLiveQuery(() => userManager.all()); | ||||||
|     const subscriptions = useLiveQuery(() => subscriptionManager.all()); |  | ||||||
|     const selectedSubscription = findSelected(location, subscriptions); |  | ||||||
|     const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0; |     const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0; | ||||||
| 
 | 
 | ||||||
|     useConnectionListeners(); |     useConnectionListeners(); | ||||||
| 
 | 
 | ||||||
|     useEffect(() => { |     useEffect(() => connectionManager.refresh(subscriptions, users), [subscriptions, users]); | ||||||
|         connectionManager.refresh(subscriptions, users); |     useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]); | ||||||
|     }, [subscriptions, users]); // Dangle!
 |  | ||||||
| 
 |  | ||||||
|     useEffect(() => { |  | ||||||
|         document.title = (newNotificationsCount > 0) ? `(${newNotificationsCount}) ntfy web` : "ntfy web"; |  | ||||||
|     }, [newNotificationsCount]); |  | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <Box sx={{display: 'flex'}}> |         <Box sx={{display: 'flex'}}> | ||||||
|             <CssBaseline/> |             <CssBaseline/> | ||||||
|             <ActionBar |             <ActionBar | ||||||
|                 subscriptions={subscriptions} |                 selected={selected} | ||||||
|                 selectedSubscription={selectedSubscription} |  | ||||||
|                 onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} |                 onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} | ||||||
|             /> |             /> | ||||||
|             <Navigation |             <Navigation | ||||||
|                 subscriptions={subscriptions} |                 subscriptions={subscriptions} | ||||||
|                 selectedSubscription={selectedSubscription} |                 selectedSubscription={selected} | ||||||
|                 notificationsGranted={notificationsGranted} |                 notificationsGranted={notificationsGranted} | ||||||
|                 mobileDrawerOpen={mobileDrawerOpen} |                 mobileDrawerOpen={mobileDrawerOpen} | ||||||
|                 onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} |                 onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} | ||||||
|  | @ -75,12 +107,7 @@ const Root = () => { | ||||||
|             /> |             /> | ||||||
|             <Main> |             <Main> | ||||||
|                 <Toolbar/> |                 <Toolbar/> | ||||||
|                 <Routes> |                 {props.children} | ||||||
|                     <Route path="settings" element={<Preferences />} /> |  | ||||||
|                     <Route path="/" element={<Notifications mode="all" subscriptions={subscriptions} />} /> |  | ||||||
|                     <Route path=":baseUrl/:topic" element={<Notifications mode="one" subscription={selectedSubscription}/>} /> |  | ||||||
|                     <Route path=":topic" element={<Notifications mode="one" subscription={selectedSubscription}/>} /> |  | ||||||
|                 </Routes> |  | ||||||
|             </Main> |             </Main> | ||||||
|         </Box> |         </Box> | ||||||
|     ); |     ); | ||||||
|  | @ -107,32 +134,6 @@ const Main = (props) => { | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const findSelected = (location, subscriptions) => { |  | ||||||
|     if (!subscriptions || !location)  { |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|     const [subscription] = subscriptions.filter(s => location.pathname === subscriptionRoute(s)); |  | ||||||
|     return subscription; |  | ||||||
| 
 |  | ||||||
|     /* |  | ||||||
|     if (location.pathname === "/" || location.pathname === "/settings") { |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|     if (!subscription) { |  | ||||||
|         const [, topic] = location.pathname.split("/"); |  | ||||||
|         const subscription = { |  | ||||||
|             id: topicUrl(window.location.origin, topic), |  | ||||||
|             baseUrl: window.location.origin, |  | ||||||
|             topic: topic, |  | ||||||
|             last: "" |  | ||||||
|         } |  | ||||||
|         subscriptionManager.save(subscription); |  | ||||||
|         return subscription; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|      */ |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const useConnectionListeners = () => { | const useConnectionListeners = () => { | ||||||
|     const navigate = useNavigate(); |     const navigate = useNavigate(); | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|  | @ -155,4 +156,8 @@ const useConnectionListeners = () => { | ||||||
|     []); |     []); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const updateTitle = (newNotificationsCount) => { | ||||||
|  |     document.title = (newNotificationsCount > 0) ? `(${newNotificationsCount}) ntfy web` : "ntfy web"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export default App; | export default App; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Philipp Heckel
						Philipp Heckel