WIP: DIsplay name for the web app
This commit is contained in:
		
							parent
							
								
									2d26a990a9
								
							
						
					
					
						commit
						4d6c147f24
					
				
					 9 changed files with 108 additions and 15 deletions
				
			
		|  | @ -7,7 +7,7 @@ import Typography from "@mui/material/Typography"; | |||
| import * as React from "react"; | ||||
| import {useEffect, useRef, useState} from "react"; | ||||
| import Box from "@mui/material/Box"; | ||||
| import {formatShortDateTime, shuffle, topicShortUrl} from "../app/utils"; | ||||
| import {formatShortDateTime, shuffle, topicDisplayName, topicShortUrl} from "../app/utils"; | ||||
| import {useLocation, useNavigate} from "react-router-dom"; | ||||
| import ClickAwayListener from '@mui/material/ClickAwayListener'; | ||||
| import Grow from '@mui/material/Grow'; | ||||
|  | @ -24,13 +24,14 @@ import subscriptionManager from "../app/SubscriptionManager"; | |||
| import logo from "../img/ntfy.svg"; | ||||
| import {useTranslation} from "react-i18next"; | ||||
| import {Portal, Snackbar} from "@mui/material"; | ||||
| import SubscriptionSettingsDialog from "./SubscriptionSettingsDialog"; | ||||
| 
 | ||||
