diff --git a/app.js b/app.js index 26e7273..3a8af83 100644 --- a/app.js +++ b/app.js @@ -12,7 +12,7 @@ let currentX = 0; let pushSubscription = null; const PUBLIC_VAPID_KEY = 'BKfRJXjSQmAJ452gLwlK_8scGrW6qMU1mBRp39ONtcQHkSsQgmLAaODIyGbgHyRpnDEv3HfXV1oGh3SC0fHxY0E'; const BACKEND_URL = 'https://webpush.virtonline.eu'; -const BUTTON_ID = 'your_button1_serial'; +const BUTTON_ID = 'game-button'; const SINGLE_CLICK = 'SingleClick'; // DOM Elements @@ -42,6 +42,42 @@ const cameraCancelButton = document.getElementById('cameraCancelButton'); let stream = null; +// Get stored basic auth credentials or prompt user for them +function getBasicAuthCredentials() { + // Try to get stored credentials from localStorage + const storedAuth = localStorage.getItem('basicAuthCredentials'); + if (storedAuth) { + try { + return JSON.parse(storedAuth); + } catch (error) { + console.error('Failed to parse stored credentials:', error); + // Fall through to prompt + } + } + + // If no stored credentials, prompt the user + const username = prompt('Please enter your username for authentication:'); + if (!username) return null; + + const password = prompt('Please enter your password:'); + if (!password) return null; + + // Store the credentials for future use + const credentials = { username, password }; + localStorage.setItem('basicAuthCredentials', JSON.stringify(credentials)); + + return credentials; +} + +// Create Basic Auth header +function createBasicAuthHeader(credentials) { + if (!credentials || !credentials.username || !credentials.password) { + return null; + } + + return 'Basic ' + btoa(`${credentials.username}:${credentials.password}`); +} + async function subscribeToPushNotifications() { let buttonId = BUTTON_ID; // 1. Validate input buttonId @@ -125,15 +161,33 @@ async function subscribeToPushNotifications() { } console.log(`Sending subscription for button "${buttonId}" to backend...`); + + // Get basic auth credentials + const credentials = getBasicAuthCredentials(); + if (!credentials) { + console.error('Authentication credentials are required.'); + alert('Authentication failed. Please try again with valid credentials.'); + return; + } + + // Create headers with auth + const headers = { + 'Content-Type': 'application/json' + }; + + // Add Authorization header with Basic Auth if credentials are available + const authHeader = createBasicAuthHeader(credentials); + if (authHeader) { + headers['Authorization'] = authHeader; + } + const response = await fetch(`${BACKEND_URL}/subscribe`, { method: 'POST', body: JSON.stringify({ button_id: buttonId, subscription: finalSubscription }), - headers: { - 'Content-Type': 'application/json' - } + headers: headers }); // 9. Handle the server response @@ -142,6 +196,13 @@ async function subscribeToPushNotifications() { console.log('Push subscription successfully sent to server:', result.message); } else { let errorMessage = `Server error: ${response.status}`; + + // If it's an auth error, clear stored credentials and try again + if (response.status === 401 || response.status === 403) { + localStorage.removeItem('basicAuthCredentials'); + errorMessage = 'Authentication failed. Please try again with valid credentials.'; + } + try { const errorResult = await response.json(); errorMessage = errorResult.message || errorMessage; @@ -515,30 +576,23 @@ function findNextPlayerWithTimeCircular(startIndex, direction) { return -1; // No player has time left } -function handleFlicAction(action, buttonId) { - console.log(`[App] Received Flic Action: ${action} from Button: ${buttonId}`); +function handleFlicAction(action, buttonId, timestamp) { + console.log(`[App] Received Flic Action: ${action} from Button: ${buttonId} at Timestamp: ${timestamp}`); // --- Trigger your PWA action based on the 'action' string --- switch (action) { case 'SingleClick': - console.log('[App] Single click action...'); + console.log('[App] Remotely triggered single click action...'); handleSingleClickLogic(buttonId); break; case 'DoubleClick': - console.log('[App] Simulating double click action...'); - const elementForDoubleClick = document.getElementById('myDoubleClickTarget'); - if (elementForDoubleClick) { - // You might need a specific function, double clicks are harder to simulate directly - handleDoubleClickLogic(); - } else { - console.warn('[App] Element #myDoubleClickTarget not found.'); - } + console.log('[App] Remotely triggered double click action...'); + handleDoubleClickLogic(); break; case 'Hold': - console.log('[App] Simulating hold action...'); - // Example: Call a function associated with holding + console.log('[App] Remotely triggered hold action...'); handleHoldLogic(); break; @@ -554,8 +608,8 @@ if ('serviceWorker' in navigator) { // Check if the message is the one we expect if (event.data && event.data.type === 'flic-action') { - const { action, button } = event.data; - handleFlicAction(action, button); + const { action, button, timestamp } = event.data; + handleFlicAction(action, button, timestamp); } // Add else if blocks here for other message types if needed }); @@ -570,7 +624,7 @@ function handleSingleClickLogic(buttonId) { console.log(`Single Click Logic Executed from Button: ${buttonId}`); if (buttonId === BUTTON_ID) { - moveCarousel(-1); + moveCarousel(-1); // Move to next player } // Reset carousel to proper position carousel.style.transform = `translateX(${-100 * currentPlayerIndex}%)`; @@ -580,12 +634,56 @@ function handleSingleClickLogic(buttonId) { function handleDoubleClickLogic() { console.log("Double Click Logic Executed!"); - // Update UI, trigger different game action, etc. + + if (buttonId === BUTTON_ID) { + moveCarousel(1); // Move to previous player + } + // Reset carousel to proper position + carousel.style.transform = `translateX(${-100 * currentPlayerIndex}%)`; + renderPlayers(); + saveData(); } function handleHoldLogic() { console.log("Hold Logic Executed!"); - // Update UI, show a menu, etc. + // Implement game pause/resume toggle + if (players.length < 2) { + console.log('Need at least 2 players to toggle game state.'); + return; + } + + // Toggle between running and paused states + switch (gameState) { + case 'setup': + // Start the game if in setup + gameState = 'running'; + audioManager.play('gameStart'); + startTimer(); + break; + case 'running': + // Pause the game if running + gameState = 'paused'; + audioManager.play('gamePause'); + stopTimer(); + break; + case 'paused': + // Resume the game if paused + gameState = 'running'; + audioManager.play('gameResume'); + startTimer(); + break; + case 'over': + // Reset timers and start new game if game is over + players.forEach(player => { + player.remainingTime = player.timeInSeconds; + }); + gameState = 'setup'; + break; + } + + updateGameButton(); + renderPlayers(); // Make sure to re-render after state change + saveData(); } // Setup button diff --git a/sw.js b/sw.js index 0adeb5d..393264c 100644 --- a/sw.js +++ b/sw.js @@ -184,7 +184,8 @@ self.addEventListener('push', event => { const messagePayload = { type: 'flic-action', // Custom message type action: pushData.data.action, // e.g., 'SingleClick', 'DoubleClick', 'Hold' - button: pushData.data.button // e.g., the button serial + button: pushData.data.button, // e.g., the button name + timestamp: pushData.data.timestamp // e.g., the timestamp of the action }; // Send message to all open PWA windows controlled by this SW