Make web push toggle global
parent
a8db08c7d4
commit
46798ac322
|
@ -95,6 +95,7 @@
|
||||||
"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_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_example": "Example",
|
||||||
"notifications_more_details": "For more information, check out the <websiteLink>website</websiteLink> or <docsLink>documentation</docsLink>.",
|
"notifications_more_details": "For more information, check out the <websiteLink>website</websiteLink> or <docsLink>documentation</docsLink>.",
|
||||||
|
"notification_toggle_mute": "Mute",
|
||||||
"notification_toggle_unmute": "Unmute",
|
"notification_toggle_unmute": "Unmute",
|
||||||
"notification_toggle_background": "Background notifications",
|
"notification_toggle_background": "Background notifications",
|
||||||
"display_name_dialog_title": "Change display name",
|
"display_name_dialog_title": "Change display name",
|
||||||
|
@ -369,6 +370,10 @@
|
||||||
"prefs_reservations_dialog_description": "Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.",
|
"prefs_reservations_dialog_description": "Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.",
|
||||||
"prefs_reservations_dialog_topic_label": "Topic",
|
"prefs_reservations_dialog_topic_label": "Topic",
|
||||||
"prefs_reservations_dialog_access_label": "Access",
|
"prefs_reservations_dialog_access_label": "Access",
|
||||||
|
"prefs_notifications_web_push_title": "Enable web push notifications",
|
||||||
|
"prefs_notifications_web_push_description": "Enable this to receive notifications in the background even when ntfy isn't running",
|
||||||
|
"prefs_notifications_web_push_enabled": "Enabled",
|
||||||
|
"prefs_notifications_web_push_disabled": "Disabled",
|
||||||
"reservation_delete_dialog_description": "Removing a reservation gives up ownership over the topic, and allows others to reserve it. You can keep, or delete existing messages and attachments.",
|
"reservation_delete_dialog_description": "Removing a reservation gives up ownership over the topic, and allows others to reserve it. You can keep, or delete existing messages and attachments.",
|
||||||
"reservation_delete_dialog_action_keep_title": "Keep cached messages and attachments",
|
"reservation_delete_dialog_action_keep_title": "Keep cached messages and attachments",
|
||||||
"reservation_delete_dialog_action_keep_description": "Messages and attachments that are cached on the server will become publicly visible for people with knowledge of the topic name.",
|
"reservation_delete_dialog_action_keep_description": "Messages and attachments that are cached on the server will become publicly visible for people with knowledge of the topic name.",
|
||||||
|
|
|
@ -45,15 +45,11 @@ class ConnectionManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`[ConnectionManager] Refreshing connections`);
|
console.log(`[ConnectionManager] Refreshing connections`);
|
||||||
const subscriptionsWithUsersAndConnectionId = subscriptions
|
const subscriptionsWithUsersAndConnectionId = subscriptions.map((s) => {
|
||||||
.map((s) => {
|
const [user] = users.filter((u) => u.baseUrl === s.baseUrl);
|
||||||
const [user] = users.filter((u) => u.baseUrl === s.baseUrl);
|
const connectionId = makeConnectionId(s, user);
|
||||||
const connectionId = makeConnectionId(s, user);
|
return { ...s, user, connectionId };
|
||||||
return { ...s, user, connectionId };
|
});
|
||||||
})
|
|
||||||
// background notifications don't need this as they come over web push.
|
|
||||||
// however, if they are muted, we again need the ws while the page is active
|
|
||||||
.filter((s) => !s.webPushEnabled && s.mutedUntil !== 1);
|
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
const targetIds = subscriptionsWithUsersAndConnectionId.map((s) => s.connectionId);
|
const targetIds = subscriptionsWithUsersAndConnectionId.map((s) => s.connectionId);
|
||||||
|
|
|
@ -114,6 +114,11 @@ class Notifier {
|
||||||
return this.pushSupported() && this.contextSupported() && this.granted() && !this.iosSupportedButInstallRequired();
|
return this.pushSupported() && this.contextSupported() && this.granted() && !this.iosSupportedButInstallRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async pushEnabled() {
|
||||||
|
const enabled = await prefs.webPushEnabled();
|
||||||
|
return this.pushPossible() && enabled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this is a HTTPS site, or served over localhost. Otherwise the Notification API
|
* Returns true if this is a HTTPS site, or served over localhost. Otherwise the Notification API
|
||||||
* is not supported, see https://developer.mozilla.org/en-US/docs/Web/API/notification
|
* is not supported, see https://developer.mozilla.org/en-US/docs/Web/API/notification
|
||||||
|
|
|
@ -31,6 +31,15 @@ class Prefs {
|
||||||
const deleteAfter = await this.db.prefs.get("deleteAfter");
|
const deleteAfter = await this.db.prefs.get("deleteAfter");
|
||||||
return deleteAfter ? Number(deleteAfter.value) : 604800; // Default is one week
|
return deleteAfter ? Number(deleteAfter.value) : 604800; // Default is one week
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async webPushEnabled() {
|
||||||
|
const obj = await this.db.prefs.get("webPushEnabled");
|
||||||
|
return obj?.value ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWebPushEnabled(enabled) {
|
||||||
|
await this.db.prefs.put({ key: "webPushEnabled", value: enabled });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefs = new Prefs(getDb());
|
const prefs = new Prefs(getDb());
|
||||||
|
|
|
@ -21,8 +21,16 @@ class SubscriptionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async webPushTopics() {
|
async webPushTopics() {
|
||||||
const subscriptions = await this.db.subscriptions.where({ webPushEnabled: 1, mutedUntil: 0 }).toArray();
|
// the Promise.resolve wrapper is not superfluous, without it the live query breaks:
|
||||||
return subscriptions.map(({ topic }) => topic);
|
// https://dexie.org/docs/dexie-react-hooks/useLiveQuery()#calling-non-dexie-apis-from-querier
|
||||||
|
if (!(await Promise.resolve(notifier.pushEnabled()))) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscriptions = await this.db.subscriptions.where({ mutedUntil: 0, baseUrl: config.base_url }).toArray();
|
||||||
|
|
||||||
|
// internal is currently a bool, it could be a 0/1 to be indexable, but for now just filter them out here
|
||||||
|
return subscriptions.filter(({ internal }) => !internal).map(({ topic }) => topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(subscriptionId) {
|
async get(subscriptionId) {
|
||||||
|
@ -49,7 +57,6 @@ class SubscriptionManager {
|
||||||
* @param {string} topic
|
* @param {string} topic
|
||||||
* @param {object} opts
|
* @param {object} opts
|
||||||
* @param {boolean} opts.internal
|
* @param {boolean} opts.internal
|
||||||
* @param {boolean} opts.webPushEnabled
|
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async add(baseUrl, topic, opts = {}) {
|
async add(baseUrl, topic, opts = {}) {
|
||||||
|
@ -67,7 +74,6 @@ class SubscriptionManager {
|
||||||
topic,
|
topic,
|
||||||
mutedUntil: 0,
|
mutedUntil: 0,
|
||||||
last: null,
|
last: null,
|
||||||
webPushEnabled: opts.webPushEnabled ? 1 : 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.db.subscriptions.put(subscription);
|
await this.db.subscriptions.put(subscription);
|
||||||
|
@ -211,12 +217,6 @@ class SubscriptionManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleBackgroundNotifications(subscription) {
|
|
||||||
await this.db.subscriptions.update(subscription.id, {
|
|
||||||
webPushEnabled: subscription.webPushEnabled === 1 ? 0 : 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async setDisplayName(subscriptionId, displayName) {
|
async setDisplayName(subscriptionId, displayName) {
|
||||||
await this.db.subscriptions.update(subscriptionId, {
|
await this.db.subscriptions.update(subscriptionId, {
|
||||||
displayName,
|
displayName,
|
||||||
|
|
|
@ -14,7 +14,7 @@ const getDbBase = (username) => {
|
||||||
const db = new Dexie(dbName);
|
const db = new Dexie(dbName);
|
||||||
|
|
||||||
db.version(2).stores({
|
db.version(2).stores({
|
||||||
subscriptions: "&id,baseUrl,[webPushEnabled+mutedUntil]",
|
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
|
||||||
notifications: "&id,subscriptionId,time,new,[subscriptionId+new]", // compound key for query performance
|
notifications: "&id,subscriptionId,time,new,[subscriptionId+new]", // compound key for query performance
|
||||||
users: "&baseUrl,username",
|
users: "&baseUrl,username",
|
||||||
prefs: "&key",
|
prefs: "&key",
|
||||||
|
|
|
@ -69,6 +69,16 @@ const Layout = () => {
|
||||||
const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
|
const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
|
||||||
const users = useLiveQuery(() => userManager.all());
|
const users = useLiveQuery(() => userManager.all());
|
||||||
const subscriptions = useLiveQuery(() => subscriptionManager.all());
|
const subscriptions = useLiveQuery(() => subscriptionManager.all());
|
||||||
|
const webPushTopics = useLiveQuery(() => subscriptionManager.webPushTopics());
|
||||||
|
|
||||||
|
const websocketSubscriptions = useMemo(
|
||||||
|
() => (subscriptions && webPushTopics ? subscriptions.filter((s) => !webPushTopics.includes(s.topic)) : []),
|
||||||
|
// websocketSubscriptions should stay stable unless the list of subscription ids changes.
|
||||||
|
// without the memoization, the connection listener calls a refresh for no reason.
|
||||||
|
// this isn't a problem due to the makeConnectionId, but it triggers an
|
||||||
|
// unnecessary recomputation for every received message.
|
||||||
|
[JSON.stringify({ subscriptions: subscriptions?.map(({ id }) => id), webPushTopics })]
|
||||||
|
);
|
||||||
const subscriptionsWithoutInternal = subscriptions?.filter((s) => !s.internal);
|
const subscriptionsWithoutInternal = subscriptions?.filter((s) => !s.internal);
|
||||||
const newNotificationsCount = subscriptionsWithoutInternal?.reduce((prev, cur) => prev + cur.new, 0) || 0;
|
const newNotificationsCount = subscriptionsWithoutInternal?.reduce((prev, cur) => prev + cur.new, 0) || 0;
|
||||||
const [selected] = (subscriptionsWithoutInternal || []).filter(
|
const [selected] = (subscriptionsWithoutInternal || []).filter(
|
||||||
|
@ -77,7 +87,7 @@ const Layout = () => {
|
||||||
(config.base_url === s.baseUrl && params.topic === s.topic)
|
(config.base_url === s.baseUrl && params.topic === s.topic)
|
||||||
);
|
);
|
||||||
|
|
||||||
useConnectionListeners(account, subscriptions, users);
|
useConnectionListeners(account, websocketSubscriptions, users);
|
||||||
useAccountListener(setAccount);
|
useAccountListener(setAccount);
|
||||||
useBackgroundProcesses();
|
useBackgroundProcesses();
|
||||||
useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
|
useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
|
||||||
|
|
|
@ -48,6 +48,7 @@ import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite
|
||||||
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
|
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
|
||||||
import { UnauthorizedError } from "../app/errors";
|
import { UnauthorizedError } from "../app/errors";
|
||||||
import { subscribeTopic } from "./SubscribeDialog";
|
import { subscribeTopic } from "./SubscribeDialog";
|
||||||
|
import notifier from "../app/Notifier";
|
||||||
|
|
||||||
const maybeUpdateAccountSettings = async (payload) => {
|
const maybeUpdateAccountSettings = async (payload) => {
|
||||||
if (!session.exists()) {
|
if (!session.exists()) {
|
||||||
|
@ -85,6 +86,7 @@ const Notifications = () => {
|
||||||
<Sound />
|
<Sound />
|
||||||
<MinPriority />
|
<MinPriority />
|
||||||
<DeleteAfter />
|
<DeleteAfter />
|
||||||
|
<WebPushEnabled />
|
||||||
</PrefGroup>
|
</PrefGroup>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -232,6 +234,35 @@ const DeleteAfter = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const WebPushEnabled = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const labelId = "prefWebPushEnabled";
|
||||||
|
const defaultEnabled = useLiveQuery(async () => prefs.webPushEnabled());
|
||||||
|
const handleChange = async (ev) => {
|
||||||
|
await prefs.setWebPushEnabled(ev.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// while loading
|
||||||
|
if (defaultEnabled == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notifier.pushPossible()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pref labelId={labelId} title={t("prefs_notifications_web_push_title")} description={t("prefs_notifications_web_push_description")}>
|
||||||
|
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
|
||||||
|
<Select value={defaultEnabled} onChange={handleChange} aria-labelledby={labelId}>
|
||||||
|
<MenuItem value>{t("prefs_notifications_web_push_enabled")}</MenuItem>
|
||||||
|
<MenuItem value={false}>{t("prefs_notifications_web_push_disabled")}</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Pref>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Users = () => {
|
const Users = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [dialogKey, setDialogKey] = useState(0);
|
const [dialogKey, setDialogKey] = useState(0);
|
||||||
|
|
|
@ -28,7 +28,6 @@ import ReserveTopicSelect from "./ReserveTopicSelect";
|
||||||
import { AccountContext } from "./App";
|
import { AccountContext } from "./App";
|
||||||
import { TopicReservedError, UnauthorizedError } from "../app/errors";
|
import { TopicReservedError, UnauthorizedError } from "../app/errors";
|
||||||
import { ReserveLimitChip } from "./SubscriptionPopup";
|
import { ReserveLimitChip } from "./SubscriptionPopup";
|
||||||
import notifier from "../app/Notifier";
|
|
||||||
|
|
||||||
const publicBaseUrl = "https://ntfy.sh";
|
const publicBaseUrl = "https://ntfy.sh";
|
||||||
|
|
||||||
|
@ -53,12 +52,10 @@ const SubscribeDialog = (props) => {
|
||||||
const [showLoginPage, setShowLoginPage] = useState(false);
|
const [showLoginPage, setShowLoginPage] = useState(false);
|
||||||
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
|
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
|
||||||
|
|
||||||
const handleSuccess = async (webPushEnabled) => {
|
const handleSuccess = async () => {
|
||||||
console.log(`[SubscribeDialog] Subscribing to topic ${topic}`);
|
console.log(`[SubscribeDialog] Subscribing to topic ${topic}`);
|
||||||
const actualBaseUrl = baseUrl || config.base_url;
|
const actualBaseUrl = baseUrl || config.base_url;
|
||||||
const subscription = await subscribeTopic(actualBaseUrl, topic, {
|
const subscription = await subscribeTopic(actualBaseUrl, topic, {});
|
||||||
webPushEnabled,
|
|
||||||
});
|
|
||||||
poller.pollInBackground(subscription); // Dangle!
|
poller.pollInBackground(subscription); // Dangle!
|
||||||
props.onSuccess(subscription);
|
props.onSuccess(subscription);
|
||||||
};
|
};
|
||||||
|
@ -99,12 +96,6 @@ const SubscribePage = (props) => {
|
||||||
const reserveTopicEnabled =
|
const reserveTopicEnabled =
|
||||||
session.exists() && (account?.role === Role.ADMIN || (account?.role === Role.USER && (account?.stats.reservations_remaining || 0) > 0));
|
session.exists() && (account?.role === Role.ADMIN || (account?.role === Role.USER && (account?.stats.reservations_remaining || 0) > 0));
|
||||||
|
|
||||||
const [backgroundNotificationsEnabled, setBackgroundNotificationsEnabled] = useState(false);
|
|
||||||
|
|
||||||
const handleBackgroundNotificationsChanged = (e) => {
|
|
||||||
setBackgroundNotificationsEnabled(e.target.checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubscribe = async () => {
|
const handleSubscribe = async () => {
|
||||||
const user = await userManager.get(baseUrl); // May be undefined
|
const user = await userManager.get(baseUrl); // May be undefined
|
||||||
const username = user ? user.username : t("subscribe_dialog_error_user_anonymous");
|
const username = user ? user.username : t("subscribe_dialog_error_user_anonymous");
|
||||||
|
@ -142,15 +133,12 @@ const SubscribePage = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for user ${username}`);
|
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for user ${username}`);
|
||||||
props.onSuccess(backgroundNotificationsEnabled);
|
props.onSuccess();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUseAnotherChanged = (e) => {
|
const handleUseAnotherChanged = (e) => {
|
||||||
props.setBaseUrl("");
|
props.setBaseUrl("");
|
||||||
setAnotherServerVisible(e.target.checked);
|
setAnotherServerVisible(e.target.checked);
|
||||||
if (e.target.checked) {
|
|
||||||
setBackgroundNotificationsEnabled(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const subscribeButtonEnabled = (() => {
|
const subscribeButtonEnabled = (() => {
|
||||||
|
@ -256,22 +244,6 @@ const SubscribePage = (props) => {
|
||||||
)}
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
{notifier.pushPossible() && !anotherServerVisible && (
|
|
||||||
<FormGroup>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
onChange={handleBackgroundNotificationsChanged}
|
|
||||||
checked={backgroundNotificationsEnabled}
|
|
||||||
inputProps={{
|
|
||||||
"aria-label": t("subscribe_dialog_subscribe_enable_background_notifications_label"),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={t("subscribe_dialog_subscribe_enable_background_notifications_label")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogFooter status={error}>
|
<DialogFooter status={error}>
|
||||||
<Button onClick={props.onCancel}>{t("subscribe_dialog_subscribe_button_cancel")}</Button>
|
<Button onClick={props.onCancel}>{t("subscribe_dialog_subscribe_button_cancel")}</Button>
|
||||||
|
|
|
@ -15,19 +15,17 @@ import {
|
||||||
MenuItem,
|
MenuItem,
|
||||||
IconButton,
|
IconButton,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
|
||||||
Divider,
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
Check,
|
|
||||||
Clear,
|
Clear,
|
||||||
ClearAll,
|
ClearAll,
|
||||||
Edit,
|
Edit,
|
||||||
EnhancedEncryption,
|
EnhancedEncryption,
|
||||||
Lock,
|
Lock,
|
||||||
LockOpen,
|
LockOpen,
|
||||||
|
Notifications,
|
||||||
NotificationsOff,
|
NotificationsOff,
|
||||||
RemoveCircle,
|
RemoveCircle,
|
||||||
Send,
|
Send,
|
||||||
|
@ -44,7 +42,6 @@ import api from "../app/Api";
|
||||||
import { AccountContext } from "./App";
|
import { AccountContext } from "./App";
|
||||||
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
|
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
|
||||||
import { UnauthorizedError } from "../app/errors";
|
import { UnauthorizedError } from "../app/errors";
|
||||||
import notifier from "../app/Notifier";
|
|
||||||
|
|
||||||
export const SubscriptionPopup = (props) => {
|
export const SubscriptionPopup = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -169,8 +166,8 @@ export const SubscriptionPopup = (props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PopupMenu horizontal={placement} anchorEl={props.anchor} open={!!props.anchor} onClose={props.onClose}>
|
<PopupMenu horizontal={placement} anchorEl={props.anchor} open={!!props.anchor} onClose={props.onClose}>
|
||||||
{notifier.pushPossible() && <NotificationToggle subscription={subscription} />}
|
<NotificationToggle subscription={subscription} />
|
||||||
<Divider />
|
|
||||||
<MenuItem onClick={handleChangeDisplayName}>
|
<MenuItem onClick={handleChangeDisplayName}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Edit fontSize="small" />
|
<Edit fontSize="small" />
|
||||||
|
@ -334,44 +331,27 @@ const DisplayNameDialog = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkedItem = (
|
|
||||||
<ListItemIcon>
|
|
||||||
<Check />
|
|
||||||
</ListItemIcon>
|
|
||||||
);
|
|
||||||
|
|
||||||
const NotificationToggle = ({ subscription }) => {
|
const NotificationToggle = ({ subscription }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleToggleBackground = async () => {
|
const handleToggleMute = async () => {
|
||||||
try {
|
const mutedUntil = subscription.mutedUntil ? 0 : 1; // Make this a timestamp in the future
|
||||||
await subscriptionManager.toggleBackgroundNotifications(subscription);
|
await subscriptionManager.setMutedUntil(subscription.id, mutedUntil);
|
||||||
} catch (e) {
|
|
||||||
console.error("[NotificationToggle] Error setting notification type", e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const unmute = async () => {
|
return subscription.mutedUntil ? (
|
||||||
await subscriptionManager.setMutedUntil(subscription.id, 0);
|
<MenuItem onClick={handleToggleMute}>
|
||||||
};
|
<ListItemIcon>
|
||||||
|
<Notifications />
|
||||||
if (subscription.mutedUntil === 1) {
|
</ListItemIcon>
|
||||||
return (
|
{t("notification_toggle_unmute")}
|
||||||
<MenuItem onClick={unmute}>
|
</MenuItem>
|
||||||
<ListItemIcon>
|
) : (
|
||||||
<NotificationsOff />
|
<MenuItem onClick={handleToggleMute}>
|
||||||
</ListItemIcon>
|
<ListItemIcon>
|
||||||
{t("notification_toggle_unmute")}
|
<NotificationsOff />
|
||||||
</MenuItem>
|
</ListItemIcon>
|
||||||
);
|
{t("notification_toggle_mute")}
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuItem>
|
|
||||||
{subscription.webPushEnabled === 1 && checkedItem}
|
|
||||||
<ListItemText inset={subscription.webPushEnabled !== 1} onClick={handleToggleBackground}>
|
|
||||||
{t("notification_toggle_background")}
|
|
||||||
</ListItemText>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue