push notifications
This commit is contained in:
95
app.js
95
app.js
@@ -9,6 +9,8 @@ let gameState = 'setup'; // setup, running, paused, over
|
||||
let carouselPosition = 0;
|
||||
let startX = 0;
|
||||
let currentX = 0;
|
||||
let pushSubscription = null;
|
||||
const PUBLIC_VAPID_KEY = 'BNIXGVBzq6SNqvlDMFylw_hLTpf_J96ddbwfMa9Cn1tFQ1-vDqmz_NQS0a5UiczqAJ-uYs-6EeuwrnwaMRGtifk=';
|
||||
|
||||
// DOM Elements
|
||||
const carousel = document.getElementById('carousel');
|
||||
@@ -37,6 +39,47 @@ const cameraCancelButton = document.getElementById('cameraCancelButton');
|
||||
|
||||
let stream = null;
|
||||
|
||||
async function subscribeToPushNotifications() {
|
||||
if ('serviceWorker' in navigator && 'PushManager' in window) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
const subscription = await registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY)
|
||||
});
|
||||
|
||||
// Send subscription to your server
|
||||
await fetch('https://webpush.virtonline.eu/subscribe', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(subscription),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
pushSubscription = subscription;
|
||||
console.log('Push subscription successful');
|
||||
} catch (error) {
|
||||
console.error('Error subscribing to push:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function urlBase64ToUint8Array(base64String) {
|
||||
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
||||
const base64 = (base64String + padding)
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
|
||||
const rawData = window.atob(base64);
|
||||
const outputArray = new Uint8Array(rawData.length);
|
||||
|
||||
for (let i = 0; i < rawData.length; ++i) {
|
||||
outputArray[i] = rawData.charCodeAt(i);
|
||||
}
|
||||
return outputArray;
|
||||
}
|
||||
|
||||
// Add sound toggle button
|
||||
const createSoundToggleButton = () => {
|
||||
const headerButtons = document.querySelector('.header-buttons');
|
||||
@@ -177,11 +220,13 @@ gameButton.addEventListener('click', () => {
|
||||
gameState = 'paused';
|
||||
audioManager.play('gamePause');
|
||||
stopTimer();
|
||||
// sendPushNotification('Game Paused', 'The game timer has paused!');
|
||||
break;
|
||||
case 'paused':
|
||||
gameState = 'running';
|
||||
audioManager.play('gameResume');
|
||||
startTimer();
|
||||
// sendPushNotification('Game Resumed', 'The game timer has resumed!');
|
||||
break;
|
||||
case 'over':
|
||||
// Reset timers and start new game
|
||||
@@ -198,6 +243,26 @@ gameButton.addEventListener('click', () => {
|
||||
saveData();
|
||||
});
|
||||
|
||||
async function sendPushNotification(title, message) {
|
||||
if (!pushSubscription) return;
|
||||
|
||||
try {
|
||||
await fetch('https://webpush.virtonline.eu/flic-webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: title,
|
||||
body: message,
|
||||
action: 'game_update'
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error sending push notification:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Timer variables
|
||||
let timerInterval = null;
|
||||
|
||||
@@ -776,19 +841,23 @@ navigator.serviceWorker.addEventListener('message', (event) => {
|
||||
// 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();
|
||||
});
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.then(registration => {
|
||||
console.log('ServiceWorker registered');
|
||||
|
||||
// Request notification permission and subscribe
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
subscribeToPushNotifications();
|
||||
}
|
||||
});
|
||||
|
||||
setupDeepLinks();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('ServiceWorker registration failed:', error);
|
||||
setupDeepLinks();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// If service workers aren't supported, still handle deep links
|
||||
|
||||
10
index.html
10
index.html
@@ -164,6 +164,16 @@
|
||||
.catch((err) => console.log("Service Worker Failed", err));
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
// Request notification permission on page load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if ('Notification' in window) {
|
||||
Notification.requestPermission().then(permission => {
|
||||
console.log('Notification permission:', permission);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<footer class="app-footer">
|
||||
<div class="author-info">
|
||||
<p>Vibe coded by Martin</p>
|
||||
|
||||
@@ -95,5 +95,6 @@
|
||||
"url": "/?action=reset",
|
||||
"icons": [{ "src": "/icons/reset.png", "sizes": "192x192" }]
|
||||
}
|
||||
]
|
||||
],
|
||||
"gcm_sender_id": "103953800507"
|
||||
}
|
||||
47
sw.js
47
sw.js
@@ -148,27 +148,42 @@ self.addEventListener('message', event => {
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener('push', event => {
|
||||
console.log('[ServiceWorker] Push received');
|
||||
|
||||
const data = event.data ? event.data.json() : {};
|
||||
const title = data.title || 'Game Timer Notification';
|
||||
const options = {
|
||||
body: data.body || 'You have a new notification',
|
||||
icon: '/icons/android-chrome-192x192.png',
|
||||
badge: '/icons/android-chrome-192x192.png',
|
||||
data: data
|
||||
};
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(title, options)
|
||||
);
|
||||
});
|
||||
|
||||
// This helps with navigation after app is installed
|
||||
self.addEventListener('notificationclick', event => {
|
||||
console.log('[ServiceWorker] Notification click received');
|
||||
|
||||
event.notification.close();
|
||||
|
||||
// This looks to see if the current is already open and focuses if it is
|
||||
// Handle the notification click
|
||||
event.waitUntil(
|
||||
self.clients.matchAll({
|
||||
type: 'window'
|
||||
})
|
||||
.then(clientList => {
|
||||
// Check if there is already a window/tab open with the target URL
|
||||
for (const client of clientList) {
|
||||
// If so, just focus it
|
||||
if (client.url.startsWith(self.location.origin) && 'focus' in client) {
|
||||
return client.focus();
|
||||
self.clients.matchAll({ type: 'window' })
|
||||
.then(clientList => {
|
||||
for (const client of clientList) {
|
||||
if (client.url.startsWith(self.location.origin) && 'focus' in client) {
|
||||
return client.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
// If not, open a new window/tab
|
||||
if (self.clients.openWindow) {
|
||||
return self.clients.openWindow('/');
|
||||
}
|
||||
})
|
||||
|
||||
if (self.clients.openWindow) {
|
||||
return self.clients.openWindow('/');
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user