Copy fix
This commit is contained in:
@@ -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)"
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user