From 1fe598a966d888d36d9dd18ab6e8ba1c51f33a77 Mon Sep 17 00:00:00 2001
From: Philipp Heckel <pheckel@datto.com>
Date: Fri, 18 Feb 2022 14:41:01 -0500
Subject: [PATCH] Split stuff

---
 web/src/App.js          | 50 ++++++++++++++---------------------------
 web/src/ProTip.js       | 22 ------------------
 web/src/Subscription.js | 19 ++++++++++++++++
 web/src/WsConnection.js | 34 ++++++++++++++++++++++++++++
 web/src/utils.js        |  6 +++++
 5 files changed, 76 insertions(+), 55 deletions(-)
 delete mode 100644 web/src/ProTip.js
 create mode 100644 web/src/Subscription.js
 create mode 100644 web/src/WsConnection.js
 create mode 100644 web/src/utils.js

diff --git a/web/src/App.js b/web/src/App.js
index 7d52df94..d8c1c21f 100644
--- a/web/src/App.js
+++ b/web/src/App.js
@@ -3,38 +3,24 @@ 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 ProTip from './ProTip';
 import {useState} from "react";
-
-function Copyright() {
-    return (
-        <Typography variant="body2" color="text.secondary" align="center">
-            {'Copyright © '}
-            <Link color="inherit" href="https://mui.com/">
-                Your Website
-            </Link>{' '}
-            {new Date().getFullYear()}
-            {'.'}
-        </Typography>
-    );
-}
-
-const topicUrl = (baseUrl, topic) => `${baseUrl}/${topic}`;
-const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
-const shortTopicUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic))
+import Subscription from './Subscription';
+import WsConnection from './WsConnection';
 
 function SubscriptionList(props) {
     return (
         <div className="subscriptionList">
-            {props.subscriptions.map(subscription => <SubscriptionItem key={topicUrl(subscription.base_url, subscription.topic)} {...subscription}/>)}
+            {props.subscriptions.map(subscription =>
+                <SubscriptionItem key={subscription.url} subscription={subscription}/>)}
         </div>
     );
 }
 
 function SubscriptionItem(props) {
+    const subscription = props.subscription;
     return (
         <div>
-            <div>{shortTopicUrl(props.base_url, props.topic)}</div>
+            <div>{subscription.shortUrl()}</div>
         </div>
     );
 }
@@ -49,7 +35,7 @@ function NotificationList(props) {
     );
 }
 
-function NotificationItem(props) {
+const NotificationItem = (props) => {
     return (
         <div>
             <div className="date">{props.time}</div>
@@ -58,14 +44,14 @@ function NotificationItem(props) {
     );
 }
 
-function SubscriptionAddForm(props) {
+const defaultBaseUrl = "https://ntfy.sh"
+
+const SubscriptionAddForm = (props) => {
     const [topic, setTopic] = useState("");
     const handleSubmit = (ev) => {
         ev.preventDefault();
-        props.onSubmit({
-            base_url: "https://ntfy.sh",
-            topic: topic,
-        });
+        props.onSubmit(new Subscription(defaultBaseUrl, topic));
+        setTopic('');
     }
     return (
         <form onSubmit={handleSubmit}>
@@ -80,19 +66,17 @@ function SubscriptionAddForm(props) {
     );
 }
 
-export default function App() {
+const App = () => {
     const [state, setState] = useState({
         subscriptions: [],
     });
-    /*const subscriptions = [
-        {base_url: "https://ntfy.sh", topic: "mytopic"},
-        {base_url: "https://ntfy.sh", topic: "phils_alerts"},
-    ];*/
     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());
+        connection.start();
         setState(prevState => ({
             subscriptions: [...prevState.subscriptions, newSubscription],
         }));
@@ -106,9 +90,9 @@ export default function App() {
                 <SubscriptionAddForm onSubmit={addSubscription}/>
                 <SubscriptionList subscriptions={state.subscriptions}/>
                 <NotificationList notifications={notifications}/>
-                <ProTip/>
-                <Copyright/>
             </Box>
         </Container>
     );
 }
+
+export default App;
diff --git a/web/src/ProTip.js b/web/src/ProTip.js
deleted file mode 100644
index 6d81f33b..00000000
--- a/web/src/ProTip.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import * as React from 'react';
-import Link from '@mui/material/Link';
-import SvgIcon from '@mui/material/SvgIcon';
-import Typography from '@mui/material/Typography';
-
-function LightBulbIcon(props) {
-  return (
-    <SvgIcon {...props}>
-      <path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z" />
-    </SvgIcon>
-  );
-}
-
-export default function ProTip() {
-  return (
-    <Typography sx={{ mt: 6, mb: 3 }} color="text.secondary">
-      <LightBulbIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
-      Pro tip: See more <Link href="https://mui.com/getting-started/templates/">templates</Link> on
-      the MUI documentation.
-    </Typography>
-  );
-}
diff --git a/web/src/Subscription.js b/web/src/Subscription.js
new file mode 100644
index 00000000..7b517d4d
--- /dev/null
+++ b/web/src/Subscription.js
@@ -0,0 +1,19 @@
+import {topicUrl, shortTopicUrl, topicUrlWs} from './utils';
+
+export default class Subscription {
+    url = '';
+    baseUrl = '';
+    topic = '';
+    notifications = [];
+    constructor(baseUrl, topic) {
+        this.url = topicUrl(baseUrl, topic);
+        this.baseUrl = baseUrl;
+        this.topic = topic;
+    }
+    wsUrl() {
+        return topicUrlWs(this.baseUrl, this.topic);
+    }
+    shortUrl() {
+        return shortTopicUrl(this.baseUrl, this.topic);
+    }
+}
diff --git a/web/src/WsConnection.js b/web/src/WsConnection.js
new file mode 100644
index 00000000..c9d7eb3c
--- /dev/null
+++ b/web/src/WsConnection.js
@@ -0,0 +1,34 @@
+
+export default class WsConnection {
+    constructor(url) {
+        this.url = url;
+        this.ws = null;
+    }
+    start() {
+        const socket = new WebSocket(this.url);
+        socket.onopen = function(e) {
+            console.log(this.url, "[open] Connection established");
+        };
+        socket.onmessage = function(event) {
+            console.log(this.url, `[message] Data received from server: ${event.data}`);
+        };
+        socket.onclose = function(event) {
+            if (event.wasClean) {
+                console.log(this.url, `[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
+            } else {
+                console.log(this.url, `[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}`);
+        };
+        this.ws = socket;
+    }
+    cancel() {
+        if (this.ws != null) {
+            this.ws.close();
+        }
+    }
+}
diff --git a/web/src/utils.js b/web/src/utils.js
new file mode 100644
index 00000000..b902a7e5
--- /dev/null
+++ b/web/src/utils.js
@@ -0,0 +1,6 @@
+export const topicUrl = (baseUrl, topic) => `${baseUrl}/${topic}`;
+export const topicUrlWs = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/ws`
+    .replaceAll("https://", "wss://")
+    .replaceAll("http://", "ws://");
+export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
+export const shortTopicUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic));