refactoring
This commit is contained in:
197
pushFlicIntegration.js
Normal file
197
pushFlicIntegration.js
Normal file
@@ -0,0 +1,197 @@
|
||||
// pushFlicIntegration.js
|
||||
import { PUBLIC_VAPID_KEY, BACKEND_URL, FLIC_BUTTON_ID, FLIC_ACTIONS } from './config.js';
|
||||
|
||||
let pushSubscription = null; // Keep track locally if needed
|
||||
let actionHandlers = {}; // Store handlers for different Flic actions
|
||||
|
||||
// --- Helper Functions ---
|
||||
|
||||
// Get stored basic auth credentials or prompt user for them
|
||||
function getBasicAuthCredentials() {
|
||||
const storedAuth = localStorage.getItem('basicAuthCredentials');
|
||||
if (storedAuth) {
|
||||
try { return JSON.parse(storedAuth); } catch (error) { console.error('Failed to parse stored credentials:', error); }
|
||||
}
|
||||
const username = prompt('Please enter your username for backend authentication:');
|
||||
if (!username) return null;
|
||||
const password = prompt('Please enter your password:');
|
||||
if (!password) return null;
|
||||
const credentials = { username, password };
|
||||
localStorage.setItem('basicAuthCredentials', JSON.stringify(credentials));
|
||||
return credentials;
|
||||
}
|
||||
|
||||
// Create Basic Auth header string
|
||||
function createBasicAuthHeader(credentials) {
|
||||
if (!credentials?.username || !credentials.password) return null;
|
||||
return 'Basic ' + btoa(`${credentials.username}:${credentials.password}`);
|
||||
}
|
||||
|
||||
// Convert URL-safe base64 string to Uint8Array
|
||||
function urlBase64ToUint8Array(base64String) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Convert ArrayBuffer to URL-safe Base64 string
|
||||
function arrayBufferToBase64(buffer) {
|
||||
let binary = '';
|
||||
const bytes = new Uint8Array(buffer);
|
||||
for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||
return window.btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
// --- Push Subscription Logic ---
|
||||
|
||||
async function subscribeToPush() {
|
||||
const buttonId = FLIC_BUTTON_ID; // Use configured button ID
|
||||
|
||||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
|
||||
console.error('Push Messaging is not supported.');
|
||||
alert('Push Notifications are not supported by your browser.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const permission = await Notification.requestPermission();
|
||||
if (permission !== 'granted') {
|
||||
console.warn('Notification permission denied.');
|
||||
alert('Please enable notifications to link the Flic button.');
|
||||
return;
|
||||
}
|
||||
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
let existingSubscription = await registration.pushManager.getSubscription();
|
||||
let needsResubscribe = !existingSubscription;
|
||||
|
||||
if (existingSubscription) {
|
||||
const existingKey = existingSubscription.options?.applicationServerKey;
|
||||
if (!existingKey || arrayBufferToBase64(existingKey) !== PUBLIC_VAPID_KEY) {
|
||||
console.log('VAPID key mismatch or missing. Unsubscribing old subscription.');
|
||||
await existingSubscription.unsubscribe();
|
||||
existingSubscription = null;
|
||||
needsResubscribe = true;
|
||||
} else {
|
||||
console.log('Existing valid subscription found.');
|
||||
pushSubscription = existingSubscription; // Store it
|
||||
}
|
||||
}
|
||||
|
||||
let finalSubscription = existingSubscription;
|
||||
if (needsResubscribe) {
|
||||
console.log('Subscribing for push notifications...');
|
||||
const applicationServerKey = urlBase64ToUint8Array(PUBLIC_VAPID_KEY);
|
||||
finalSubscription = await registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: applicationServerKey
|
||||
});
|
||||
console.log('New push subscription obtained:', finalSubscription);
|
||||
pushSubscription = finalSubscription; // Store it
|
||||
}
|
||||
|
||||
if (!finalSubscription) {
|
||||
console.error("Failed to obtain a subscription object.");
|
||||
alert("Could not get subscription details.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Send to backend
|
||||
await sendSubscriptionToServer(finalSubscription, buttonId);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during push subscription:', error);
|
||||
alert(`Subscription failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendSubscriptionToServer(subscription, buttonId) {
|
||||
console.log(`Sending subscription for button "${buttonId}" to backend...`);
|
||||
const credentials = getBasicAuthCredentials();
|
||||
if (!credentials) {
|
||||
alert('Authentication required to save button link.');
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = { 'Content-Type': 'application/json' };
|
||||
const authHeader = createBasicAuthHeader(credentials);
|
||||
if (authHeader) headers['Authorization'] = authHeader;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${BACKEND_URL}/subscribe`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ button_id: buttonId, subscription: subscription }),
|
||||
headers: headers
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
console.log('Subscription sent successfully:', result.message);
|
||||
// Maybe show a success message to the user
|
||||
} else {
|
||||
let errorMsg = `Server error: ${response.status}`;
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
localStorage.removeItem('basicAuthCredentials'); // Clear bad creds
|
||||
errorMsg = 'Authentication failed. Please try again.';
|
||||
} else {
|
||||
try { errorMsg = (await response.json()).message || errorMsg; } catch (e) { /* use default */ }
|
||||
}
|
||||
console.error('Failed to send subscription:', errorMsg);
|
||||
alert(`Failed to save link: ${errorMsg}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Network error sending subscription:', error);
|
||||
alert(`Network error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Flic Action Handling ---
|
||||
|
||||
// Called by app.js when a message is received from the service worker
|
||||
export function handleFlicAction(action, buttonId, timestamp) {
|
||||
console.log(`[PushFlic] Received Action: ${action} from Button: ${buttonId}`);
|
||||
|
||||
// Ignore actions from buttons other than the configured one
|
||||
if (buttonId !== FLIC_BUTTON_ID) {
|
||||
console.warn(`[PushFlic] Ignoring action from unknown button: ${buttonId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the registered handler for this action
|
||||
const handler = actionHandlers[action];
|
||||
if (handler && typeof handler === 'function') {
|
||||
console.log(`[PushFlic] Executing handler for ${action}`);
|
||||
handler(); // Execute the handler registered in app.js
|
||||
} else {
|
||||
console.warn(`[PushFlic] No handler registered for action: ${action}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Initialization ---
|
||||
|
||||
export function initPushFlic(handlers) {
|
||||
actionHandlers = handlers; // Store the handlers passed from app.js
|
||||
// Example: handlers = { SingleClick: handleNextPlayer, Hold: handleTogglePause }
|
||||
|
||||
// Attempt to subscribe immediately if permission might already be granted
|
||||
// Or trigger subscription on a user action (e.g., a "Link Flic Button" button)
|
||||
// For simplicity, let's try subscribing if SW is ready and permission allows
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
console.log('[PushFlic] Permission granted, attempting subscription.');
|
||||
subscribeToPush();
|
||||
} else {
|
||||
console.log('[PushFlic] Notification permission not granted.');
|
||||
// Optionally provide a button for the user to trigger subscription later
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user