tried fixing infinite loop

This commit is contained in:
Mamalizz
2025-03-27 14:15:14 +03:30
parent 8fd03bba13
commit 5d78f61f87
4 changed files with 192 additions and 49 deletions
@@ -0,0 +1,92 @@
import { API_ENDPOINTS } from "~/constants";
import useSubscribeNotification from "~/composables/api/notifications/useSubscribeNotification";
import { useToast } from "~/composables/global/useToast";
interface VapidKeys {
publicKey: string;
}
export const usePushNotifications = () => {
const isSupported = ref(false);
const permission = usePermission("notifications");
const subscription = useLocalStorage<PushSubscriptionJSON | null>(
"push-subscription",
null
);
const vapid = ref<VapidKeys | null>(null);
const { mutateAsync: subscribeNotification } = useSubscribeNotification();
const { addToast } = useToast();
const unsubscribe = async () => {
const swRegistration = await navigator.serviceWorker.ready;
const existingSubscription =
await swRegistration.pushManager.getSubscription();
if (existingSubscription) {
await existingSubscription.unsubscribe();
}
};
onMounted(async () => {
if (typeof window !== "undefined" && "serviceWorker" in navigator) {
isSupported.value = true;
vapid.value = await $fetch("/api/vapid");
}
});
const subscribe = async () => {
if (!isSupported.value || !vapid.value?.publicKey) {
throw new Error("Push notifications not supported");
}
const swRegistration = await navigator.serviceWorker.ready;
await unsubscribe();
const applicationServerKey = vapid.value.publicKey
.replace(/-/g, "+")
.replace(/_/g, "/");
const convertedKey = Uint8Array.from(atob(applicationServerKey), (c) =>
c.charCodeAt(0)
);
const pushSubscription = await swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedKey,
});
const subscriptionJson = pushSubscription.toJSON();
subscribeNotification(
{
body: subscriptionJson,
},
{
onSuccess: () => {
addToast({
message: "اعلانات برای دستگاه شما فعال شد",
});
},
onError: () => {
addToast({
message: "خطایی در فعال شدن اعلانات رخ داد",
options: {
status: "error",
},
});
},
}
);
subscription.value = subscriptionJson;
};
return {
isSupported,
permission,
subscribe,
unsubscribe,
subscription,
};
};
+31 -26
View File
@@ -6,45 +6,45 @@ export default defineNuxtConfig({
css: [ css: [
"~/assets/css/tailwind.css", "~/assets/css/tailwind.css",
"swiper/css", "swiper/css",
"animate.css/animate.min.css" "animate.css/animate.min.css",
], ],
routeRules: { routeRules: {
"/products": { prerender: false, ssr: false } "/products": { prerender: false, ssr: false },
}, },
app: { app: {
head: { head: {
title: "فروشگاه هی ملز" title: "فروشگاه هی ملز",
}, },
pageTransition: { pageTransition: {
enterActiveClass: enterActiveClass:
"animate__animated animate__fadeIn animate__faster", "animate__animated animate__fadeIn animate__faster",
leaveActiveClass: leaveActiveClass:
"animate__animated animate__fadeOut animate__faster", "animate__animated animate__fadeOut animate__faster",
mode: "out-in" mode: "out-in",
}, },
layoutTransition: { layoutTransition: {
enterActiveClass: enterActiveClass:
"animate__animated animate__fadeIn animate__faster", "animate__animated animate__fadeIn animate__faster",
leaveActiveClass: leaveActiveClass:
"animate__animated animate__fadeOut animate__faster", "animate__animated animate__fadeOut animate__faster",
mode: "out-in" mode: "out-in",
} },
}, },
postcss: { postcss: {
plugins: { plugins: {
"@tailwindcss/postcss": {}, "@tailwindcss/postcss": {},
autoprefixer: {} autoprefixer: {},
} },
}, },
components: [ components: [
{ {
path: "~/components", path: "~/components",
pathPrefix: false pathPrefix: false,
} },
], ],
icon: { icon: {
@@ -52,9 +52,9 @@ export default defineNuxtConfig({
customCollections: [ customCollections: [
{ {
prefix: "ci", prefix: "ci",
dir: "./public/icons" dir: "./public/icons",
} },
] ],
}, },
modules: [ modules: [
@@ -65,21 +65,23 @@ export default defineNuxtConfig({
"DM Sans": "100..900", "DM Sans": "100..900",
Inter: "100..900", Inter: "100..900",
download: true, download: true,
inject: false inject: false,
} },
} },
], ],
"@nuxt/icon", "@nuxt/icon",
"reka-ui/nuxt", "reka-ui/nuxt",
"@vueuse/nuxt", "@vueuse/nuxt",
"@formkit/auto-animate/nuxt", "@formkit/auto-animate/nuxt",
"@vite-pwa/nuxt" "@vite-pwa/nuxt",
], ],
pwa: { pwa: {
strategies: "injectManifest", strategies: "injectManifest",
srcDir: "public", srcDir: "public",
filename: "sw.js", filename: "sw.js",
registerType:
process.env.NODE_ENV === "production" ? "autoUpdate" : "prompt",
manifest: { manifest: {
name: "Heymlz", name: "Heymlz",
short_name: "Heymlz", short_name: "Heymlz",
@@ -88,27 +90,30 @@ export default defineNuxtConfig({
{ {
src: "/logo/logo-192x192.png", src: "/logo/logo-192x192.png",
sizes: "192x192", sizes: "192x192",
type: "image/png" type: "image/png",
}, },
{ {
src: "/logo/logo-512x512.png", src: "/logo/logo-512x512.png",
sizes: "512x512", sizes: "512x512",
type: "image/png" type: "image/png",
} },
] ],
}, },
workbox: { workbox: {
navigateFallback: "/", navigateFallback: "/",
clientsClaim: true, clientsClaim: true,
skipWaiting: true skipWaiting: true,
},
devOptions: {
enabled: process.env.NODE_ENV === "production",
type: "module",
}, },
devOptions: { enabled: true, type: "module" }
}, },
runtimeConfig: { runtimeConfig: {
public: { public: {
API_BASE_URL: process.env.API_BASE_URL, API_BASE_URL: process.env.API_BASE_URL,
DEBUG: process.env.DEBUG DEBUG: process.env.DEBUG,
} },
} },
}); });
+52 -8
View File
@@ -8,29 +8,73 @@ export default defineNuxtPlugin(() => {
const { isInstalledAsPWA } = usePWA(); const { isInstalledAsPWA } = usePWA();
if ("serviceWorker" in navigator && isInstalledAsPWA.value) { if ("serviceWorker" in navigator && isInstalledAsPWA.value) {
// Initialize Workbox
wb = new Workbox("/sw.js"); wb = new Workbox("/sw.js");
wb.addEventListener("waiting", () => { navigator.serviceWorker
checkForUpdate(); .register("/sw.js")
}); .then((registration) => {
// Native Service Worker API for update detection
registration.addEventListener("updatefound", () => {
const newWorker = registration.installing;
if (newWorker) {
newWorker.addEventListener("statechange", () => {
if (newWorker.state === "installed") {
// Only show prompt if there's a controller (not first install)
if (navigator.serviceWorker.controller) {
checkForUpdate();
}
}
});
}
});
wb.register() // Workbox events for consistency
.then((registration: any) => { wb?.addEventListener("waiting", () => {
checkForUpdate();
});
// Check if there's already a waiting worker
if (registration.waiting) { if (registration.waiting) {
checkForUpdate(); checkForUpdate();
} }
// Periodic update checks (optional)
setInterval(() => {
registration.update().catch((err) => {
console.debug(
"Service worker update check failed:",
err
);
});
}, 60 * 60 * 1000); // Check every hour
}) })
.catch((error) => { .catch((err) => {
console.error("Service worker registration failed:", error); console.error("Service worker registration failed:", err);
}); });
// Register Workbox
wb.register().catch((error) => {
console.error("Workbox registration failed:", error);
});
} }
const checkForUpdate = () => { const checkForUpdate = () => {
updateAvailable.value = true; if (!updateAvailable.value) {
updateAvailable.value = true;
}
}; };
const handleUpdate = () => { const handleUpdate = () => {
if (wb) { if (wb) {
// Send skip waiting message
wb.messageSW({ type: "SKIP_WAITING" }).then(() => { wb.messageSW({ type: "SKIP_WAITING" }).then(() => {
// Notify all tabs to reload
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage({
type: "CLIENT_RELOAD",
});
}
window.location.reload(); window.location.reload();
}); });
} }
+17 -15
View File
@@ -3,33 +3,36 @@ import { precacheAndRoute } from "workbox-precaching";
// Precaching configuration for PWA assets // Precaching configuration for PWA assets
precacheAndRoute(self.__WB_MANIFEST); precacheAndRoute(self.__WB_MANIFEST);
// Version
const VERSION = "1.0.4"; const VERSION = "1.0.4";
// Service Worker Installation // Only enable skipWaiting and claim in production
const isProduction = process.env.NODE_ENV === "production";
self.addEventListener("install", (event) => { self.addEventListener("install", (event) => {
self.skipWaiting(); if (isProduction) {
self.skipWaiting();
}
}); });
// Service Worker Activation
self.addEventListener("activate", (event) => { self.addEventListener("activate", (event) => {
event.waitUntil( event.waitUntil(
(async () => { (async () => {
const clients = await self.clients.matchAll({ type: "window" }); if (isProduction) {
const clients = await self.clients.matchAll({ type: "window" });
// Notify all open clients about the version clients.forEach((client) =>
clients.forEach((client) => client.postMessage({
client.postMessage({ type: "VERSION_CHECK", version: VERSION }) type: "VERSION_CHECK",
); version: VERSION,
})
self.clients.claim(); );
self.clients.claim();
}
console.log("Service Worker Activated (Version: " + VERSION + ")"); console.log("Service Worker Activated (Version: " + VERSION + ")");
})() })()
); );
}); });
// Push Notification Handler for Django Web Push // Rest of your existing handlers remain the same...
self.addEventListener("push", (event) => { self.addEventListener("push", (event) => {
try { try {
const payload = event.data?.json() || { const payload = event.data?.json() || {
@@ -51,7 +54,6 @@ self.addEventListener("push", (event) => {
} }
}); });
// Notification Click Handler
self.addEventListener("notificationclick", (event) => { self.addEventListener("notificationclick", (event) => {
event.notification.close(); event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data?.url || "/")); event.waitUntil(clients.openWindow(event.notification.data?.url || "/"));