diff --git a/web/package.json b/web/package.json
index 735efcd..c1749e0 100644
--- a/web/package.json
+++ b/web/package.json
@@ -15,7 +15,6 @@
"@mui/styles": "^5.4.2",
"react": "latest",
"react-dom": "latest",
- "react-router-dom": "^6.2.1",
"react-scripts": "^3.0.1"
},
"browserslist": {
diff --git a/web/public/index.html b/web/public/index.html
index db52435..c02c095 100644
--- a/web/public/index.html
+++ b/web/public/index.html
@@ -3,7 +3,7 @@
- ntfy.sh | Send push notifications to your phone via PUT/POST
+ ntfy web
diff --git a/web/src/app/utils.js b/web/src/app/utils.js
index ea79661..0c04a27 100644
--- a/web/src/app/utils.js
+++ b/web/src/app/utils.js
@@ -11,8 +11,12 @@ export const topicUrlAuth = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/aut
export const topicShortUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic));
export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
+export const validUrl = (url) => {
+ return url.match(/^https?:\/\//);
+}
+
export const validTopic = (topic) => {
- return topic.match(/^([-_a-zA-Z0-9]{1,64})$/) // Regex must match Go & Android app!
+ return topic.match(/^([-_a-zA-Z0-9]{1,64})$/); // Regex must match Go & Android app!
}
// Format emojis (see emoji.js)
diff --git a/web/src/components/ActionBar.js b/web/src/components/ActionBar.js
index 132f47e..6970f4f 100644
--- a/web/src/components/ActionBar.js
+++ b/web/src/components/ActionBar.js
@@ -6,6 +6,7 @@ import MenuIcon from "@mui/icons-material/Menu";
import Typography from "@mui/material/Typography";
import IconSubscribeSettings from "./IconSubscribeSettings";
import * as React from "react";
+import Box from "@mui/material/Box";
const ActionBar = (props) => {
const title = (props.selectedSubscription !== null)
@@ -26,7 +27,11 @@ const ActionBar = (props) => {
>
-
+
{title}
diff --git a/web/src/components/App.js b/web/src/components/App.js
index e55bd95..a95e8d7 100644
--- a/web/src/components/App.js
+++ b/web/src/components/App.js
@@ -15,6 +15,7 @@ import ActionBar from "./ActionBar";
import Users from "../app/Users";
import notificationManager from "../app/NotificationManager";
import NoTopics from "./NoTopics";
+import Preferences from "./Preferences";
// TODO subscribe dialog:
// - check/use existing user
@@ -26,10 +27,15 @@ const App = () => {
console.log(`[App] Rendering main view`);
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
+ const [prefsOpen, setPrefsOpen] = useState(false);
const [subscriptions, setSubscriptions] = useState(new Subscriptions());
const [users, setUsers] = useState(new Users());
const [selectedSubscription, setSelectedSubscription] = useState(null);
const [notificationsGranted, setNotificationsGranted] = useState(notificationManager.granted());
+ const handleSubscriptionClick = (subscriptionId) => {
+ setSelectedSubscription(subscriptions.get(subscriptionId));
+ setPrefsOpen(false);
+ }
const handleSubscribeSubmit = (subscription, user) => {
console.log(`[App] New subscription: ${subscription.id}`);
if (user !== null) {
@@ -67,6 +73,10 @@ const App = () => {
setNotificationsGranted(granted);
})
};
+ const handlePrefsClick = () => {
+ setPrefsOpen(true);
+ setSelectedSubscription(null);
+ };
const poll = (subscription, user) => {
const since = subscription.last;
api.poll(subscription.baseUrl, subscription.topic, since, user)
@@ -138,9 +148,11 @@ const App = () => {
selectedSubscription={selectedSubscription}
mobileDrawerOpen={mobileDrawerOpen}
notificationsGranted={notificationsGranted}
+ prefsOpen={prefsOpen}
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
- onSubscriptionClick={(subscriptionId) => setSelectedSubscription(subscriptions.get(subscriptionId))}
+ onSubscriptionClick={handleSubscriptionClick}
onSubscribeSubmit={handleSubscribeSubmit}
+ onPrefsClick={handlePrefsClick}
onRequestPermissionClick={handleRequestPermission}
/>
@@ -155,18 +167,34 @@ const App = () => {
height: '100vh',
overflow: 'auto',
backgroundColor: (theme) => theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900]
- }}>
+ }}
+ >
- {selectedSubscription !== null &&
- }
- {selectedSubscription == null && }
+
);
}
+const MainContent = (props) => {
+ if (props.prefsOpen) {
+ return ;
+ }
+ if (props.subscription !== null) {
+ return (
+
+ );
+ } else {
+ return ;
+ }
+};
+
export default App;
diff --git a/web/src/components/Navigation.js b/web/src/components/Navigation.js
index 940be5d..26ef22b 100644
--- a/web/src/components/Navigation.js
+++ b/web/src/components/Navigation.js
@@ -14,6 +14,7 @@ import SubscribeDialog from "./SubscribeDialog";
import {Alert, AlertTitle, ListSubheader} from "@mui/material";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
+import Preferences from "./Preferences";
const navWidth = 240;
@@ -97,11 +98,15 @@ const NavList = (props) => {
>}
-
+
@@ -115,7 +120,7 @@ const NavList = (props) => {
{
props.onSubscriptionClick(id)}
- selected={props.selectedSubscription && props.selectedSubscription.id === id}
+ selected={props.selectedSubscription && !props.prefsOpen && props.selectedSubscription.id === id}
>
diff --git a/web/src/components/Preferences.js b/web/src/components/Preferences.js
new file mode 100644
index 0000000..eb10b74
--- /dev/null
+++ b/web/src/components/Preferences.js
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import {CardContent} from "@mui/material";
+import Typography from "@mui/material/Typography";
+import Card from "@mui/material/Card";
+
+const Preferences = (props) => {
+ return (
+ <>
+
+ Manage users
+
+
+
+ You may manage users for your protected topics here. Please note that since this is a client
+ application only, username and password are stored in the browser's local storage.
+
+
+ >
+ );
+};
+
+export default Preferences;
diff --git a/web/src/components/SubscribeDialog.js b/web/src/components/SubscribeDialog.js
index 0b74f72..203aa8b 100644
--- a/web/src/components/SubscribeDialog.js
+++ b/web/src/components/SubscribeDialog.js
@@ -8,10 +8,10 @@ import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import Subscription from "../app/Subscription";
-import {useMediaQuery} from "@mui/material";
+import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/material";
import theme from "./theme";
import api from "../app/Api";
-import {topicUrl, validTopic} from "../app/utils";
+import {topicUrl, validTopic, validUrl} from "../app/utils";
import useStyles from "./styles";
import User from "../app/User";
@@ -19,18 +19,20 @@ const defaultBaseUrl = "http://127.0.0.1"
//const defaultBaseUrl = "https://ntfy.sh"
const SubscribeDialog = (props) => {
- const [baseUrl, setBaseUrl] = useState(defaultBaseUrl); // FIXME
+ const [baseUrl, setBaseUrl] = useState("");
const [topic, setTopic] = useState("");
const [showLoginPage, setShowLoginPage] = useState(false);
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
- const handleSuccess = (baseUrl, topic, user) => {
- const subscription = new Subscription(baseUrl, topic);
+ const handleSuccess = (user) => {
+ const actualBaseUrl = (baseUrl) ? baseUrl : defaultBaseUrl; // FIXME
+ const subscription = new Subscription(actualBaseUrl, topic);
props.onSuccess(subscription, user);
}
return (
-