Files
game-timer/deeplinks.js
2025-03-24 01:52:53 +01:00

135 lines
4.6 KiB
JavaScript

// 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;