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
|
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.
|
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):
|
Here's an example using the [`X-Actions` header](#using-a-header):
|
||||||
|
|
||||||
=== "Command line (curl)"
|
=== "Command line (curl)"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { NavigationRoute, registerRoute } from "workbox-routing";
|
|||||||
import { NetworkFirst } from "workbox-strategies";
|
import { NetworkFirst } from "workbox-strategies";
|
||||||
import { clientsClaim } from "workbox-core";
|
import { clientsClaim } from "workbox-core";
|
||||||
import { dbAsync } from "../src/app/db";
|
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 { badge, icon, messageWithSequenceId, notificationTag, toNotificationParams } from "../src/app/notificationUtils";
|
||||||
import initI18n from "../src/app/i18n";
|
import initI18n from "../src/app/i18n";
|
||||||
import {
|
import {
|
||||||
@@ -256,26 +256,6 @@ const handleClick = async (event) => {
|
|||||||
if (action.clear) {
|
if (action.clear) {
|
||||||
await clearNotification();
|
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) {
|
} else if (action.action === ACTION_HTTP) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(action.url, {
|
const response = await fetch(action.url, {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// and cannot be used in the service worker
|
// and cannot be used in the service worker
|
||||||
|
|
||||||
import emojisMapped from "./emojisMapped";
|
import emojisMapped from "./emojisMapped";
|
||||||
import { ACTION_COPY, ACTION_HTTP, ACTION_VIEW } from "./actions";
|
import { ACTION_HTTP, ACTION_VIEW } from "./actions";
|
||||||
|
|
||||||
const toEmojis = (tags) => {
|
const toEmojis = (tags) => {
|
||||||
if (!tags) return [];
|
if (!tags) return [];
|
||||||
@@ -82,7 +82,7 @@ export const toNotificationParams = ({ message, defaultTitle, topicRoute, baseUr
|
|||||||
topicRoute,
|
topicRoute,
|
||||||
},
|
},
|
||||||
actions: message.actions
|
actions: message.actions
|
||||||
?.filter(({ action }) => action === ACTION_VIEW || action === ACTION_HTTP || action === ACTION_COPY)
|
?.filter(({ action }) => action === ACTION_VIEW || action === ACTION_HTTP)
|
||||||
.map(({ label }) => ({
|
.map(({ label }) => ({
|
||||||
action: label,
|
action: label,
|
||||||
title: label,
|
title: label,
|
||||||
|
|||||||
@@ -12,15 +12,6 @@ const registerSW = () => {
|
|||||||
return;
|
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({
|
viteRegisterSW({
|
||||||
onRegisteredSW(swUrl, registration) {
|
onRegisteredSW(swUrl, registration) {
|
||||||
console.log("[ServiceWorker] Registered:", { swUrl, registration });
|
console.log("[ServiceWorker] Registered:", { swUrl, registration });
|
||||||
|
|||||||
Reference in New Issue
Block a user