push setup

This commit is contained in:
cpu
2025-03-29 06:01:10 +01:00
parent 2d8c1a6fff
commit 5ebd2cf005
4 changed files with 376 additions and 4 deletions

View File

@@ -7,6 +7,7 @@ import camera from './ui/camera.js'; // Default export
import audioManager from './ui/audio.js';
import * as pushFlic from './services/pushFlicIntegration.js';
import { initEnv } from './env-loader.js';
import * as pushSettingsUI from './ui/pushSettingsUI.js'; // Import the new push settings UI module
// Import externalized modules
import * as gameActions from './core/gameActions.js';
@@ -70,7 +71,10 @@ async function initialize() {
ui.elements.resetCancelButton.addEventListener('click', eventHandlers.handleResetCancel);
ui.elements.cameraButton.addEventListener('click', eventHandlers.handleCameraButtonClick);
// 6. Setup Flic action handlers
// 6. Initialize Push Notification Settings UI
pushSettingsUI.initPushSettingsUI();
// 7. Setup Flic action handlers
const flicActionHandlers = {
[config.FLIC_ACTIONS.SINGLE_CLICK]: playerManager.nextPlayer,
[config.FLIC_ACTIONS.DOUBLE_CLICK]: playerManager.previousPlayer,
@@ -78,14 +82,14 @@ async function initialize() {
};
serviceWorkerManager.setFlicActionHandlers(flicActionHandlers);
// 7. Setup Service Worker (which also initializes Flic)
// 8. Setup Service Worker (which also initializes Flic)
serviceWorkerManager.setupServiceWorker(serviceWorkerManager.handleServiceWorkerMessage);
// 8. Initial UI Update based on loaded state
// 9. Initial UI Update based on loaded state
ui.renderPlayers();
ui.updateGameButton();
// 9. Reset running state to paused on load
// 10. Reset running state to paused on load
if (state.getGameState() === config.GAME_STATES.RUNNING) {
console.log("Game was running on load, setting to paused.");
state.setGameState(config.GAME_STATES.PAUSED);

280
src/js/ui/pushSettingsUI.js Normal file
View File

@@ -0,0 +1,280 @@
// pushSettingsUI.js - UI handling for push notification settings
import { setupPushNotifications, forceCredentialsPrompt } from '../services/serviceWorkerManager.js';
import { FLIC_BUTTON_ID } from '../config.js';
// --- DOM Elements ---
const elements = {
pushSettingsButton: null,
pushSettingsModal: null,
notificationPermissionStatus: null,
subscriptionStatus: null,
pushUsername: null,
pushPassword: null,
pushSaveButton: null,
pushCancelButton: null,
pushUnsubscribeButton: null,
pushResubscribeButton: null
};
// --- State ---
let currentSubscription = null;
// --- Initialization ---
export function initPushSettingsUI() {
// Cache DOM elements
elements.pushSettingsButton = document.getElementById('pushSettingsButton');
elements.pushSettingsModal = document.getElementById('pushSettingsModal');
elements.notificationPermissionStatus = document.getElementById('notificationPermissionStatus');
elements.subscriptionStatus = document.getElementById('subscriptionStatus');
elements.pushUsername = document.getElementById('pushUsername');
elements.pushPassword = document.getElementById('pushPassword');
elements.pushSaveButton = document.getElementById('pushSaveButton');
elements.pushCancelButton = document.getElementById('pushCancelButton');
elements.pushUnsubscribeButton = document.getElementById('pushUnsubscribeButton');
elements.pushResubscribeButton = document.getElementById('pushResubscribeButton');
// Set up event listeners
elements.pushSettingsButton.addEventListener('click', openPushSettingsModal);
elements.pushCancelButton.addEventListener('click', closePushSettingsModal);
elements.pushSaveButton.addEventListener('click', saveCredentialsAndSubscribe);
elements.pushUnsubscribeButton.addEventListener('click', unsubscribeFromPush);
elements.pushResubscribeButton.addEventListener('click', resubscribeToPush);
// Initial status check
updateNotificationStatus();
}
// --- UI Functions ---
// Open the push settings modal and update statuses
function openPushSettingsModal() {
// Update status displays
updateNotificationStatus();
updateSubscriptionStatus();
// Load saved credentials if available
loadSavedCredentials();
// Show the modal
elements.pushSettingsModal.classList.add('active');
}
// Close the push settings modal
function closePushSettingsModal() {
elements.pushSettingsModal.classList.remove('active');
}
// Update the notification permission status display
function updateNotificationStatus() {
if (!('Notification' in window)) {
elements.notificationPermissionStatus.textContent = 'Not Supported';
elements.notificationPermissionStatus.className = 'status-denied';
return;
}
const permission = Notification.permission;
elements.notificationPermissionStatus.textContent = permission;
switch (permission) {
case 'granted':
elements.notificationPermissionStatus.className = 'status-granted';
break;
case 'denied':
elements.notificationPermissionStatus.className = 'status-denied';
break;
default:
elements.notificationPermissionStatus.className = 'status-default';
}
}
// Update the subscription status display
async function updateSubscriptionStatus() {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
elements.subscriptionStatus.textContent = 'Not Supported';
elements.subscriptionStatus.className = 'status-denied';
return;
}
try {
const registration = await navigator.serviceWorker.ready;
currentSubscription = await registration.pushManager.getSubscription();
if (currentSubscription) {
elements.subscriptionStatus.textContent = 'Active';
elements.subscriptionStatus.className = 'status-active';
} else {
elements.subscriptionStatus.textContent = 'Not Subscribed';
elements.subscriptionStatus.className = 'status-inactive';
}
} catch (error) {
console.error('Error checking subscription status:', error);
elements.subscriptionStatus.textContent = 'Error';
elements.subscriptionStatus.className = 'status-denied';
}
}
// Load saved credentials from localStorage
function loadSavedCredentials() {
try {
const storedAuth = localStorage.getItem('basicAuthCredentials');
if (storedAuth) {
const credentials = JSON.parse(storedAuth);
if (credentials.username && credentials.password) {
elements.pushUsername.value = credentials.username;
elements.pushPassword.value = credentials.password;
}
}
} catch (error) {
console.error('Error loading saved credentials:', error);
}
}
// --- Action Functions ---
// Save credentials and subscribe to push notifications
async function saveCredentialsAndSubscribe() {
const username = elements.pushUsername.value.trim();
const password = elements.pushPassword.value.trim();
if (!username || !password) {
alert('Please enter both username and password');
return;
}
// Save credentials to localStorage
const credentials = { username, password };
localStorage.setItem('basicAuthCredentials', JSON.stringify(credentials));
// Request notification permission if needed
if (Notification.permission !== 'granted') {
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
alert('Notification permission is required for push notifications');
updateNotificationStatus();
return;
}
}
// Subscribe using the service worker
try {
await setupPushNotifications();
await updateSubscriptionStatus();
alert('Subscription successful!');
} catch (error) {
console.error('Subscription error:', error);
alert(`Error subscribing: ${error.message}`);
}
// Close the modal
closePushSettingsModal();
}
// Unsubscribe from push notifications
async function unsubscribeFromPush() {
if (!currentSubscription) {
alert('No active subscription to unsubscribe from');
return;
}
try {
await currentSubscription.unsubscribe();
await updateSubscriptionStatus();
alert('Successfully unsubscribed from push notifications');
} catch (error) {
console.error('Error unsubscribing:', error);
alert(`Error unsubscribing: ${error.message}`);
}
}
// Force resubscription to push notifications
async function resubscribeToPush() {
try {
// Force credential prompt and resubscribe
await forceCredentialsPrompt();
await updateSubscriptionStatus();
} catch (error) {
console.error('Error resubscribing:', error);
alert(`Error resubscribing: ${error.message}`);
}
}
// Manually trigger sendSubscriptionToServer with the current subscription
export async function sendSubscriptionToServer() {
if (!currentSubscription) {
await updateSubscriptionStatus();
if (!currentSubscription) {
alert('No active subscription available. Please subscribe first.');
return;
}
}
// Get stored credentials
let credentials;
try {
const storedAuth = localStorage.getItem('basicAuthCredentials');
if (storedAuth) {
credentials = JSON.parse(storedAuth);
if (!credentials.username || !credentials.password) {
throw new Error('Invalid credentials');
}
} else {
throw new Error('No stored credentials');
}
} catch (error) {
alert('No valid credentials found. Please set them up first.');
openPushSettingsModal();
return;
}
// Create Basic Auth header
const createBasicAuthHeader = (creds) => {
return 'Basic ' + btoa(`${creds.username}:${creds.password}`);
};
const headers = {
'Content-Type': 'application/json',
'Authorization': createBasicAuthHeader(credentials)
};
// Import the backend URL from config
let backendUrl;
try {
const configModule = await import('../config.js');
backendUrl = configModule.BACKEND_URL;
} catch (error) {
alert('Could not get backend URL from config.');
return;
}
try {
// Make the request to the server
const response = await fetch(`${backendUrl}/subscribe`, {
method: 'POST',
body: JSON.stringify({
button_id: FLIC_BUTTON_ID,
subscription: currentSubscription
}),
headers: headers,
credentials: 'include'
});
if (response.ok) {
const result = await response.json();
alert(`Subscription sent successfully: ${result.message || 'OK'}`);
} else {
let errorMsg = `Server error: ${response.status}`;
if (response.status === 401 || response.status === 403) {
localStorage.removeItem('basicAuthCredentials');
errorMsg = 'Authentication failed. Credentials cleared.';
} else {
try {
const errorData = await response.json();
errorMsg = errorData.message || errorMsg;
} catch (e) { /* use default */ }
}
alert(`Failed to send subscription: ${errorMsg}`);
}
} catch (error) {
alert(`Network error: ${error.message}`);
}
}