PageSourceSearch

https://delivdash-fptcqsw5.manus.space/sw.js

js delivdash-fptcqsw5.manus.space collected 2026-10-03 05:01:26 UTC 4,508 bytes, 121 lines download raw bytes

1/* Service Worker — إشعارات Web Push المرئية والصوتية على الشاشة الرئيسية */
2self.addEventListener("install", (event) => {
3  self.skipWaiting();
4});
5
6self.addEventListener("activate", (event) => {
7  event.waitUntil(self.clients.claim());
8});
9
10// أنماط اهتزاز مختلفة حسب نوع الحدث
11const VIBRATION_PATTERNS = {
12  order: [300, 100, 300, 100, 500],
13  message: [150, 80, 150],
14  instruction: [200, 100, 200, 100, 200, 100, 400],
15  general: [200, 100, 200],
16};
17
18self.addEventListener("push", (event) => {
19  let data = { title: "إشعار جديد", body: "", url: "/", tag: undefined, kind: "general" };
20  try {
21    if (event.data) data = { ...data, ...event.data.json() };
22  } catch (e) {
23    if (event.data) data.body = event.data.text();
24  }
25  const kind = VIBRATION_PATTERNS[data.kind] ? data.kind : "general";
26  const options = {
27    body: data.body,
28    dir: "rtl",
29    lang: "ar",
30    icon: "/pwa-icon-192.png",
31    badge: "/pwa-badge.png",
32    tag: data.tag,
33    renotify: true,
34    requireInteraction: false,
35    silent: false,
36    vibrate: VIBRATION_PATTERNS[kind],
37    data: { url: data.url || "/", kind },
38  };
39  event.waitUntil(
40    Promise.all([
41      self.registration.showNotification(data.title, options),
42      // إبلاغ النوافذ المفتوحة لتشغيل الصوت المخصص حسب نوع الحدث
43      self.clients
44        .matchAll({ type: "window", includeUncontrolled: true })
45        .then((clientList) => {
46          for (const client of clientList) {
47            client.postMessage({ type: "push-received", kind, title: data.title });
48          }
49        })
50        .catch(() => {}),
51    ]),
52  );
53});
54
55self.addEventListener("notificationclick", (event) => {
56  event.notification.close();
57  const targetUrl = (event.notification.data && event.notification.data.url) || "/";
58  event.waitUntil(
59    self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
60      for (const client of clientList) {
61        // إن كانت هناك نافذة مفتوحة للتطبيق ركّز عليها وانتقل للرابط
62        if ("focus" in client) {
63          client.focus();
64          if ("navigate" in client && targetUrl !== "/") {
65            client.navigate(targetUrl).catch(() => {});
66          }
67          return;
68        }
69      }
70      if (self.clients.openWindow) return self.clients.openWindow(targetUrl);
71    }),
72  );
73});
74
75// تجديد الاشتراك تلقائياً عند تغييره من متصفح/نظام التشغيل (انتهاء صلاحية المفاتيح مثلاً)
76// دون أي تدخل من المستخدم: نعيد الاشتراك بنفس مفتاح VAPID ونبلّغ الخادم بالاستبدال
77self.addEventListener("pushsubscriptionchange", (event) => {
78  const oldEndpoint =
79    (event.oldSubscription && event.oldSubscription.endpoint) || null;
80  const applicationServerKey =
81    (event.oldSubscription && event.oldSubscription.options
82      ? event.oldSubscription.options.applicationServerKey
83      : null) ||
84    (event.newSubscription && event.newSubscription.options
85      ? event.newSubscription.options.applicationServerKey
86      : null);
87
88  const renew = async () => {
89    try {
90      // استخدم الاشتراك الجديد إن وفّره المتصفح، وإلا أعد الاشتراك بنفس المفتاح
91      let sub = event.newSubscription || null;
92      if (!sub) {
93        if (!applicationServerKey) return;
94        sub = await self.registration.pushManager.subscribe({
95          userVisibleOnly: true,
96          applicationServerKey,
97        });
98      }
99      if (!sub) return;
100      const json = sub.toJSON();
101      if (!json.endpoint || !json.keys || !json.keys.p256dh || !json.keys.auth) return;
102      // أبلغ الخادم باستبدال الاشتراك القديم بالجديد (tRPC mutation عبر HTTP POST)
103      await fetch("/api/trpc/push.renew", {
104        method: "POST",
105        headers: { "Content-Type": "application/json" },
106        body: JSON.stringify({
107          json: {
108            oldEndpoint: oldEndpoint || json.endpoint,
109            endpoint: json.endpoint,
110            p256dh: json.keys.p256dh,
111            auth: json.keys.auth,
112          },
113        }),
114      });
115    } catch (e) {
116      // صامت — ستُعاد المزامنة عند فتح التطبيق التالي عبر usePushNotifications
117    }
118  };
119
120  event.waitUntil(renew());
121});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.