diff --git a/web/src/App.js b/web/src/App.js index d8c1c21f..b75e6433 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -3,44 +3,56 @@ import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Link from '@mui/material/Link'; -import {useState} from "react"; +import {useEffect, useState} from "react"; import Subscription from './Subscription'; import WsConnection from './WsConnection'; -function SubscriptionList(props) { +const SubscriptionList = (props) => { + const subscriptions = props.subscriptions; return (
- {props.subscriptions.map(subscription => - )} + {Object.keys(subscriptions).map(id => + props.handleSubscriptionClick(id)} + />) + }
); } -function SubscriptionItem(props) { +const SubscriptionItem = (props) => { const subscription = props.subscription; return ( -
-
{subscription.shortUrl()}
-
+ <> +
+ {subscription.shortUrl()} +
+ ); } -function NotificationList(props) { +const NotificationList = (props) => { return (
- {props.notifications.map(notification => )} -
{props.timestamp}
-
{props.message}
+ {props.notifications.map(notification => + )}
); } const NotificationItem = (props) => { + const notification = props.notification; return ( -
-
{props.time}
-
{props.message}
-
+ <> +
{notification.time}
+
{notification.message}
+ ); } @@ -67,20 +79,23 @@ const SubscriptionAddForm = (props) => { } const App = () => { - const [state, setState] = useState({ - subscriptions: [], - }); - const notifications = [ - {id: "qGrfmhp3vK", times: 1645193395, message: "Message 1"}, - {id: "m4YYjfxwyT", times: 1645193428, message: "Message 2"} - ]; - const addSubscription = (newSubscription) => { - const connection = new WsConnection(newSubscription.wsUrl()); + 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})); 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 ( @@ -88,7 +103,11 @@ const App = () => { ntfy - + diff --git a/web/src/Subscription.js b/web/src/Subscription.js index 7b517d4d..1222ee2a 100644 --- a/web/src/Subscription.js +++ b/web/src/Subscription.js @@ -1,15 +1,26 @@ import {topicUrl, shortTopicUrl, topicUrlWs} from './utils'; export default class Subscription { - url = ''; + id = ''; baseUrl = ''; topic = ''; notifications = []; + lastActive = null; constructor(baseUrl, topic) { - this.url = topicUrl(baseUrl, topic); + this.id = topicUrl(baseUrl, topic); this.baseUrl = baseUrl; this.topic = topic; } + addNotification(notification) { + if (notification.time === null) { + return; + } + this.notifications.push(notification); + this.lastActive = notification.time; + } + url() { + return this.id; + } wsUrl() { return topicUrlWs(this.baseUrl, this.topic); } diff --git a/web/src/WsConnection.js b/web/src/WsConnection.js index c9d7eb3c..4e9dfafa 100644 --- a/web/src/WsConnection.js +++ b/web/src/WsConnection.js @@ -1,28 +1,47 @@ export default class WsConnection { - constructor(url) { - this.url = url; + id = ''; + constructor(subscription, onNotification) { + this.id = subscription.id; + this.subscription = subscription; + this.onNotification = onNotification; this.ws = null; } start() { - const socket = new WebSocket(this.url); - socket.onopen = function(e) { - console.log(this.url, "[open] Connection established"); + const socket = new WebSocket(this.subscription.wsUrl()); + socket.onopen = (event) => { + 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) { - console.log(this.url, `[message] Data received from server: ${event.data}`); - }; - socket.onclose = function(event) { + socket.onclose = (event) => { 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 { - console.log(this.url, `[close] Connection died`); + console.log(this.id, `[close] Connection died`); // e.g. server process killed or network down // event.code is usually 1006 in this case } }; - socket.onerror = function(error) { - console.log(this.url, `[error] ${error.message}`); + socket.onerror = (event) => { + console.log(this.id, `[error] ${event.message}`); }; this.ws = socket; }