From bade9c0a150e9241e02b5e1a3aed7cad30e3dca0 Mon Sep 17 00:00:00 2001 From: cpu Date: Wed, 26 Mar 2025 21:15:47 +0100 Subject: [PATCH] added unsubscribe --- app.js | 164 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 135 insertions(+), 29 deletions(-) diff --git a/app.js b/app.js index ab5d819..5a107bd 100644 --- a/app.js +++ b/app.js @@ -42,38 +42,144 @@ const cameraCancelButton = document.getElementById('cameraCancelButton'); let stream = null; async function subscribeToPushNotifications() { - if ('serviceWorker' in navigator && 'PushManager' in window) { - try { - const registration = await navigator.serviceWorker.ready; - const subscription = await registration.pushManager.subscribe({ - userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY) - }); - - // Send subscription to your server - await fetch(`${BACKEND_URL}/subscribe`, { - method: 'POST', - body: JSON.stringify({ - button_id: BACKEND_URL, - subscription: subscription // The PushSubscription object - }), - headers: { - 'Content-Type': 'application/json' - }, - }); - - pushSubscription = subscription; - console.log('Push subscription successful'); - } catch (error) { - console.error('Error subscribing to push:', error); - } + let buttonId = BUTTON_ID; + // 1. Validate input buttonId + if (!buttonId || typeof buttonId !== 'string' || buttonId.trim() === '') { + console.error('Button ID is required to subscribe.'); + alert('Please provide a valid Flic Button ID/Serial.'); + return; } - } + + // 2. Check for browser support + if (!('serviceWorker' in navigator) || !('PushManager' in window)) { + console.error('Push Messaging is not supported'); + alert('Sorry, Push Notifications are not supported by your browser.'); + return; + } + + try { + // 3. Request notification permission (requires user interaction) + const permission = await Notification.requestPermission(); + if (permission !== 'granted') { + console.error('Notification permission not granted.'); + alert('You denied notification permission. Please enable it in browser settings if you want to link the button.'); + return; + } + console.log('Notification permission granted.'); + + // 4. Get Service Worker registration + const registration = await navigator.serviceWorker.ready; + console.log('Service Worker is ready.'); + + // 5. Get existing subscription + let existingSubscription = await registration.pushManager.getSubscription(); + let needsResubscribe = false; + + if (existingSubscription) { + console.log('Existing subscription found.'); + + // 6. Compare applicationServerKeys + const existingKeyArrayBuffer = existingSubscription.options.applicationServerKey; + if (!existingKeyArrayBuffer) { + console.warn("Existing subscription doesn't have an applicationServerKey."); + needsResubscribe = true; // Treat as needing resubscription + } else { + const existingKeyBase64 = arrayBufferToBase64(existingKeyArrayBuffer); + console.log('Existing VAPID Key (Base64):', existingKeyBase64); + console.log('Current VAPID Key (Base64): ', PUBLIC_VAPID_KEY); + + if (existingKeyBase64 !== PUBLIC_VAPID_KEY) { + console.log('VAPID keys DO NOT match. Unsubscribing the old one.'); + await existingSubscription.unsubscribe(); + console.log('Successfully unsubscribed old subscription.'); + existingSubscription = null; // Clear it so we subscribe anew below + needsResubscribe = true; // Explicitly flag for clarity + } else { + console.log('VAPID keys match. No need to resubscribe.'); + needsResubscribe = false; + } + } + } else { + console.log('No existing subscription found.'); + needsResubscribe = true; // No subscription exists, so we need one + } + + // 7. Subscribe if needed (no existing sub or keys mismatched) + let finalSubscription = existingSubscription; // Use existing if keys matched + if (needsResubscribe || !finalSubscription) { + console.log('Attempting to subscribe with current VAPID key...'); + const applicationServerKey = urlBase64ToUint8Array(PUBLIC_VAPID_KEY); + finalSubscription = await registration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: applicationServerKey + }); + console.log('New push subscription obtained:', finalSubscription); + } + + // 8. Send the final subscription (new or validated existing) to your server + if (!finalSubscription) { + console.error("Failed to obtain a final subscription object."); + alert("Could not get subscription details. Please try again."); + return; + } + + console.log(`Sending subscription for button "${buttonId}" to backend...`); + const response = await fetch(`${BACKEND_URL}/subscribe`, { + method: 'POST', + body: JSON.stringify({ + button_id: buttonId, + subscription: finalSubscription + }), + headers: { + 'Content-Type': 'application/json' + } + }); + + // 9. Handle the server response + if (response.ok) { + const result = await response.json(); + console.log('Push subscription successfully sent to server:', result.message); + } else { + let errorMessage = `Server error: ${response.status}`; + try { + const errorResult = await response.json(); + errorMessage = errorResult.message || errorMessage; + } catch (e) { + errorMessage = response.statusText || errorMessage; + } + console.error('Failed to send push subscription to server:', errorMessage); + alert(`Failed to save notification settings on server: ${errorMessage}`); + } + + } catch (error) { + console.error('Error during push subscription process:', error); + // Handle potential errors from permission request, SW registration, get/unsubscribe/subscribe, or fetch network issues + if (error.name === 'InvalidStateError') { + alert(`Subscription failed: ${error.message}. Please try again or ensure no conflicting subscriptions exist.`); + } else { + alert(`An error occurred: ${error.message}`); + } + } +} + +// Helper to convert ArrayBuffer to Base64 string (URL safe) +function arrayBufferToBase64(buffer) { + let binary = ''; + const bytes = new Uint8Array(buffer); + const len = bytes.byteLength; + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return window.btoa(binary) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+$/, ''); // Remove padding +} - function urlBase64ToUint8Array(base64String) { +function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding) - .replace(/-/g, '+') + .replace(/\-/g, '+') .replace(/_/g, '/'); const rawData = window.atob(base64); @@ -83,7 +189,7 @@ async function subscribeToPushNotifications() { outputArray[i] = rawData.charCodeAt(i); } return outputArray; - } +} // Add sound toggle button const createSoundToggleButton = () => {