Improve service worker type safety

This commit is contained in:
2025-09-21 14:00:52 +03:00
Unverified
parent 9a25bdcf62
commit b53deae633
4 changed files with 135 additions and 60 deletions
-57
View File
@@ -1,57 +0,0 @@
// Service Worker for Push Notifications
self.addEventListener("push", function(event) {
if (event.data) {
const data = event.data.json();
const options = {
body: data.body,
icon: data.icon || "/logo.png",
badge: "/logo.png",
image: data.image,
tag: data.tag || "message",
data: data.data,
actions: [
{
action: "open",
title: "Open Chat"
},
{
action: "close",
title: "Close"
}
],
requireInteraction: true,
silent: false
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
}
});
self.addEventListener("notificationclick", function(event) {
event.notification.close();
if (event.action === "open" || !event.action) {
event.waitUntil(
clients.matchAll({ type: "window" }).then(function(clientList) {
// If there's already a window open, focus it
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === self.location.origin && "focus" in client) {
return client.focus();
}
}
// Otherwise, open a new window
if (clients.openWindow) {
return clients.openWindow(self.location.origin);
}
})
);
}
});
self.addEventListener("notificationclose", function(event) {
// Handle notification close if needed
});
@@ -0,0 +1,89 @@
/// <reference lib="webworker" />
declare const self: ServiceWorkerGlobalScope;
interface NotificationPayload {
title: string;
body: string;
icon?: string;
image?: string;
tag?: string;
data?: any;
}
interface NotificationAction {
action: string;
title: string;
}
interface NotificationOptions {
body: string;
icon: string;
badge: string;
image?: string;
tag: string;
data?: any;
actions: NotificationAction[];
requireInteraction: boolean;
silent: boolean;
}
// Service Worker for Push Notifications
self.addEventListener("push", function(event: ExtendableEvent) {
const pushEvent = event as PushEvent;
if (pushEvent.data) {
const data: NotificationPayload = pushEvent.data.json();
const options: NotificationOptions = {
body: data.body,
icon: data.icon || "/logo.png",
badge: "/logo.png",
image: data.image,
tag: data.tag || "message",
data: data.data,
actions: [
{
action: "open",
title: "Open Chat"
},
{
action: "close",
title: "Close"
}
],
requireInteraction: true,
silent: false
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
}
});
self.addEventListener("notificationclick", function(event: ExtendableEvent) {
const notificationEvent = event as NotificationEvent;
notificationEvent.notification.close();
if (notificationEvent.action === "open" || !notificationEvent.action) {
event.waitUntil(
self.clients.matchAll({ type: "window" }).then(function(clientList: readonly WindowClient[]) {
// If there's already a window open, focus it
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === self.location.origin && "focus" in client) {
return client.focus();
}
}
// Otherwise, open a new window
if (self.clients.openWindow) {
return self.clients.openWindow(self.location.origin);
}
})
);
}
});
self.addEventListener("notificationclose", function(_event: ExtendableEvent) {
// Handle notification close if needed
});
+1 -1
View File
@@ -28,7 +28,7 @@ class PushNotificationManager {
}
try {
this.registration = await navigator.serviceWorker.register("/sw.js");
this.registration = await navigator.serviceWorker.register("/assets/serviceWorker.js");
console.log("Service Worker registered successfully");
return true;
} catch (error) {
+45 -2
View File
@@ -3,6 +3,9 @@ import { createHtmlPlugin } from "vite-plugin-html";
import autoprefixer from "autoprefixer";
import electron from "vite-plugin-electron/simple";
import react from "@vitejs/plugin-react";
import { resolve } from "path";
const serviceWorkerPath = resolve(__dirname, "src/service-worker/service-worker.ts");
const plugins: PluginOption[] = [
react(),
@@ -17,7 +20,31 @@ const plugins: PluginOption[] = [
minifyCSS: true,
minifyJS: true
}
})
}),
{
name: 'service-worker-redirect',
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
if (req.url === '/assets/serviceWorker.js') {
try {
const traspiled = await server.transformRequest(serviceWorkerPath);
res.writeHead(200, {
'Content-Type': "application/javascript"
});
res.end(traspiled!.code);
} catch (e) {
try {
res.writeHead(500);
res.end();
} catch (e) {}
}
} else {
next();
}
});
}
}
]
if (process.env.VITE_ELECTRON) {
@@ -79,6 +106,22 @@ export default defineConfig({
},
cssMinify: true,
assetsInlineLimit: 0,
outDir: process.env.VITE_ELECTRON ? "build/electron/dist" : "build/normal/dist"
outDir: process.env.VITE_ELECTRON ? "build/electron/dist" : "build/normal/dist",
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
serviceWorker: serviceWorkerPath
},
output: {
entryFileNames: (chunkInfo) => {
// Проверяем имя чанка
if (chunkInfo.name === 'serviceWorker') {
return 'assets/serviceWorker.js'; // Указываем фиксированное имя для этого скрипта
}
// Для остальных файлов используем стандартное именование с хэшем
return 'assets/[name]-[hash].js';
}
}
}
}
});