|
|
@@ -4,11 +4,20 @@ import Toolbar from "@mui/material/Toolbar";
|
|
|
import IconButton from "@mui/material/IconButton";
|
|
|
import MenuIcon from "@mui/icons-material/Menu";
|
|
|
import Typography from "@mui/material/Typography";
|
|
|
-import SubscribeSettings from "./SubscribeSettings";
|
|
|
import * as React from "react";
|
|
|
+import {useEffect, useRef, useState} from "react";
|
|
|
import Box from "@mui/material/Box";
|
|
|
-import {topicShortUrl} from "../app/utils";
|
|
|
-import {useLocation} from "react-router-dom";
|
|
|
+import {subscriptionRoute, topicShortUrl} from "../app/utils";
|
|
|
+import {useLocation, useNavigate} from "react-router-dom";
|
|
|
+import ClickAwayListener from '@mui/material/ClickAwayListener';
|
|
|
+import Grow from '@mui/material/Grow';
|
|
|
+import Paper from '@mui/material/Paper';
|
|
|
+import Popper from '@mui/material/Popper';
|
|
|
+import MenuItem from '@mui/material/MenuItem';
|
|
|
+import MenuList from '@mui/material/MenuList';
|
|
|
+import MoreVertIcon from "@mui/icons-material/MoreVert";
|
|
|
+import api from "../app/Api";
|
|
|
+import subscriptionManager from "../app/SubscriptionManager";
|
|
|
|
|
|
const ActionBar = (props) => {
|
|
|
const location = useLocation();
|
|
|
@@ -41,7 +50,7 @@ const ActionBar = (props) => {
|
|
|
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
|
|
|
{title}
|
|
|
</Typography>
|
|
|
- {props.selectedSubscription && <SubscribeSettings
|
|
|
+ {props.selectedSubscription && <SettingsIcon
|
|
|
subscription={props.selectedSubscription}
|
|
|
onUnsubscribe={props.onUnsubscribe}
|
|
|
/>}
|
|
|
@@ -50,4 +59,111 @@ const ActionBar = (props) => {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
+// Originally from https://mui.com/components/menus/#MenuListComposition.js
|
|
|
+const SettingsIcon = (props) => {
|
|
|
+ const navigate = useNavigate();
|
|
|
+ const [open, setOpen] = useState(false);
|
|
|
+ const anchorRef = useRef(null);
|
|
|
+
|
|
|
+ const handleToggle = () => {
|
|
|
+ setOpen((prevOpen) => !prevOpen);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleClose = (event) => {
|
|
|
+ if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setOpen(false);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleClearAll = async (event) => {
|
|
|
+ handleClose(event);
|
|
|
+ console.log(`[ActionBar] Deleting all notifications from ${props.subscription.id}`);
|
|
|
+ await subscriptionManager.deleteNotifications(props.subscription.id);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleUnsubscribe = async (event) => {
|
|
|
+ console.log(`[ActionBar] Unsubscribing from ${props.subscription.id}`);
|
|
|
+ handleClose(event);
|
|
|
+ await subscriptionManager.remove(props.subscription.id);
|
|
|
+ const newSelected = await subscriptionManager.first(); // May be undefined
|
|
|
+ if (newSelected) {
|
|
|
+ navigate(subscriptionRoute(newSelected));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleSendTestMessage = () => {
|
|
|
+ const baseUrl = props.subscription.baseUrl;
|
|
|
+ const topic = props.subscription.topic;
|
|
|
+ api.publish(baseUrl, topic,
|
|
|
+ `This is a test notification sent by the ntfy Web UI at ${new Date().toString()}.`); // FIXME result ignored
|
|
|
+ setOpen(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleListKeyDown = (event) => {
|
|
|
+ if (event.key === 'Tab') {
|
|
|
+ event.preventDefault();
|
|
|
+ setOpen(false);
|
|
|
+ } else if (event.key === 'Escape') {
|
|
|
+ setOpen(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // return focus to the button when we transitioned from !open -> open
|
|
|
+ const prevOpen = useRef(open);
|
|
|
+ useEffect(() => {
|
|
|
+ if (prevOpen.current === true && open === false) {
|
|
|
+ anchorRef.current.focus();
|
|
|
+ }
|
|
|
+ prevOpen.current = open;
|
|
|
+ }, [open]);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <IconButton
|
|
|
+ color="inherit"
|
|
|
+ size="large"
|
|
|
+ edge="end"
|
|
|
+ ref={anchorRef}
|
|
|
+ id="composition-button"
|
|
|
+ onClick={handleToggle}
|
|
|
+ >
|
|
|
+ <MoreVertIcon/>
|
|
|
+ </IconButton>
|
|
|
+ <Popper
|
|
|
+ open={open}
|
|
|
+ anchorEl={anchorRef.current}
|
|
|
+ role={undefined}
|
|
|
+ placement="bottom-start"
|
|
|
+ transition
|
|
|
+ disablePortal
|
|
|
+ >
|
|
|
+ {({TransitionProps, placement}) => (
|
|
|
+ <Grow
|
|
|
+ {...TransitionProps}
|
|
|
+ style={{
|
|
|
+ transformOrigin:
|
|
|
+ placement === 'bottom-start' ? 'left top' : 'left bottom',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Paper>
|
|
|
+ <ClickAwayListener onClickAway={handleClose}>
|
|
|
+ <MenuList
|
|
|
+ autoFocusItem={open}
|
|
|
+ id="composition-menu"
|
|
|
+ onKeyDown={handleListKeyDown}
|
|
|
+ >
|
|
|
+ <MenuItem onClick={handleSendTestMessage}>Send test notification</MenuItem>
|
|
|
+ <MenuItem onClick={handleClearAll}>Clear all notifications</MenuItem>
|
|
|
+ <MenuItem onClick={handleUnsubscribe}>Unsubscribe</MenuItem>
|
|
|
+ </MenuList>
|
|
|
+ </ClickAwayListener>
|
|
|
+ </Paper>
|
|
|
+ </Grow>
|
|
|
+ )}
|
|
|
+ </Popper>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
export default ActionBar;
|