Move more stuff out of App.js
parent
acde2e5b6e
commit
09b128f27a
|
@ -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;
|
||||
|
|
|
@ -47,31 +47,21 @@ const Root = () => {
|
|||
const subscriptions = useLiveQuery(() => subscriptionManager.all());
|
||||
const selectedSubscription = findSelected(location, subscriptions);
|
||||
|
||||
const handleSubscriptionClick = async (subscriptionId) => {
|
||||
const subscription = await subscriptionManager.get(subscriptionId);
|
||||
navigate(subscriptionRoute(subscription));
|
||||
}
|
||||
const handleSubscribeSubmit = async (subscription) => {
|
||||
console.log(`[App] New subscription: ${subscription.id}`, subscription);
|
||||
navigate(subscriptionRoute(subscription));
|
||||
handleRequestPermission();
|
||||
};
|
||||
const handleUnsubscribe = async (subscriptionId) => {
|
||||
console.log(`[App] Unsubscribing from ${subscriptionId}`);
|
||||
const newSelected = await subscriptionManager.first(); // May be undefined
|
||||
if (newSelected) {
|
||||
navigate(subscriptionRoute(newSelected));
|
||||
}
|
||||
};
|
||||
|
||||
const handleRequestPermission = () => {
|
||||
notificationManager.maybeRequestPermission(granted => setNotificationsGranted(granted));
|
||||
};
|
||||
// Define hooks: Note that the order of the hooks is important. The "loading" hooks
|
||||
// must be before the "saving" hooks.
|
||||
|
||||
useEffect(() => {
|
||||
poller.startWorker();
|
||||
pruner.startWorker();
|
||||
}, [/* initial render */]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleNotification = async (subscriptionId, notification) => {
|
||||
try {
|
||||
|
@ -93,14 +83,17 @@ const Root = () => {
|
|||
// This is for the use of 'navigate' // FIXME
|
||||
//eslint-disable-next-line
|
||||
}, [/* initial render */]);
|
||||
useEffect(() => { connectionManager.refresh(subscriptions, users) }, [subscriptions, users]); // Dangle!
|
||||
|
||||
useEffect(() => {
|
||||
connectionManager.refresh(subscriptions, users);
|
||||
}, [subscriptions, users]); // Dangle!
|
||||
|
||||
return (
|
||||
<Box sx={{display: 'flex'}}>
|
||||
<CssBaseline/>
|
||||
<ActionBar
|
||||
subscriptions={subscriptions}
|
||||
selectedSubscription={selectedSubscription}
|
||||
onUnsubscribe={handleUnsubscribe}
|
||||
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
||||
/>
|
||||
<Box component="nav" sx={{width: {sm: Navigation.width}, flexShrink: {sm: 0}}}>
|
||||
|
@ -110,7 +103,6 @@ const Root = () => {
|
|||
mobileDrawerOpen={mobileDrawerOpen}
|
||||
notificationsGranted={notificationsGranted}
|
||||
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
||||
onSubscriptionClick={handleSubscriptionClick}
|
||||
onSubscribeSubmit={handleSubscribeSubmit}
|
||||
onRequestPermissionClick={handleRequestPermission}
|
||||
/>
|
||||
|
|
|
@ -58,16 +58,20 @@ const NavList = (props) => {
|
|||
const location = useLocation();
|
||||
const [subscribeDialogKey, setSubscribeDialogKey] = useState(0);
|
||||
const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false);
|
||||
|
||||
const handleSubscribeReset = () => {
|
||||
setSubscribeDialogOpen(false);
|
||||
setSubscribeDialogKey(prev => prev+1);
|
||||
}
|
||||
|
||||
const handleSubscribeSubmit = (subscription) => {
|
||||
handleSubscribeReset();
|
||||
props.onSubscribeSubmit(subscription);
|
||||
}
|
||||
|
||||
const showSubscriptionsList = props.subscriptions?.length > 0;
|
||||
const showGrantPermissionsBox = props.subscriptions?.length > 0 && !props.notificationsGranted;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toolbar sx={{ display: { xs: 'none', sm: 'block' } }}/>
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
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 IconButton from "@mui/material/IconButton";
|
||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||
import api from "../app/Api";
|
||||
import subscriptionManager from "../app/SubscriptionManager";
|
||||
|
||||
// Originally from https://mui.com/components/menus/#MenuListComposition.js
|
||||
const SubscribeSettings = (props) => {
|
||||
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(`[IconSubscribeSettings] Deleting all notifications from ${props.subscription.id}`);
|
||||
await subscriptionManager.deleteNotifications(props.subscription.id);
|
||||
};
|
||||
|
||||
const handleUnsubscribe = async (event) => {
|
||||
handleClose(event);
|
||||
await subscriptionManager.remove(props.subscription.id);
|
||||
props.onUnsubscribe(props.subscription.id);
|
||||
};
|
||||
|
||||
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 SubscribeSettings;
|
Loading…
Reference in New Issue