Make topics clickable, show notifications
This commit is contained in:
		
							parent
							
								
									1fe598a966
								
							
						
					
					
						commit
						b497063af4
					
				
					 3 changed files with 94 additions and 45 deletions
				
			
		|  | @ -3,44 +3,56 @@ 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 {useState} from "react"; | import {useEffect, useState} from "react"; | ||||||
| import Subscription from './Subscription'; | import Subscription from './Subscription'; | ||||||
| import WsConnection from './WsConnection'; | import WsConnection from './WsConnection'; | ||||||
| 
 | 
 | ||||||
| function SubscriptionList(props) { | const SubscriptionList = (props) => { | ||||||
|  |     const subscriptions = props.subscriptions; | ||||||
|     return ( |     return ( | ||||||
|         <div className="subscriptionList"> |         <div className="subscriptionList"> | ||||||
|             {props.subscriptions.map(subscription => |             {Object.keys(subscriptions).map(id => | ||||||
|                 <SubscriptionItem key={subscription.url} subscription={subscription}/>)} |                 <SubscriptionItem | ||||||
|  |                     key={id} | ||||||
|  |                     subscription={subscriptions[id]} | ||||||
|  |                     selected={props.selectedSubscription === subscriptions[id]} | ||||||
|  |                     onClick={() => props.handleSubscriptionClick(id)} | ||||||
|  |                 />) | ||||||
|  |             } | ||||||
|         </div> |         </div> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function SubscriptionItem(props) { | const SubscriptionItem = (props) => { | ||||||
|     const subscription = props.subscription; |     const subscription = props.subscription; | ||||||
|     return ( |     return ( | ||||||
|         <div> |         <> | ||||||
|             <div>{subscription.shortUrl()}</div> |             <div | ||||||
|         </div> |                 onClick={props.onClick} | ||||||
|  |                 style={{ fontWeight: props.selected ? 'bold' : '' }} | ||||||
|  |             > | ||||||
|  |                 {subscription.shortUrl()} | ||||||
|  |             </div> | ||||||
|  |         </> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function NotificationList(props) { | const NotificationList = (props) => { | ||||||
|     return ( |     return ( | ||||||
|         <div className="notificationList"> |         <div className="notificationList"> | ||||||
|             {props.notifications.map(notification => <NotificationItem key={notification.id} {...notification}/>)} |             {props.notifications.map(notification => | ||||||
|             <div className="date">{props.timestamp}</div> |                 <NotificationItem key={notification.id} notification={notification}/>)} | ||||||
|             <div className="message">{props.message}</div> |  | ||||||
|         </div> |         </div> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const NotificationItem = (props) => { | const NotificationItem = (props) => { | ||||||
|  |     const notification = props.notification; | ||||||
|     return ( |     return ( | ||||||
|         <div> |         <> | ||||||
|             <div className="date">{props.time}</div> |             <div className="date">{notification.time}</div> | ||||||
|             <div className="message">{props.message}</div> |             <div className="message">{notification.message}</div> | ||||||
|         </div> |         </> | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -67,20 +79,23 @@ const SubscriptionAddForm = (props) => { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const App = () => { | const App = () => { | ||||||
|     const [state, setState] = useState({ |     const [subscriptions, setSubscriptions] = useState({}); | ||||||
|         subscriptions: [], |     const [selectedSubscription, setSelectedSubscription] = useState(null); | ||||||
|     }); |     const [connections, setConnections] = useState({}); | ||||||
|     const notifications = [ |     const subscriptionChanged = (subscription) => { | ||||||
|         {id: "qGrfmhp3vK", times: 1645193395, message: "Message 1"}, |         setSubscriptions(prev => ({...prev, [subscription.id]: subscription})); // Fake-replace
 | ||||||
|         {id: "m4YYjfxwyT", times: 1645193428, message: "Message 2"} |     }; | ||||||
|     ]; |     const addSubscription = (subscription) => { | ||||||
|     const addSubscription = (newSubscription) => { |         const connection = new WsConnection(subscription, subscriptionChanged); | ||||||
|         const connection = new WsConnection(newSubscription.wsUrl()); |         setSubscriptions(prev => ({...prev, [subscription.id]: subscription})); | ||||||
|  |         setConnections(prev => ({...prev, [connection.id]: connection})); | ||||||
|         connection.start(); |         connection.start(); | ||||||
|         setState(prevState => ({ |     }; | ||||||
|             subscriptions: [...prevState.subscriptions, newSubscription], |     const handleSubscriptionClick = (subscriptionId) => { | ||||||
|         })); |         console.log(`handleSubscriptionClick ${subscriptionId}`) | ||||||
|     } |         setSelectedSubscription(subscriptions[subscriptionId]); | ||||||
|  |     }; | ||||||
|  |     const notifications = (selectedSubscription !== null) ? selectedSubscription.notifications : []; | ||||||
|     return ( |     return ( | ||||||
|         <Container maxWidth="sm"> |         <Container maxWidth="sm"> | ||||||
|             <Box sx={{my: 4}}> |             <Box sx={{my: 4}}> | ||||||
|  | @ -88,7 +103,11 @@ const App = () => { | ||||||
|                     ntfy |                     ntfy | ||||||
|                 </Typography> |                 </Typography> | ||||||
|                 <SubscriptionAddForm onSubmit={addSubscription}/> |                 <SubscriptionAddForm onSubmit={addSubscription}/> | ||||||
|                 <SubscriptionList subscriptions={state.subscriptions}/> |                 <SubscriptionList | ||||||
|  |                     subscriptions={subscriptions} | ||||||
|  |                     selectedSubscription={selectedSubscription} | ||||||
|  |                     handleSubscriptionClick={handleSubscriptionClick} | ||||||
|  |                 /> | ||||||
|                 <NotificationList notifications={notifications}/> |                 <NotificationList notifications={notifications}/> | ||||||
|             </Box> |             </Box> | ||||||
|         </Container> |         </Container> | ||||||
|  |  | ||||||
|  | @ -1,15 +1,26 @@ | ||||||
| import {topicUrl, shortTopicUrl, topicUrlWs} from './utils'; | import {topicUrl, shortTopicUrl, topicUrlWs} from './utils'; | ||||||
| 
 | 
 | ||||||
| export default class Subscription { | export default class Subscription { | ||||||
|     url = ''; |     id = ''; | ||||||
|     baseUrl = ''; |     baseUrl = ''; | ||||||
|     topic = ''; |     topic = ''; | ||||||
|     notifications = []; |     notifications = []; | ||||||
|  |     lastActive = null; | ||||||
|     constructor(baseUrl, topic) { |     constructor(baseUrl, topic) { | ||||||
|         this.url = topicUrl(baseUrl, topic); |         this.id = topicUrl(baseUrl, topic); | ||||||
|         this.baseUrl = baseUrl; |         this.baseUrl = baseUrl; | ||||||
|         this.topic = topic; |         this.topic = topic; | ||||||
|     } |     } | ||||||
|  |     addNotification(notification) { | ||||||
|  |         if (notification.time === null) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         this.notifications.push(notification); | ||||||
|  |         this.lastActive = notification.time; | ||||||
|  |     } | ||||||
|  |     url() { | ||||||
|  |         return this.id; | ||||||
|  |     } | ||||||
|     wsUrl() { |     wsUrl() { | ||||||
|         return topicUrlWs(this.baseUrl, this.topic); |         return topicUrlWs(this.baseUrl, this.topic); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,28 +1,47 @@ | ||||||
| 
 | 
 | ||||||
| export default class WsConnection { | export default class WsConnection { | ||||||
|     constructor(url) { |     id = ''; | ||||||
|         this.url = url; |     constructor(subscription, onNotification) { | ||||||
|  |         this.id = subscription.id; | ||||||
|  |         this.subscription = subscription; | ||||||
|  |         this.onNotification = onNotification; | ||||||
|         this.ws = null; |         this.ws = null; | ||||||
|     } |     } | ||||||
|     start() { |     start() { | ||||||
|         const socket = new WebSocket(this.url); |         const socket = new WebSocket(this.subscription.wsUrl()); | ||||||
|         socket.onopen = function(e) { |         socket.onopen = (event) => { | ||||||
|             console.log(this.url, "[open] Connection established"); |             console.log(this.id, "[open] Connection established"); | ||||||
|  |         } | ||||||
|  |         socket.onmessage = (event) => { | ||||||
|  |             console.log(this.id, `[message] Data received from server: ${event.data}`); | ||||||
|  |             try { | ||||||
|  |                 const data = JSON.parse(event.data); | ||||||
|  |                 const relevantAndValid = | ||||||
|  |                     data.event === 'message' && | ||||||
|  |                     'id' in data && | ||||||
|  |                     'time' in data && | ||||||
|  |                     'message' in data; | ||||||
|  |                 if (!relevantAndValid) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 console.log('adding') | ||||||
|  |                 this.subscription.addNotification(data); | ||||||
|  |                 this.onNotification(this.subscription); | ||||||
|  |             } catch (e) { | ||||||
|  |                 console.log(this.id, `[message] Error handling message: ${e}`); | ||||||
|  |             } | ||||||
|         }; |         }; | ||||||
|         socket.onmessage = function(event) { |         socket.onclose = (event) => { | ||||||
|             console.log(this.url, `[message] Data received from server: ${event.data}`); |  | ||||||
|         }; |  | ||||||
|         socket.onclose = function(event) { |  | ||||||
|             if (event.wasClean) { |             if (event.wasClean) { | ||||||
|                 console.log(this.url, `[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); |                 console.log(this.id, `[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); | ||||||
|             } else { |             } else { | ||||||
|                 console.log(this.url, `[close] Connection died`); |                 console.log(this.id, `[close] Connection died`); | ||||||
|                 // e.g. server process killed or network down
 |                 // e.g. server process killed or network down
 | ||||||
|                 // event.code is usually 1006 in this case
 |                 // event.code is usually 1006 in this case
 | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         socket.onerror = function(error) { |         socket.onerror = (event) => { | ||||||
|             console.log(this.url, `[error] ${error.message}`); |             console.log(this.id, `[error] ${event.message}`); | ||||||
|         }; |         }; | ||||||
|         this.ws = socket; |         this.ws = socket; | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue