diff --git a/web/package.json b/web/package.json index c1749e0e..5a1e4456 100644 --- a/web/package.json +++ b/web/package.json @@ -12,7 +12,6 @@ "@emotion/styled": "latest", "@mui/icons-material": "^5.4.2", "@mui/material": "latest", - "@mui/styles": "^5.4.2", "react": "latest", "react-dom": "latest", "react-scripts": "^3.0.1" diff --git a/web/src/app/NotificationManager.js b/web/src/app/NotificationManager.js index 78934b18..e84bb42a 100644 --- a/web/src/app/NotificationManager.js +++ b/web/src/app/NotificationManager.js @@ -1,7 +1,11 @@ import {formatMessage, formatTitleWithFallback, topicShortUrl} from "./utils"; +import repository from "./Repository"; class NotificationManager { notify(subscription, notification, onClickFallback) { + if (!this.shouldNotify(subscription, notification)) { + return; + } const message = formatMessage(notification); const title = formatTitleWithFallback(notification, topicShortUrl(subscription.baseUrl, subscription.topic)); const n = new Notification(title, { @@ -27,6 +31,14 @@ class NotificationManager { }); } } + + shouldNotify(subscription, notification) { + const priority = (notification.priority) ? notification.priority : 3; + if (priority < repository.getMinPriority()) { + return false; + } + return true; + } } const notificationManager = new NotificationManager(); diff --git a/web/src/app/Repository.js b/web/src/app/Repository.js index 5371c0a4..c3faf464 100644 --- a/web/src/app/Repository.js +++ b/web/src/app/Repository.js @@ -87,6 +87,24 @@ class Repository { console.log(`[Repository] Saving selected subscription ${selectedSubscriptionId} to localStorage`); localStorage.setItem('selectedSubscriptionId', selectedSubscriptionId); } + + setMinPriority(minPriority) { + localStorage.setItem('minPriority', minPriority.toString()); + } + + getMinPriority() { + const minPriority = localStorage.getItem('minPriority'); + return (minPriority) ? Number(minPriority) : 1; + } + + setDeleteAfter(deleteAfter) { + localStorage.setItem('deleteAfter', deleteAfter.toString()); + } + + getDeleteAfter() { + const deleteAfter = localStorage.getItem('deleteAfter'); + return (deleteAfter) ? Number(deleteAfter) : 604800; // Default is one week + } } const repository = new Repository(); diff --git a/web/src/app/Subscription.js b/web/src/app/Subscription.js index ece61df1..6d3b3458 100644 --- a/web/src/app/Subscription.js +++ b/web/src/app/Subscription.js @@ -10,7 +10,7 @@ class Subscription { } addNotification(notification) { - if (this.notifications.has(notification.id)) { + if (!notification.event || notification.event !== 'message' || this.notifications.has(notification.id)) { return false; } this.notifications.set(notification.id, notification); diff --git a/web/src/components/App.js b/web/src/components/App.js index a95e8d70..0ff4d08f 100644 --- a/web/src/components/App.js +++ b/web/src/components/App.js @@ -22,6 +22,9 @@ import Preferences from "./Preferences"; // - add baseUrl // TODO user management // TODO embed into ntfy server +// TODO make default server functional +// TODO indexeddb for notifications + subscriptions +// TODO business logic with callbacks const App = () => { console.log(`[App] Rendering main view`); diff --git a/web/src/components/Navigation.js b/web/src/components/Navigation.js index 26ef22b9..f54313b0 100644 --- a/web/src/components/Navigation.js +++ b/web/src/components/Navigation.js @@ -14,7 +14,6 @@ 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; @@ -72,24 +71,7 @@ const NavList = (props) => { - {showGrantPermissionsBox && - <> - - Notifications are disabled - - Grant your browser permission to display desktop notifications. - - - - - } + {showGrantPermissionsBox && } {showSubscriptionsList && <> @@ -147,4 +129,26 @@ const SubscriptionList = (props) => { ); } +const PermissionAlert = (props) => { + return ( + <> + + Notifications are disabled + + Grant your browser permission to display desktop notifications. + + + + + + ); +}; + export default Navigation; diff --git a/web/src/components/Preferences.js b/web/src/components/Preferences.js index eb10b74f..e7d712fa 100644 --- a/web/src/components/Preferences.js +++ b/web/src/components/Preferences.js @@ -1,22 +1,191 @@ import * as React from 'react'; -import {CardContent} from "@mui/material"; +import {useState} from 'react'; +import {FormControl, Select, Stack, Table, TableBody, TableCell, TableHead, TableRow} from "@mui/material"; import Typography from "@mui/material/Typography"; -import Card from "@mui/material/Card"; +import Paper from "@mui/material/Paper"; +import repository from "../app/Repository"; +import {Paragraph} from "./styles"; +import EditIcon from '@mui/icons-material/Edit'; +import CloseIcon from "@mui/icons-material/Close"; +import IconButton from "@mui/material/IconButton"; +import Container from "@mui/material/Container"; +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; const Preferences = (props) => { return ( - <> + + + + + + + + ); +}; + +const Notifications = (props) => { + return ( + - Manage users + Notifications - - - 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. - - + + + + + + ); +}; + +const MinPriority = () => { + const [minPriority, setMinPriority] = useState(repository.getMinPriority()); + const handleChange = (ev) => { + setMinPriority(ev.target.value); + repository.setMinPriority(ev.target.value); + } + return ( + + + + + + ) +}; + +const DeleteAfter = () => { + const [deleteAfter, setDeleteAfter] = useState(repository.getDeleteAfter()); + const handleChange = (ev) => { + setDeleteAfter(ev.target.value); + repository.setDeleteAfter(ev.target.value); + } + return ( + + + + + + ) +}; + + +const PrefGroup = (props) => { + return ( +
+ {props.children} +
+ ) +}; + +const Pref = (props) => { + return ( + <> +
+ {props.title} +
+
+ {props.children} +
); }; +const DefaultServer = (props) => { + return ( + + + Default server + + + This server is used as a default when adding new topics. + + + + ); +}; + +const Users = (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. + + + + ); +}; + +const UserTable = () => { + const users = repository.loadUsers(); + return ( + + + + User + Service URL + + + + + {users.map((user, i) => ( + + {user.username} + {user.baseUrl} + + + + + + + + + + ))} + +
+ + ); +} + export default Preferences; diff --git a/web/src/components/SubscribeDialog.js b/web/src/components/SubscribeDialog.js index 203aa8b6..ab48df43 100644 --- a/web/src/components/SubscribeDialog.js +++ b/web/src/components/SubscribeDialog.js @@ -12,8 +12,8 @@ import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/mate import theme from "./theme"; import api from "../app/Api"; import {topicUrl, validTopic, validUrl} from "../app/utils"; -import useStyles from "./styles"; import User from "../app/User"; +import Box from "@mui/material/Box"; const defaultBaseUrl = "http://127.0.0.1" //const defaultBaseUrl = "https://ntfy.sh" @@ -123,7 +123,6 @@ const SubscribePage = (props) => { }; const LoginPage = (props) => { - const styles = useStyles(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [errorText, setErrorText] = useState(""); @@ -170,17 +169,35 @@ const LoginPage = (props) => { variant="standard" /> -
- - {errorText} - - - - - -
+ + + + ); }; +const DialogFooter = (props) => { + return ( + + + {props.status} + + + {props.children} + + + ); +}; + export default SubscribeDialog; diff --git a/web/src/components/styles.js b/web/src/components/styles.js index ce044edc..84f0af61 100644 --- a/web/src/components/styles.js +++ b/web/src/components/styles.js @@ -1,23 +1,8 @@ -import {makeStyles, styled} from "@mui/styles"; +import {styled} from "@mui/styles"; import Typography from "@mui/material/Typography"; import theme from "./theme"; import Container from "@mui/material/Container"; -const useStyles = makeStyles(theme => ({ - bottomBar: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - paddingLeft: '24px', - paddingTop: '8px 24px', - paddingBottom: '8px 24px', - }, - statusText: { - margin: '0px', - paddingTop: '8px', - } -})); - export const Paragraph = styled(Typography)({ paddingTop: 8, paddingBottom: 8, @@ -31,5 +16,3 @@ export const VerticallyCenteredContainer = styled(Container)({ alignContent: 'center', color: theme.palette.body.main }); - -export default useStyles;