| const ActionBar = (props) => { | ||||
|     const { t } = useTranslation(); | ||||
|     const location = useLocation(); | ||||
|     let title = "ntfy"; | ||||
|     if (props.selected) { | ||||
|         title = topicShortUrl(props.selected.baseUrl, props.selected.topic); | ||||
|         title = topicDisplayName(props.selected); | ||||
|     } else if (location.pathname === "/settings") { | ||||
|         title = t("action_bar_settings"); | ||||
|     } | ||||
|  | @ -79,6 +80,7 @@ const SettingsIcons = (props) => { | |||
|     const navigate = useNavigate(); | ||||
|     const [open, setOpen] = useState(false); | ||||
|     const [snackOpen, setSnackOpen] = useState(false); | ||||
|     const [subscriptionSettingsOpen, setSubscriptionSettingsOpen] = useState(false); | ||||
|     const anchorRef = useRef(null); | ||||
|     const subscription = props.subscription; | ||||
| 
 | ||||
|  | @ -116,6 +118,10 @@ const SettingsIcons = (props) => { | |||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const handleSubscriptionSettings = async () => { | ||||
|         setSubscriptionSettingsOpen(true); | ||||
|     } | ||||
| 
 | ||||
|     const handleSendTestMessage = async () => { | ||||
|         const baseUrl = props.subscription.baseUrl; | ||||
|         const topic = props.subscription.topic; | ||||
|  | @ -201,6 +207,7 @@ const SettingsIcons = (props) => { | |||
|                         <Paper> | ||||
|                             <ClickAwayListener onClickAway={handleClose}> | ||||
|                                 <MenuList autoFocusItem={open} onKeyDown={handleListKeyDown}> | ||||
|                                     <MenuItem onClick={handleSubscriptionSettings}>{t("action_bar_subscription_settings")}</MenuItem> | ||||
|                                     <MenuItem onClick={handleSendTestMessage}>{t("action_bar_send_test_notification")}</MenuItem> | ||||
|                                     <MenuItem onClick={handleClearAll}>{t("action_bar_clear_notifications")}</MenuItem> | ||||
|                                     <MenuItem onClick={handleUnsubscribe}>{t("action_bar_unsubscribe")}</MenuItem> | ||||
|  | @ -218,6 +225,14 @@ const SettingsIcons = (props) => { | |||
|                     message={t("message_bar_error_publishing")} | ||||
|                 /> | ||||
|             </Portal> | ||||
|             <Portal> | ||||
|                 <SubscriptionSettingsDialog | ||||
|                     key={`subscriptionSettingsDialog${subscription.id}`} | ||||
|                     open={subscriptionSettingsOpen} | ||||
|                     subscription={subscription} | ||||
|                     onClose={() => setSubscriptionSettingsOpen(false)} | ||||
|                 /> | ||||
|             </Portal> | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import SubscribeDialog from "./SubscribeDialog"; | |||
| import {Alert, AlertTitle, Badge, CircularProgress, Link, ListSubheader} from "@mui/material"; | ||||
| import Button from "@mui/material/Button"; | ||||
| import Typography from "@mui/material/Typography"; | ||||
| import {openUrl, topicShortUrl, topicUrl} from "../app/utils"; | ||||
| import {openUrl, topicDisplayName, topicUrl} from "../app/utils"; | ||||
| import routes from "./routes"; | ||||
| import {ConnectionState} from "../app/Connection"; | ||||
| import {useLocation, useNavigate} from "react-router-dom"; | ||||
|  | @ -173,12 +173,10 @@ const SubscriptionItem = (props) => { | |||
|     const icon = (subscription.state === ConnectionState.Connecting) | ||||
|         ? <CircularProgress size="24px"/> | ||||
|         : <Badge badgeContent={iconBadge} invisible={subscription.new === 0} color="primary"><ChatBubbleOutlineIcon/></Badge>; | ||||
|     const label = (subscription.baseUrl === window.location.origin) | ||||
|         ? subscription.topic | ||||
|         : topicShortUrl(subscription.baseUrl, subscription.topic); | ||||
|     const displayName = topicDisplayName(subscription); | ||||
|     const ariaLabel = (subscription.state === ConnectionState.Connecting) | ||||
|         ? `${label} (${t("nav_button_connecting")})` | ||||
|         : label; | ||||
|         ? `${displayName} (${t("nav_button_connecting")})` | ||||
|         : displayName; | ||||
|     const handleClick = async () => { | ||||
|         navigate(routes.forSubscription(subscription)); | ||||
|         await subscriptionManager.markNotificationsRead(subscription.id); | ||||
|  | @ -186,7 +184,7 @@ const SubscriptionItem = (props) => { | |||
|     return ( | ||||
|         <ListItemButton onClick={handleClick} selected={props.selected} aria-label={ariaLabel} aria-live="polite"> | ||||
|             <ListItemIcon>{icon}</ListItemIcon> | ||||
|             <ListItemText primary={label}/> | ||||
|             <ListItemText primary={displayName}/> | ||||
|             {subscription.mutedUntil > 0 && | ||||
|                 <ListItemIcon edge="end" aria-label={t("nav_button_muted")}><NotificationsOffOutlined /></ListItemIcon>} | ||||
|         </ListItemButton> | ||||
|  |  | |||
							
								
								
									
										59
									
								
								web/src/components/SubscriptionSettingsDialog.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								web/src/components/SubscriptionSettingsDialog.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| import * as React from 'react'; | ||||
| import {useState} from 'react'; | ||||
| import Button from '@mui/material/Button'; | ||||
| import TextField from '@mui/material/TextField'; | ||||
| import Dialog from '@mui/material/Dialog'; | ||||
| import DialogContent from '@mui/material/DialogContent'; | ||||
| import DialogContentText from '@mui/material/DialogContentText'; | ||||
| import DialogTitle from '@mui/material/DialogTitle'; | ||||
| import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/material"; | ||||
| import theme from "./theme"; | ||||
| import api from "../app/Api"; | ||||
| import {topicUrl, validTopic, validUrl} from "../app/utils"; | ||||
| import userManager from "../app/UserManager"; | ||||
| import subscriptionManager from "../app/SubscriptionManager"; | ||||
| import poller from "../app/Poller"; | ||||
| import DialogFooter from "./DialogFooter"; | ||||
| import {useTranslation} from "react-i18next"; | ||||
| 
 | ||||
| const SubscriptionSettingsDialog = (props) => { | ||||
|     const { t } = useTranslation(); | ||||
|     const subscription = props.subscription; | ||||
|     const [displayName, setDisplayName] = useState(subscription.displayName ?? ""); | ||||
|     const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); | ||||
|     const handleSave = async () => { | ||||
|         await subscriptionManager.setDisplayName(subscription.id, displayName); | ||||
|         props.onClose(); | ||||
|     } | ||||
|     return ( | ||||
|         <Dialog open={props.open} onClose={props.onClose} fullScreen={fullScreen}> | ||||
|             <DialogTitle>{t("subscription_settings_dialog_title")}</DialogTitle> | ||||
|             <DialogContent> | ||||
|                 <DialogContentText> | ||||
|                     {t("subscription_settings_dialog_description")} | ||||
|                 </DialogContentText> | ||||
|                 <TextField | ||||
|                     autoFocus | ||||
|                     margin="dense" | ||||
|                     id="topic" | ||||
|                     placeholder={t("subscription_settings_dialog_display_name_placeholder")} | ||||
|                     value={displayName} | ||||
|                     onChange={ev => setDisplayName(ev.target.value)} | ||||
|                     type="text" | ||||
|                     fullWidth | ||||
|                     variant="standard" | ||||
|                     inputProps={{ | ||||
|                         maxLength: 64, | ||||
|                         "aria-label": t("subscription_settings_dialog_display_name_placeholder") | ||||
|                     }} | ||||
|                 /> | ||||
|             </DialogContent> | ||||
|             <DialogFooter> | ||||
|                 <Button onClick={props.onClose}>{t("subscription_settings_button_cancel")}</Button> | ||||
|                 <Button onClick={handleSave}>{t("subscription_settings_button_save")}</Button> | ||||
|             </DialogFooter> | ||||
|         </Dialog> | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| export default SubscriptionSettingsDialog; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue