From 4d6c147f24d8986e1e8fcc5714df2508e2e7030d Mon Sep 17 00:00:00 2001 From: Philipp Heckel Date: Wed, 29 Jun 2022 15:57:56 -0400 Subject: [PATCH] WIP: DIsplay name for the web app --- docs/releases.md | 2 +- web/public/static/langs/en.json | 6 ++ web/src/app/Api.js | 5 +- web/src/app/Notifier.js | 5 +- web/src/app/SubscriptionManager.js | 6 ++ web/src/app/utils.js | 9 +++ web/src/components/ActionBar.js | 19 +++++- web/src/components/Navigation.js | 12 ++-- .../components/SubscriptionSettingsDialog.js | 59 +++++++++++++++++++ 9 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 web/src/components/SubscriptionSettingsDialog.js diff --git a/docs/releases.md b/docs/releases.md index 105e7a2..9f37d4c 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -14,7 +14,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release **Bugs:** -* Long-click selecting of notifications doesn't scoll to the top anymore ([#235](https://github.com/binwiederhier/ntfy/issues/235), thanks to [@wunter8](https://github.com/wunter8)) +* Long-click selecting of notifications doesn't scroll to the top anymore ([#235](https://github.com/binwiederhier/ntfy/issues/235), thanks to [@wunter8](https://github.com/wunter8)) * Add attachment and click URL extras to MESSAGE_RECEIVED broadcast ([#329](https://github.com/binwiederhier/ntfy/issues/329), thanks to [@wunter8](https://github.com/wunter8)) * Accessibility: Clear/choose service URL button in base URL dropdown now has a label ([#292](https://github.com/binwiederhier/ntfy/issues/292), thanks to [@mhameed](https://github.com/mhameed) for reporting) diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json index 3e7dd78..4d12c07 100644 --- a/web/public/static/langs/en.json +++ b/web/public/static/langs/en.json @@ -2,6 +2,7 @@ "action_bar_show_menu": "Show menu", "action_bar_logo_alt": "ntfy logo", "action_bar_settings": "Settings", + "action_bar_subscription_settings": "Subscription settings", "action_bar_send_test_notification": "Send test notification", "action_bar_clear_notifications": "Clear all notifications", "action_bar_unsubscribe": "Unsubscribe", @@ -59,6 +60,11 @@ "notifications_no_subscriptions_description": "Click the \"{{linktext}}\" link to create or subscribe to a topic. After that, you can send messages via PUT or POST and you'll receive notifications here.", "notifications_example": "Example", "notifications_more_details": "For more information, check out the website or documentation.", + "subscription_settings_dialog_title": "Subscription settings", + "subscription_settings_dialog_description": "Configure settings specifically for this topic subscription. Settings are currently only applied locally.", + "subscription_settings_dialog_display_name_placeholder": "Display name", + "subscription_settings_button_cancel": "Cancel", + "subscription_settings_button_save": "Save", "notifications_loading": "Loading notifications …", "publish_dialog_title_topic": "Publish to {{topic}}", "publish_dialog_title_no_topic": "Publish notification", diff --git a/web/src/app/Api.js b/web/src/app/Api.js index 1e89bc0..a07f7a5 100644 --- a/web/src/app/Api.js +++ b/web/src/app/Api.js @@ -1,13 +1,12 @@ import { - basicAuth, - encodeBase64, fetchLinesIterator, maybeWithBasicAuth, topicShortUrl, topicUrl, topicUrlAuth, topicUrlJsonPoll, - topicUrlJsonPollWithSince, userStatsUrl + topicUrlJsonPollWithSince, + userStatsUrl } from "./utils"; import userManager from "./UserManager"; diff --git a/web/src/app/Notifier.js b/web/src/app/Notifier.js index 04cc0cf..613340c 100644 --- a/web/src/app/Notifier.js +++ b/web/src/app/Notifier.js @@ -1,4 +1,4 @@ -import {formatMessage, formatTitleWithDefault, openUrl, playSound, topicShortUrl} from "./utils"; +import {formatMessage, formatTitleWithDefault, openUrl, playSound, topicDisplayName, topicShortUrl} from "./utils"; import prefs from "./Prefs"; import subscriptionManager from "./SubscriptionManager"; import logo from "../img/ntfy.png"; @@ -18,8 +18,9 @@ class Notifier { return; } const shortUrl = topicShortUrl(subscription.baseUrl, subscription.topic); + const displayName = topicDisplayName(subscription); const message = formatMessage(notification); - const title = formatTitleWithDefault(notification, shortUrl); + const title = formatTitleWithDefault(notification, displayName); // Show notification console.log(`[Notifier, ${shortUrl}] Displaying notification ${notification.id}: ${message}`); diff --git a/web/src/app/SubscriptionManager.js b/web/src/app/SubscriptionManager.js index 01418ee..f657348 100644 --- a/web/src/app/SubscriptionManager.js +++ b/web/src/app/SubscriptionManager.js @@ -133,6 +133,12 @@ class SubscriptionManager { }); } + async setDisplayName(subscriptionId, displayName) { + await db.subscriptions.update(subscriptionId, { + displayName: displayName + }); + } + async pruneNotifications(thresholdTimestamp) { await db.notifications .where("time").below(thresholdTimestamp) diff --git a/web/src/app/utils.js b/web/src/app/utils.js index aaf8911..ffc359b 100644 --- a/web/src/app/utils.js +++ b/web/src/app/utils.js @@ -38,6 +38,15 @@ export const disallowedTopic = (topic) => { return config.disallowedTopics.includes(topic); } +export const topicDisplayName = (subscription) => { + if (subscription.displayName) { + return subscription.displayName; + } else if (subscription.baseUrl === window.location.origin) { + return subscription.topic; + } + return topicShortUrl(subscription.baseUrl, subscription.topic); +}; + // Format emojis (see emoji.js) const emojis = {}; rawEmojis.forEach(emoji => { diff --git a/web/src/components/ActionBar.js b/web/src/components/ActionBar.js index 30ab271..e284f92 100644 --- a/web/src/components/ActionBar.js +++ b/web/src/components/ActionBar.js @@ -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) => { + {t("action_bar_subscription_settings")} {t("action_bar_send_test_notification")} {t("action_bar_clear_notifications")} {t("action_bar_unsubscribe")} @@ -218,6 +225,14 @@ const SettingsIcons = (props) => { message={t("message_bar_error_publishing")} /> + + setSubscriptionSettingsOpen(false)} + /> + ); }; diff --git a/web/src/components/Navigation.js b/web/src/components/Navigation.js index af67cb9..694da59 100644 --- a/web/src/components/Navigation.js +++ b/web/src/components/Navigation.js @@ -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) ? : ; - 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 ( {icon} - + {subscription.mutedUntil > 0 && } diff --git a/web/src/components/SubscriptionSettingsDialog.js b/web/src/components/SubscriptionSettingsDialog.js new file mode 100644 index 0000000..a31fe0f --- /dev/null +++ b/web/src/components/SubscriptionSettingsDialog.js @@ -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 ( + + {t("subscription_settings_dialog_title")} + + + {t("subscription_settings_dialog_description")} + + setDisplayName(ev.target.value)} + type="text" + fullWidth + variant="standard" + inputProps={{ + maxLength: 64, + "aria-label": t("subscription_settings_dialog_display_name_placeholder") + }} + /> + + + + + + + ); +}; + +export default SubscriptionSettingsDialog;