diff --git a/docs/publish.md b/docs/publish.md index 4f68c572..00c43b5e 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -2304,6 +2304,11 @@ _Supported on:_ :material-android: :material-firefox: The `copy` action **copies a given value to the clipboard when the action button is tapped**. This is useful for one-time passcodes, tokens, or any other value you want to quickly copy without opening the full notification. +!!! info + The copy action button is only shown in the web app and Android app notification list, **not** in browser desktop + notifications. This is because browsers do not allow clipboard access from notification actions without direct + user interaction with the page. + Here's an example using the [`X-Actions` header](#using-a-header): === "Command line (curl)" diff --git a/web/public/sw.js b/web/public/sw.js index a09cb56e..fa2ac9e0 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -4,7 +4,7 @@ import { NavigationRoute, registerRoute } from "workbox-routing"; import { NetworkFirst } from "workbox-strategies"; import { clientsClaim } from "workbox-core"; import { dbAsync } from "../src/app/db"; -import { ACTION_COPY, ACTION_HTTP, ACTION_VIEW } from "../src/app/actions"; +import { ACTION_HTTP, ACTION_VIEW } from "../src/app/actions"; import { badge, icon, messageWithSequenceId, notificationTag, toNotificationParams } from "../src/app/notificationUtils"; import initI18n from "../src/app/i18n"; import { @@ -256,26 +256,6 @@ const handleClick = async (event) => { if (action.clear) { await clearNotification(); } - } else if (action.action === ACTION_COPY) { - try { - // Service worker can't access the clipboard API directly, so we try to - // open a focused client and use it, or fall back to opening a window - const allClients = await self.clients.matchAll({ type: "window" }); - const focusedClient = allClients.find((c) => c.focused) || allClients[0]; - if (focusedClient) { - focusedClient.postMessage({ type: "copy", value: action.value }); - } - if (action.clear) { - await clearNotification(); - } - } catch (e) { - console.error("[ServiceWorker] Error performing copy action", e); - self.registration.showNotification(`${t("notifications_actions_failed_notification")}: ${action.label} (${action.action})`, { - body: e.message, - icon, - badge, - }); - } } else if (action.action === ACTION_HTTP) { try { const response = await fetch(action.url, { diff --git a/web/src/app/notificationUtils.js b/web/src/app/notificationUtils.js index ed75832c..070f8231 100644 --- a/web/src/app/notificationUtils.js +++ b/web/src/app/notificationUtils.js @@ -2,7 +2,7 @@ // and cannot be used in the service worker import emojisMapped from "./emojisMapped"; -import { ACTION_COPY, ACTION_HTTP, ACTION_VIEW } from "./actions"; +import { ACTION_HTTP, ACTION_VIEW } from "./actions"; const toEmojis = (tags) => { if (!tags) return []; @@ -82,7 +82,7 @@ export const toNotificationParams = ({ message, defaultTitle, topicRoute, baseUr topicRoute, }, actions: message.actions - ?.filter(({ action }) => action === ACTION_VIEW || action === ACTION_HTTP || action === ACTION_COPY) + ?.filter(({ action }) => action === ACTION_VIEW || action === ACTION_HTTP) .map(({ label }) => ({ action: label, title: label, diff --git a/web/src/registerSW.js b/web/src/registerSW.js index fa90af19..842cf80e 100644 --- a/web/src/registerSW.js +++ b/web/src/registerSW.js @@ -12,15 +12,6 @@ const registerSW = () => { return; } - // Listen for messages from the service worker (e.g., "copy" action) - navigator.serviceWorker.addEventListener("message", (event) => { - if (event.data?.type === "copy" && event.data?.value) { - navigator.clipboard?.writeText(event.data.value).catch((e) => { - console.error("[ServiceWorker] Failed to copy to clipboard", e); - }); - } - }); - viteRegisterSW({ onRegisteredSW(swUrl, registration) { console.log("[ServiceWorker] Registered:", { swUrl, registration });