clean up
This commit is contained in:
36
app.js
36
app.js
@@ -5,7 +5,6 @@ import * as ui from './ui.js';
|
|||||||
import * as timer from './timer.js';
|
import * as timer from './timer.js';
|
||||||
import camera from './camera.js'; // Default export
|
import camera from './camera.js'; // Default export
|
||||||
import audioManager from './audio.js';
|
import audioManager from './audio.js';
|
||||||
import deepLinkManager from './deeplinks.js';
|
|
||||||
import * as pushFlic from './pushFlicIntegration.js';
|
import * as pushFlic from './pushFlicIntegration.js';
|
||||||
|
|
||||||
// --- Core Game Actions ---
|
// --- Core Game Actions ---
|
||||||
@@ -338,15 +337,9 @@ function setupServiceWorker() {
|
|||||||
|
|
||||||
// Initialize Flic integration (which will try to subscribe)
|
// Initialize Flic integration (which will try to subscribe)
|
||||||
initFlic();
|
initFlic();
|
||||||
|
|
||||||
// Setup deep links *after* SW might be ready
|
|
||||||
setupDeepLinks();
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('ServiceWorker registration failed:', error);
|
console.error('ServiceWorker registration failed:', error);
|
||||||
// Still setup deep links even if SW fails
|
|
||||||
setupDeepLinks();
|
|
||||||
// Maybe inform user push notifications won't work
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Listen for SW controller changes
|
// Listen for SW controller changes
|
||||||
@@ -357,40 +350,15 @@ function setupServiceWorker() {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('ServiceWorker not supported.');
|
console.warn('ServiceWorker not supported.');
|
||||||
// Setup deep links anyway
|
|
||||||
window.addEventListener('load', setupDeepLinks);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleServiceWorkerMessage(event) {
|
function handleServiceWorkerMessage(event) {
|
||||||
console.log('[App] Message received from Service Worker:', event.data);
|
console.log('[App] Message received from Service Worker:', event.data);
|
||||||
if (event.data?.type === 'flic-action') {
|
if (event.data?.type === 'flic-action') {
|
||||||
const { action, button, timestamp } = event.data;
|
const { action, button, timestamp } = event.data;
|
||||||
pushFlic.handleFlicAction(action, button, timestamp);
|
pushFlic.handleFlicAction(action, button, timestamp);
|
||||||
}
|
}
|
||||||
else if (event.data?.type === 'ACTION') { // Handle deep link actions sent from SW
|
|
||||||
console.log('Received action from service worker via postMessage:', event.data.action);
|
|
||||||
deepLinkManager.handleAction(event.data.action);
|
|
||||||
}
|
|
||||||
// Add other message type handlers if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Deep Link Setup ---
|
|
||||||
function setupDeepLinks() {
|
|
||||||
deepLinkManager.registerHandler('start', startGame);
|
|
||||||
deepLinkManager.registerHandler('pause', pauseGame);
|
|
||||||
deepLinkManager.registerHandler('toggle', togglePauseResume);
|
|
||||||
deepLinkManager.registerHandler('nextplayer', nextPlayer);
|
|
||||||
deepLinkManager.registerHandler('prevplayer', previousPlayer); // Add previous player handler
|
|
||||||
deepLinkManager.registerHandler('reset', handleResetButtonClick); // Show confirmation
|
|
||||||
|
|
||||||
// Process initial deep link on load
|
|
||||||
deepLinkManager.processDeepLink();
|
|
||||||
|
|
||||||
// Listen for subsequent deep links
|
|
||||||
window.addEventListener('hashchange', deepLinkManager.processDeepLink);
|
|
||||||
// Also listen for popstate if using history API or query params
|
|
||||||
window.addEventListener('popstate', deepLinkManager.processDeepLink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Flic Integration Setup ---
|
// --- Flic Integration Setup ---
|
||||||
@@ -453,7 +421,7 @@ function initialize() {
|
|||||||
ui.elements.resetCancelButton.addEventListener('click', handleResetCancel);
|
ui.elements.resetCancelButton.addEventListener('click', handleResetCancel);
|
||||||
ui.elements.cameraButton.addEventListener('click', handleCameraButtonClick);
|
ui.elements.cameraButton.addEventListener('click', handleCameraButtonClick);
|
||||||
|
|
||||||
// 6. Setup Service Worker (which also initializes Flic and Deep Links)
|
// 6. Setup Service Worker (which also initializes Flic)
|
||||||
setupServiceWorker();
|
setupServiceWorker();
|
||||||
|
|
||||||
// 7. Initial UI Update based on loaded state
|
// 7. Initial UI Update based on loaded state
|
||||||
|
|||||||
135
deeplinks.js
135
deeplinks.js
@@ -1,135 +0,0 @@
|
|||||||
// 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;
|
|
||||||
56
index.html
56
index.html
@@ -9,14 +9,6 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
|
||||||
<!-- Deep Linking - App Links for Android -->
|
|
||||||
<link rel="alternate" href="android-app://eu.virtonline.gametimer/https://game-timer.virtonline.eu" />
|
|
||||||
|
|
||||||
<!-- Deep Linking - Universal Links for iOS -->
|
|
||||||
<meta name="apple-itunes-app" content="app-id=yourAppID, app-argument=https://game-timer.virtonline.eu">
|
|
||||||
|
|
||||||
<!-- Deep Linking - Web App URL Handling -->
|
|
||||||
<link rel="alternate" href="web+gametimer://action" />
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
@@ -108,53 +100,7 @@
|
|||||||
<button id="cameraCaptureButton" class="camera-button-large"></button>
|
<button id="cameraCaptureButton" class="camera-button-large"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Script for handling URL scheme and deep links -->
|
|
||||||
<script type="module">
|
|
||||||
// Register the custom URL protocol handler (web+gametimer://)
|
|
||||||
if ('registerProtocolHandler' in navigator) {
|
|
||||||
try {
|
|
||||||
navigator.registerProtocolHandler(
|
|
||||||
'web+gametimer',
|
|
||||||
'https://game-timer.virtonline.eu/?action=%s',
|
|
||||||
'Game Timer'
|
|
||||||
);
|
|
||||||
console.log('Protocol handler registered');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to register protocol handler:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to parse URL parameters
|
|
||||||
function getUrlParams() {
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
const hashParams = new URLSearchParams(window.location.hash.substring(1));
|
|
||||||
|
|
||||||
// Check search parameters first (for direct links)
|
|
||||||
const action = searchParams.get('action');
|
|
||||||
if (action) {
|
|
||||||
// Clean the action parameter (remove 'web+gametimer://' if present)
|
|
||||||
return action.replace('web+gametimer://', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check hash parameters (for deep links)
|
|
||||||
return hashParams.get('action');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize URL handling
|
|
||||||
function initUrlHandling() {
|
|
||||||
const action = getUrlParams();
|
|
||||||
if (action) {
|
|
||||||
console.log('URL action detected:', action);
|
|
||||||
// Set the action in the hash to be processed by the main app
|
|
||||||
window.location.hash = `action=${action}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run initialization when DOM is fully loaded
|
|
||||||
document.addEventListener('DOMContentLoaded', initUrlHandling);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Main application script -->
|
<!-- Main application script -->
|
||||||
<script type="module" src="app.js"></script>
|
<script type="module" src="app.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ async function subscribeToPush() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to backend
|
|
||||||
await sendSubscriptionToServer(finalSubscription, buttonId);
|
await sendSubscriptionToServer(finalSubscription, buttonId);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -154,7 +153,7 @@ async function sendSubscriptionToServer(subscription, buttonId) {
|
|||||||
|
|
||||||
// Called by app.js when a message is received from the service worker
|
// Called by app.js when a message is received from the service worker
|
||||||
export function handleFlicAction(action, buttonId, timestamp) {
|
export function handleFlicAction(action, buttonId, timestamp) {
|
||||||
console.log(`[PushFlic] Received Action: ${action} from Button: ${buttonId}`);
|
console.log(`[PushFlic] Received Action: ${action} from Button: ${buttonId} at ${timestamp}`);
|
||||||
|
|
||||||
// Ignore actions from buttons other than the configured one
|
// Ignore actions from buttons other than the configured one
|
||||||
if (buttonId !== FLIC_BUTTON_ID) {
|
if (buttonId !== FLIC_BUTTON_ID) {
|
||||||
@@ -166,7 +165,17 @@ export function handleFlicAction(action, buttonId, timestamp) {
|
|||||||
const handler = actionHandlers[action];
|
const handler = actionHandlers[action];
|
||||||
if (handler && typeof handler === 'function') {
|
if (handler && typeof handler === 'function') {
|
||||||
console.log(`[PushFlic] Executing handler for ${action}`);
|
console.log(`[PushFlic] Executing handler for ${action}`);
|
||||||
handler(); // Execute the handler registered in app.js
|
// Execute the handler registered in app.js
|
||||||
|
switch(action) {
|
||||||
|
case FLIC_ACTIONS.SINGLE_CLICK:
|
||||||
|
nextPlayer();
|
||||||
|
break;
|
||||||
|
case FLIC_ACTIONS.DOUBLE_CLICK:
|
||||||
|
previousPlayer();
|
||||||
|
break;
|
||||||
|
case FLIC_ACTIONS.HOLD:
|
||||||
|
togglePauseResume();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[PushFlic] No handler registered for action: ${action}`);
|
console.warn(`[PushFlic] No handler registered for action: ${action}`);
|
||||||
}
|
}
|
||||||
|
|||||||
79
sw.js
79
sw.js
@@ -8,7 +8,6 @@ const CACHE_FILES = [
|
|||||||
'/index.html',
|
'/index.html',
|
||||||
'/app.js',
|
'/app.js',
|
||||||
'/audio.js',
|
'/audio.js',
|
||||||
'/deeplinks.js',
|
|
||||||
'/styles.css',
|
'/styles.css',
|
||||||
'/manifest.json',
|
'/manifest.json',
|
||||||
'/icons/android-chrome-192x192.png',
|
'/icons/android-chrome-192x192.png',
|
||||||
@@ -18,9 +17,6 @@ const CACHE_FILES = [
|
|||||||
'/icons/favicon-16x16.png'
|
'/icons/favicon-16x16.png'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Valid deep link actions
|
|
||||||
const VALID_ACTIONS = ['start', 'pause', 'toggle', 'nextplayer', 'reset'];
|
|
||||||
|
|
||||||
// Install event - Cache files
|
// Install event - Cache files
|
||||||
self.addEventListener('install', event => {
|
self.addEventListener('install', event => {
|
||||||
console.log('[ServiceWorker] Install');
|
console.log('[ServiceWorker] Install');
|
||||||
@@ -56,57 +52,6 @@ self.addEventListener('activate', event => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch event - Serve from cache, fallback to network
|
|
||||||
self.addEventListener('fetch', event => {
|
|
||||||
console.log('[ServiceWorker] Fetch', event.request.url);
|
|
||||||
|
|
||||||
// For navigation requests that include our deep link parameters,
|
|
||||||
// skip the cache and go straight to network
|
|
||||||
if (event.request.mode === 'navigate') {
|
|
||||||
const url = new URL(event.request.url);
|
|
||||||
|
|
||||||
// Check if request has action parameter or hash
|
|
||||||
if (url.searchParams.has('action') || url.hash.includes('action=')) {
|
|
||||||
console.log('[ServiceWorker] Processing deep link navigation');
|
|
||||||
|
|
||||||
// Verify the action is valid
|
|
||||||
const action = url.searchParams.get('action') ||
|
|
||||||
new URLSearchParams(url.hash.substring(1)).get('action');
|
|
||||||
|
|
||||||
if (action && VALID_ACTIONS.includes(action)) {
|
|
||||||
console.log('[ServiceWorker] Valid action found:', action);
|
|
||||||
|
|
||||||
// For navigation requests with valid actions, let the request go through
|
|
||||||
// so our app can handle the deep link
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event.respondWith(
|
|
||||||
caches.match(event.request)
|
|
||||||
.then(response => {
|
|
||||||
return response || fetch(event.request)
|
|
||||||
.then(res => {
|
|
||||||
// Check if we should cache this response
|
|
||||||
if (shouldCacheResponse(event.request, res)) {
|
|
||||||
return caches.open(CACHE_NAME)
|
|
||||||
.then(cache => {
|
|
||||||
console.log('[ServiceWorker] Caching new resource:', event.request.url);
|
|
||||||
cache.put(event.request, res.clone());
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log('[ServiceWorker] Fetch failed; returning offline page', error);
|
|
||||||
// You could return a custom offline page here
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper function to determine if a response should be cached
|
// Helper function to determine if a response should be cached
|
||||||
function shouldCacheResponse(request, response) {
|
function shouldCacheResponse(request, response) {
|
||||||
// Only cache GET requests
|
// Only cache GET requests
|
||||||
@@ -124,30 +69,6 @@ function shouldCacheResponse(request, response) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle deep links from other apps (including Flic)
|
|
||||||
self.addEventListener('message', event => {
|
|
||||||
if (event.data && event.data.type === 'PROCESS_ACTION') {
|
|
||||||
const action = event.data.action;
|
|
||||||
console.log('[ServiceWorker] Received action message:', action);
|
|
||||||
|
|
||||||
// Validate the action
|
|
||||||
if (!VALID_ACTIONS.includes(action)) {
|
|
||||||
console.warn('[ServiceWorker] Invalid action received:', action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Broadcast the action to all clients
|
|
||||||
self.clients.matchAll().then(clients => {
|
|
||||||
clients.forEach(client => {
|
|
||||||
client.postMessage({
|
|
||||||
type: 'ACTION',
|
|
||||||
action: action
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener('push', event => {
|
self.addEventListener('push', event => {
|
||||||
console.log('[ServiceWorker] Push received');
|
console.log('[ServiceWorker] Push received');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user