Re_Figma_Code/src/utils/pushNotifications.ts

152 lines
5.0 KiB
TypeScript

const VAPID_PUBLIC_KEY = import.meta.env.VITE_PUBLIC_VAPID_KEY as string;
const VITE_BASE_URL = import.meta.env.VITE_BASE_URL as string;
function urlBase64ToUint8Array(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
export async function registerServiceWorker() {
if (!('serviceWorker' in navigator)) {
throw new Error('Service workers are not supported in this browser');
}
let registration: ServiceWorkerRegistration;
try {
// Check if service worker is already registered
const existingRegistration = await navigator.serviceWorker.getRegistration('/service-worker.js');
if (existingRegistration) {
// Service worker already registered, wait for it to be ready
registration = await navigator.serviceWorker.ready;
} else {
// Register new service worker
registration = await navigator.serviceWorker.register('/service-worker.js');
// Wait for the service worker to be ready (may take a moment)
registration = await navigator.serviceWorker.ready;
}
} catch (error: any) {
throw new Error(`Failed to register service worker: ${error?.message || 'Unknown error'}`);
}
return registration;
}
export async function subscribeUserToPush(register: ServiceWorkerRegistration) {
if (!VAPID_PUBLIC_KEY) {
throw new Error('Missing VAPID public key configuration');
}
// Check if already subscribed
let subscription: PushSubscription;
try {
const existingSubscription = await register.pushManager.getSubscription();
if (existingSubscription) {
// Already subscribed, check if it's still valid
subscription = existingSubscription;
} else {
// Subscribe to push
subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
}
} catch (error: any) {
throw new Error(`Failed to subscribe to push notifications: ${error?.message || 'Unknown error'}`);
}
// Convert subscription to JSON format for backend
const subscriptionJson = subscription.toJSON();
// Attach auth token if available
const token = (window as any)?.localStorage?.getItem?.('accessToken') ||
(document?.cookie || '').match(/accessToken=([^;]+)/)?.[1] || '';
if (!token) {
throw new Error('Authentication token not found. Please log in again.');
}
// Send subscription to backend
try {
const response = await fetch(`${VITE_BASE_URL}/api/v1/workflows/notifications/subscribe`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(subscriptionJson)
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(errorData?.error || errorData?.message || `Server error: ${response.status}`);
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Failed to save subscription');
}
} catch (error: any) {
// If it's already our error, rethrow it
if (error instanceof Error && error.message.includes('Failed')) {
throw error;
}
throw new Error(`Failed to save subscription to server: ${error?.message || 'Network error'}`);
}
return subscription;
}
export async function setupPushNotifications() {
// Check if notifications are supported
if (!('Notification' in window)) {
throw new Error('Notifications are not supported in this browser');
}
// Check permission status
let permission = Notification.permission;
if (permission === 'denied') {
throw new Error('Notification permission was denied. Please enable notifications in your browser settings and try again.');
}
if (permission === 'default') {
// Request permission if not already requested
permission = await Notification.requestPermission();
if (permission !== 'granted') {
throw new Error('Notification permission was denied. Please enable notifications in your browser settings and try again.');
}
}
// Final check - permission should be 'granted' at this point
if (permission !== 'granted') {
throw new Error('Notification permission is required. Please grant permission and try again.');
}
// Register service worker (or get existing)
let reg: ServiceWorkerRegistration;
try {
reg = await registerServiceWorker();
} catch (error: any) {
throw new Error(`Service worker registration failed: ${error?.message || 'Unknown error'}`);
}
// Subscribe to push
try {
await subscribeUserToPush(reg);
} catch (error: any) {
throw error; // Re-throw with detailed message
}
}