Просмотр исходного кода

Web: Fix `clear=true` on action buttons not clearing the notification

binwiederhier 1 неделя назад
Родитель
Сommit
1b554d5b08
4 измененных файлов с 44 добавлено и 5 удалено
  1. 1 0
      docs/releases.md
  2. 21 4
      web/public/sw.js
  3. 2 0
      web/src/app/notificationUtils.js
  4. 20 1
      web/src/components/Notifications.jsx

+ 1 - 0
docs/releases.md

@@ -1688,6 +1688,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
 
 **Bug fixes + maintenance:**
 
+* Web: Fix `clear=true` on action buttons not clearing the notification ([#1029](https://github.com/binwiederhier/ntfy/issues/1029), thanks to [@ElFishi](https://github.com/ElFishi) for reporting)
 * Fix crash when commit string is shorter than 7 characters in non-GitHub-Action builds ([#1493](https://github.com/binwiederhier/ntfy/issues/1493), thanks to [@cyrinux](https://github.com/cyrinux) for reporting)
 * Fix log spam from `http: response.WriteHeader on hijacked connection` for WebSocket errors ([#1362](https://github.com/binwiederhier/ntfy/issues/1362), thanks to [@bonfiresh](https://github.com/bonfiresh) for reporting)
 * Web: Fix Markdown message line height to match plain text (1.5 instead of 1.2) ([#1139](https://github.com/binwiederhier/ntfy/issues/1139), thanks to [@etfz](https://github.com/etfz) for reporting)

+ 21 - 4
web/public/sw.js

@@ -237,8 +237,24 @@ const handleClick = async (event) => {
     if (event.action) {
       const action = event.notification.data.message.actions.find(({ label }) => event.action === label);
 
+      // Helper to clear notification and mark as read
+      const clearNotification = async () => {
+        event.notification.close();
+        const { subscriptionId, message: msg } = event.notification.data;
+        const seqId = msg.sequence_id || msg.id;
+        if (subscriptionId && seqId) {
+          const db = await dbAsync();
+          await db.notifications.where({ subscriptionId, sequenceId: seqId }).modify({ new: 0 });
+          const badgeCount = await db.notifications.where({ new: 1 }).count();
+          self.navigator.setAppBadge?.(badgeCount);
+        }
+      };
+
       if (action.action === "view") {
         self.clients.openWindow(action.url);
+        if (action.clear) {
+          await clearNotification();
+        }
       } else if (action.action === "http") {
         try {
           const response = await fetch(action.url, {
@@ -250,6 +266,11 @@ const handleClick = async (event) => {
           if (!response.ok) {
             throw new Error(`HTTP ${response.status} ${response.statusText}`);
           }
+
+          // Only clear on success
+          if (action.clear) {
+            await clearNotification();
+          }
         } catch (e) {
           console.error("[ServiceWorker] Error performing http action", e);
           self.registration.showNotification(`${t("notifications_actions_failed_notification")}: ${action.label} (${action.action})`, {
@@ -259,10 +280,6 @@ const handleClick = async (event) => {
           });
         }
       }
-
-      if (action.clear) {
-        event.notification.close();
-      }
     } else if (message.click) {
       self.clients.openWindow(message.click);
 

+ 2 - 0
web/src/app/notificationUtils.js

@@ -60,6 +60,7 @@ export const toNotificationParams = ({ message, defaultTitle, topicRoute, baseUr
   const image = isImage(message.attachment) ? message.attachment.url : undefined;
   const sequenceId = message.sequence_id || message.id;
   const tag = notificationTag(baseUrl, topic, sequenceId);
+  const subscriptionId = `${baseUrl}/${topic}`;
 
   // https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API
   return [
@@ -75,6 +76,7 @@ export const toNotificationParams = ({ message, defaultTitle, topicRoute, baseUr
       silent: false,
       // This is used by the notification onclick event
       data: {
+        subscriptionId,
         message,
         topicRoute,
       },

+ 20 - 1
web/src/components/Notifications.jsx

@@ -39,6 +39,7 @@ import {
 import { formatMessage, formatTitle, isImage } from "../app/notificationUtils";
 import { LightboxBackdrop, Paragraph, VerticallyCenteredContainer } from "./styles";
 import subscriptionManager from "../app/SubscriptionManager";
+import notifier from "../app/Notifier";
 import priority1 from "../img/priority-1.svg";
 import priority2 from "../img/priority-2.svg";
 import priority4 from "../img/priority-4.svg";
@@ -508,6 +509,15 @@ const updateActionStatus = (notification, action, progress, error) => {
   });
 };
 
+const clearNotification = async (notification) => {
+  console.log(`[Notifications] Clearing notification ${notification.id}`);
+  const subscription = await subscriptionManager.get(notification.subscriptionId);
+  if (subscription) {
+    await notifier.cancel(subscription, notification);
+  }
+  await subscriptionManager.markNotificationRead(notification.id);
+};
+
 const performHttpAction = async (notification, action) => {
   console.log(`[Notifications] Performing HTTP user action`, action);
   try {
@@ -523,6 +533,9 @@ const performHttpAction = async (notification, action) => {
     const success = response.status >= 200 && response.status <= 299;
     if (success) {
       updateActionStatus(notification, action, ACTION_PROGRESS_SUCCESS, null);
+      if (action.clear) {
+        await clearNotification(notification);
+      }
     } else {
       updateActionStatus(notification, action, ACTION_PROGRESS_FAILED, `${action.label}: Unexpected response HTTP ${response.status}`);
     }
@@ -548,10 +561,16 @@ const UserAction = (props) => {
     );
   }
   if (action.action === "view") {
+    const handleClick = () => {
+      openUrl(action.url);
+      if (action.clear) {
+        clearNotification(notification);
+      }
+    };
     return (
       <Tooltip title={t("notifications_actions_open_url_title", { url: action.url })}>
         <Button
-          onClick={() => openUrl(action.url)}
+          onClick={handleClick}
           aria-label={t("notifications_actions_open_url_title", {
             url: action.url,
           })}