1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
export class ServiceWorkerManager {
private registration: ServiceWorkerRegistration | null = null;
private updateAvailable = false;
private onUpdateCallback?: () => void;
async initialize() {
if (!("serviceWorker" in navigator)) {
console.log("Service Workers not supported");
return;
}
try {
// First, unregister any old service workers to start fresh
const registrations = await navigator.serviceWorker.getRegistrations();
for (const registration of registrations) {
if (registration.scope.includes(window.location.origin)) {
console.log("Unregistering old service worker:", registration.scope);
await registration.unregister();
}
}
// Register the new worker with a fresh name
this.registration = await navigator.serviceWorker.register("/pwa-worker.js", {
updateViaCache: 'none' // Disable caching for the SW file itself
});
console.log("PWA Worker registered with scope:", this.registration.scope);
// Implement proper updatefound detection (web.dev pattern)
await this.detectSWUpdate();
// Check for updates periodically
setInterval(() => {
this.checkForUpdates();
}, 30 * 1000);
// Check when page becomes visible
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
this.checkForUpdates();
}
});
} catch (error) {
console.error("Service Worker registration failed:", error);
}
}
private async detectSWUpdate() {
if (!this.registration) return;
// Listen for new service worker discovery
this.registration.addEventListener("updatefound", () => {
const newSW = this.registration!.installing;
if (!newSW) return;
console.log("New service worker found, monitoring installation...");
newSW.addEventListener("statechange", () => {
console.log("New SW state:", newSW.state);
if (newSW.state === "installed") {
if (navigator.serviceWorker.controller) {
// New service worker is installed, but old one is still controlling
// This means an update is available
console.log("New service worker installed - update available!");
this.updateAvailable = true;
this.onUpdateCallback?.();
} else {
// First install, no controller yet
console.log("Service worker installed for the first time");
}
}
if (newSW.state === "activated") {
console.log("New service worker activated");
// Optionally notify about successful update
}
});
});
// Also listen for controller changes
navigator.serviceWorker.addEventListener("controllerchange", () => {
console.log("Service worker controller changed - reloading page");
window.location.reload();
});
}
async checkForUpdates() {
if (this.registration) {
try {
await this.registration.update();
} catch (error) {
console.error("Failed to check for updates:", error);
}
}
}
activateUpdate() {
if (this.registration && this.registration.waiting) {
this.registration.waiting.postMessage({ type: "SKIP_WAITING" });
this.updateAvailable = false;
}
}
onUpdate(callback: () => void) {
this.onUpdateCallback = callback;
}
isUpdateAvailable() {
return this.updateAvailable;
}
async clearCache(): Promise<void> {
try {
// Delete all caches
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map(name => caches.delete(name)));
console.log("All caches cleared");
} catch (error) {
console.error("Failed to clear cache:", error);
throw error;
}
}
// Nuclear option: completely reset the PWA
async resetPWA(): Promise<void> {
try {
console.log("Resetting PWA completely...");
// 1. Unregister ALL service workers
const registrations = await navigator.serviceWorker.getRegistrations();
await Promise.all(registrations.map(reg => reg.unregister()));
// 2. Clear all caches
await this.clearCache();
// 3. Clear local storage (optional)
localStorage.clear();
sessionStorage.clear();
console.log("PWA reset complete - reloading...");
// 4. Force reload after a short delay
setTimeout(() => {
window.location.reload();
}, 500);
} catch (error) {
console.error("Failed to reset PWA:", error);
throw error;
}
}
}
export const swManager = new ServiceWorkerManager();
|