Initial import
This commit is contained in:
161
src/sw.js
Executable file
161
src/sw.js
Executable file
@@ -0,0 +1,161 @@
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
import { clientsClaim } from 'workbox-core';
|
||||
import { precacheAndRoute, cleanupOutdatedCaches, createHandlerBoundToURL } from 'workbox-precaching';
|
||||
import { NavigationRoute, registerRoute } from 'workbox-routing';
|
||||
|
||||
clientsClaim();
|
||||
cleanupOutdatedCaches();
|
||||
|
||||
const manifest = self.__WB_MANIFEST;
|
||||
|
||||
if (Array.isArray(manifest) && manifest.length > 0) {
|
||||
precacheAndRoute(manifest);
|
||||
const navigationHandler = createHandlerBoundToURL('/index.html');
|
||||
registerRoute(new NavigationRoute(navigationHandler));
|
||||
} else {
|
||||
registerRoute(
|
||||
new NavigationRoute(({ request }) => {
|
||||
return fetch(request);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data?.type === 'SKIP_WAITING') {
|
||||
void self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener('push', (event) => {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let payload = {};
|
||||
|
||||
try {
|
||||
payload = event.data.json();
|
||||
} catch {
|
||||
payload = {
|
||||
title: '알림',
|
||||
body: event.data.text(),
|
||||
data: {},
|
||||
};
|
||||
}
|
||||
|
||||
const title = payload.title || 'AI Code App';
|
||||
const body = payload.body || '새 알림이 도착했습니다.';
|
||||
const notificationScope = payload.data?.notificationScope;
|
||||
const notificationKey =
|
||||
payload.data?.notificationKey ||
|
||||
[payload.threadId ?? 'ai-code-app-notification', payload.data?.eventType ?? 'event', Date.now()].join(':');
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.getNotifications().then((notifications) => {
|
||||
if (notificationScope === 'automation') {
|
||||
notifications
|
||||
.filter((notification) => notification.data?.notificationScope === 'automation')
|
||||
.forEach((notification) => notification.close());
|
||||
}
|
||||
|
||||
return self.registration.showNotification(title, {
|
||||
body,
|
||||
data: payload.data ?? {},
|
||||
tag: notificationKey,
|
||||
badge: '/pwa-192x192.svg',
|
||||
icon: '/pwa-192x192.svg',
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('notificationclick', (event) => {
|
||||
const notificationData = event.notification.data ?? {};
|
||||
const notificationSessionId = typeof notificationData.sessionId === 'string' ? notificationData.sessionId.trim() : '';
|
||||
const notificationCategory = typeof notificationData.category === 'string' ? notificationData.category.trim() : '';
|
||||
const notificationType = typeof notificationData.type === 'string' ? notificationData.type.trim() : '';
|
||||
const notificationThreadId = typeof notificationData.threadId === 'string' ? notificationData.threadId.trim() : '';
|
||||
|
||||
event.notification.close();
|
||||
let targetUrl;
|
||||
|
||||
try {
|
||||
targetUrl = notificationData.targetUrl ? new URL(String(notificationData.targetUrl)) : new URL('/', self.location.origin);
|
||||
} catch {
|
||||
targetUrl = new URL('/', self.location.origin);
|
||||
}
|
||||
|
||||
if (targetUrl.origin !== self.location.origin) {
|
||||
const fallbackUrl = new URL('/', self.location.origin);
|
||||
targetUrl.searchParams.forEach((value, key) => {
|
||||
fallbackUrl.searchParams.set(key, value);
|
||||
});
|
||||
targetUrl = fallbackUrl;
|
||||
}
|
||||
|
||||
if (!targetUrl.searchParams.has('topMenu')) {
|
||||
targetUrl.searchParams.set('topMenu', notificationData.category === 'chat' ? 'chat' : 'plans');
|
||||
}
|
||||
|
||||
if (notificationData.planId && !targetUrl.searchParams.has('planId')) {
|
||||
targetUrl.searchParams.set('planId', String(notificationData.planId));
|
||||
}
|
||||
|
||||
if (notificationData.workId && !targetUrl.searchParams.has('workId')) {
|
||||
targetUrl.searchParams.set('workId', String(notificationData.workId));
|
||||
}
|
||||
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
self.registration.getNotifications().then((notifications) => {
|
||||
const isChatNotification =
|
||||
notificationCategory === 'chat' ||
|
||||
notificationType.startsWith('chat') ||
|
||||
notificationThreadId.startsWith('chat:');
|
||||
|
||||
if (!notificationSessionId || !isChatNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifications.forEach((notification) => {
|
||||
const candidateData = notification.data && typeof notification.data === 'object' ? notification.data : null;
|
||||
const candidateSessionId =
|
||||
candidateData && typeof candidateData.sessionId === 'string' ? candidateData.sessionId.trim() : '';
|
||||
const candidateCategory =
|
||||
candidateData && typeof candidateData.category === 'string' ? candidateData.category.trim() : '';
|
||||
const candidateType =
|
||||
candidateData && typeof candidateData.type === 'string' ? candidateData.type.trim() : '';
|
||||
const candidateThreadId =
|
||||
candidateData && typeof candidateData.threadId === 'string' ? candidateData.threadId.trim() : '';
|
||||
const isCandidateChatNotification =
|
||||
candidateCategory === 'chat' ||
|
||||
candidateType.startsWith('chat') ||
|
||||
candidateThreadId.startsWith('chat:');
|
||||
|
||||
if (isCandidateChatNotification && candidateSessionId === notificationSessionId) {
|
||||
notification.close();
|
||||
}
|
||||
});
|
||||
}),
|
||||
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
|
||||
const existingClient = clientList[0];
|
||||
|
||||
if (existingClient) {
|
||||
existingClient.focus();
|
||||
|
||||
try {
|
||||
const clientUrl = new URL(existingClient.url);
|
||||
if (clientUrl.origin === targetUrl.origin) {
|
||||
return existingClient.navigate(targetUrl.toString());
|
||||
}
|
||||
} catch {
|
||||
return existingClient.navigate(targetUrl.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return self.clients.openWindow(targetUrl.toString());
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user