diff --git a/apps.js b/app.js
similarity index 87%
rename from apps.js
rename to app.js
index b9d429c..0b23594 100644
--- a/apps.js
+++ b/app.js
@@ -1,5 +1,6 @@
// Import the audio manager
import audioManager from './audio.js';
+import deepLinkManager from './deeplinks.js';
// Initialize variables
let players = [];
@@ -545,7 +546,7 @@ function stopCameraStream() {
}
}
-// Player form submit - FIXED VERSION
+// Player form submit
playerForm.addEventListener('submit', (e) => {
e.preventDefault();
@@ -677,154 +678,66 @@ deletePlayerButton.addEventListener('click', () => {
audioManager.play('modalClose');
});
-// Function to get action from URL parameters (search or hash)
-function getActionFromUrl() {
- // Check for action in both search params and hash
- const searchParams = new URLSearchParams(window.location.search);
- const hashParams = new URLSearchParams(window.location.hash.substring(1));
-
- // First check search params (for direct curl or navigation)
- const searchAction = searchParams.get('action');
- if (searchAction) {
- console.log('Found action in search params:', searchAction);
- return searchAction;
- }
-
- // Then check hash params (existing deep link mechanism)
- const hashAction = hashParams.get('action');
- if (hashAction) {
- console.log('Found action in hash params:', hashAction);
- return hashAction;
- }
-
- return null;
+// Function to create deep links
+function createDeepLink(action) {
+ return deepLinkManager.generateDeepLink(action);
}
-// Function to handle deep link actions
-function handleActionFromUrl(action) {
- if (!action) return;
-
- console.log('Processing action:', action);
-
- // Execute action based on the parameter
- switch (action) {
- case 'start':
- if (gameState === 'setup' || gameState === 'paused') {
- if (players.length < 2) {
- console.log('Cannot start: Need at least 2 players');
- return;
- }
- gameState = 'running';
- audioManager.play('gameStart');
- startTimer();
- updateGameButton();
- renderPlayers();
- saveData();
+// Function to setup deep links
+function setupDeepLinks() {
+ // Register handlers for each action
+ deepLinkManager.registerHandler('start', () => {
+ if (gameState === 'setup' || gameState === 'paused') {
+ if (players.length < 2) {
+ console.log('Cannot start: Need at least 2 players');
+ return;
}
- break;
- case 'pause':
- if (gameState === 'running') {
- gameState = 'paused';
- audioManager.play('gamePause');
- stopTimer();
- updateGameButton();
- renderPlayers();
- saveData();
- }
- break;
- case 'toggle':
- // Toggle between start/pause depending on current state
- gameButton.click();
- break;
- case 'nextplayer':
- if (gameState === 'running') {
- const nextIndex = findNextPlayerWithTimeCircular(currentPlayerIndex, 1);
- if (nextIndex !== -1 && nextIndex !== currentPlayerIndex) {
- currentPlayerIndex = nextIndex;
- audioManager.play('playerSwitch');
- renderPlayers();
- saveData();
- }
- }
- break;
- default:
- console.log('Unknown action:', action);
- }
-}
-
-// Function to handle deep links from URL or Flic button
-function handleDeepLink() {
- // Get action from URL parameters
- const action = getActionFromUrl();
-
- // Process the action if found
- if (action) {
- handleActionFromUrl(action);
-
- // Clear the parameters to prevent duplicate actions if page is refreshed
- if (window.history && window.history.replaceState) {
- // Create new URL without the action parameter
- const newUrl = window.location.pathname;
- window.history.replaceState({}, document.title, newUrl);
+ gameState = 'running';
+ audioManager.play('gameStart');
+ startTimer();
+ updateGameButton();
+ renderPlayers();
+ saveData();
}
- }
-}
-
-// Listen for service worker messages (for receiving actions while app is running)
-navigator.serviceWorker.addEventListener('message', (event) => {
- if (event.data && event.data.type === 'ACTION') {
- console.log('Received action from service worker:', event.data.action);
- handleActionFromUrl(event.data.action);
- }
-});
-
-// Service Worker Registration
-if ('serviceWorker' in navigator) {
- window.addEventListener('load', () => {
- navigator.serviceWorker.register('/sw.js')
- .then(registration => {
- console.log('ServiceWorker registered: ', registration);
-
- // Check for and handle deep links after service worker is ready
- handleDeepLink();
- })
- .catch(error => {
- console.log('ServiceWorker registration failed: ', error);
-
- // Still try to handle deep links even if service worker failed
- handleDeepLink();
- });
});
-} else {
- // If service workers aren't supported, still handle deep links
- window.addEventListener('load', handleDeepLink);
+
+ deepLinkManager.registerHandler('pause', () => {
+ if (gameState === 'running') {
+ gameState = 'paused';
+ audioManager.play('gamePause');
+ stopTimer();
+ updateGameButton();
+ renderPlayers();
+ saveData();
+ }
+ });
+
+ deepLinkManager.registerHandler('toggle', () => {
+ // Simply trigger the game button click
+ gameButton.click();
+ });
+
+ deepLinkManager.registerHandler('nextplayer', () => {
+ if (gameState === 'running') {
+ const nextIndex = findNextPlayerWithTimeCircular(currentPlayerIndex, 1);
+ if (nextIndex !== -1 && nextIndex !== currentPlayerIndex) {
+ currentPlayerIndex = nextIndex;
+ audioManager.play('playerSwitch');
+ renderPlayers();
+ saveData();
+ }
+ }
+ });
+
+ deepLinkManager.registerHandler('reset', () => {
+ // Show the reset confirmation dialog
+ resetButton.click();
+ });
+
+ // Process deep links on page load
+ deepLinkManager.processDeepLink();
}
-// Also check for hash changes (needed for handling link activation when app is already open)
-window.addEventListener('hashchange', () => {
- console.log('Hash changed, checking for actions');
- handleDeepLink();
-});
-
-// Also check for navigation events that might include search parameters
-window.addEventListener('popstate', () => {
- console.log('Navigation occurred, checking for actions');
- handleDeepLink();
-});
-
-// Make sure to handle rotation by adding window event listener for orientation changes
-window.addEventListener('orientationchange', () => {
- // If camera is active, adjust video dimensions
- if (cameraContainer.classList.contains('active') && stream) {
- // Give a moment for the orientation to complete
- setTimeout(() => {
- // This may cause the video to briefly reset but will ensure proper dimensions
- cameraView.srcObject = null;
- cameraView.srcObject = stream;
- }, 300);
- }
-});
-
// Clean up when the modal is closed
function cleanupCameraData() {
// Clear any captured image data
@@ -839,12 +752,60 @@ function cleanupCameraData() {
cameraContainer.classList.remove('active');
}
+// Make sure to handle rotation by adding window event listener for orientation changes
+window.addEventListener('orientationchange', () => {
+ // If camera is active, adjust video dimensions
+ if (cameraContainer.classList.contains('active') && stream) {
+ // Give a moment for the orientation to complete
+ setTimeout(() => {
+ // This may cause the video to briefly reset but will ensure proper dimensions
+ cameraView.srcObject = null;
+ cameraView.srcObject = stream;
+ }, 300);
+ }
+});
+
+// Listen for service worker messages (for receiving actions while app is running)
+navigator.serviceWorker.addEventListener('message', (event) => {
+ if (event.data && event.data.type === 'ACTION') {
+ console.log('Received action from service worker:', event.data.action);
+ deepLinkManager.handleAction(event.data.action);
+ }
+});
+
+// Service Worker Registration
+if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/sw.js')
+ .then(registration => {
+ console.log('ServiceWorker registered: ', registration);
+
+ // Setup and handle deep links after service worker is ready
+ setupDeepLinks();
+ })
+ .catch(error => {
+ console.log('ServiceWorker registration failed: ', error);
+
+ // Still try to handle deep links even if service worker failed
+ setupDeepLinks();
+ });
+ });
+} else {
+ // If service workers aren't supported, still handle deep links
+ window.addEventListener('load', setupDeepLinks);
+}
+
+// Also check for hash changes (needed for handling link activation when app is already open)
+window.addEventListener('hashchange', () => {
+ console.log('Hash changed, checking for actions');
+ deepLinkManager.processDeepLink();
+});
+
+// Also check for navigation events that might include search parameters
+window.addEventListener('popstate', () => {
+ console.log('Navigation occurred, checking for actions');
+ deepLinkManager.processDeepLink();
+});
+
// Initialize the app
loadData();
-
-// Process URL parameters on initial load (needed for direct curl requests)
-if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', handleDeepLink);
-} else {
- handleDeepLink();
-}
\ No newline at end of file
diff --git a/deeplinks.js b/deeplinks.js
new file mode 100644
index 0000000..374f0f6
--- /dev/null
+++ b/deeplinks.js
@@ -0,0 +1,135 @@
+// deeplinks.js - Deep Link Manager for Game Timer
+
+// Available actions
+const VALID_ACTIONS = ['start', 'pause', 'toggle', 'nextplayer', 'reset'];
+
+// Class to manage all deep link functionality
+class DeepLinkManager {
+ constructor() {
+ this.actionHandlers = {};
+
+ // Initialize listeners
+ this.initServiceWorkerListener();
+ this.initHashChangeListener();
+ this.initPopStateListener();
+ }
+
+ // Register action handlers
+ registerHandler(action, handlerFn) {
+ if (VALID_ACTIONS.includes(action)) {
+ this.actionHandlers[action] = handlerFn;
+ console.log(`Registered handler for action: ${action}`);
+ } else {
+ console.warn(`Attempted to register handler for invalid action: ${action}`);
+ }
+ }
+
+ // Extract action from URL parameters (search or hash)
+ getActionFromUrl() {
+ // Check for action in both search params and hash
+ const searchParams = new URLSearchParams(window.location.search);
+ const hashParams = new URLSearchParams(window.location.hash.substring(1));
+
+ // First check search params (for direct curl or navigation)
+ const searchAction = searchParams.get('action');
+ if (searchAction && VALID_ACTIONS.includes(searchAction)) {
+ console.log('Found action in search params:', searchAction);
+ return searchAction;
+ }
+
+ // Then check hash params (existing deep link mechanism)
+ const hashAction = hashParams.get('action');
+ if (hashAction && VALID_ACTIONS.includes(hashAction)) {
+ console.log('Found action in hash params:', hashAction);
+ return hashAction;
+ }
+
+ return null;
+ }
+
+ // Process an action
+ handleAction(action) {
+ if (!action) return;
+
+ console.log('Processing action:', action);
+
+ if (this.actionHandlers[action]) {
+ this.actionHandlers[action]();
+ } else {
+ console.log('No handler registered for action:', action);
+ }
+ }
+
+ // Handle deep links from URL
+ processDeepLink() {
+ // Get action from URL parameters
+ const action = this.getActionFromUrl();
+
+ // Process the action if found
+ if (action) {
+ this.handleAction(action);
+
+ // Clear the parameters to prevent duplicate actions if page is refreshed
+ if (window.history && window.history.replaceState) {
+ // Create new URL without the action parameter
+ const newUrl = window.location.pathname;
+ window.history.replaceState({}, document.title, newUrl);
+ }
+ }
+ }
+
+ // Initialize service worker message listener
+ initServiceWorkerListener() {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.addEventListener('message', (event) => {
+ if (event.data && event.data.type === 'ACTION') {
+ console.log('Received action from service worker:', event.data.action);
+ this.handleAction(event.data.action);
+ }
+ });
+ }
+ }
+
+ // Initialize hash change listener
+ initHashChangeListener() {
+ window.addEventListener('hashchange', () => {
+ console.log('Hash changed, checking for actions');
+ this.processDeepLink();
+ });
+ }
+
+ // Initialize popstate listener for navigation events
+ initPopStateListener() {
+ window.addEventListener('popstate', () => {
+ console.log('Navigation occurred, checking for actions');
+ this.processDeepLink();
+ });
+ }
+
+ // Send an action to the service worker
+ sendActionToServiceWorker(action) {
+ if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
+ navigator.serviceWorker.controller.postMessage({
+ type: 'PROCESS_ACTION',
+ action: action
+ });
+ }
+ }
+
+ // Generate a deep link URL for a specific action
+ generateDeepLink(action, useHash = false) {
+ if (!VALID_ACTIONS.includes(action)) {
+ console.warn(`Cannot generate deep link for invalid action: ${action}`);
+ return null;
+ }
+
+ const baseUrl = window.location.origin + window.location.pathname;
+ return useHash ?
+ `${baseUrl}#action=${action}` :
+ `${baseUrl}?action=${action}`;
+ }
+}
+
+// Export singleton instance
+const deepLinkManager = new DeepLinkManager();
+export default deepLinkManager;
\ No newline at end of file
diff --git a/index.html b/index.html
index 891ebbe..0d76fbf 100644
--- a/index.html
+++ b/index.html
@@ -156,8 +156,7 @@
-
-
+