mirror of
https://github.com/screentinker/screentinker.git
synced 2026-05-15 07:32:23 -06:00
The remote-control feature dispatches synthetic click events on the player when the dashboard forwards touches. The global click handler called requestFullscreen() on every click, but the browser only honors that API for trusted user gestures — synthetic events rejected with "Permissions check failed" / "API can only be initiated by a user gesture", spamming the console for the duration of any remote session. Gate the fullscreen request on event.isTrusted. Local user clicks still trigger fullscreen; remote-control taps no longer try (and fail). Bumped SW cache to v8. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
53 lines
1.8 KiB
JavaScript
53 lines
1.8 KiB
JavaScript
const CACHE_NAME = 'rd-player-v8';
|
|
|
|
// Install: skip waiting to activate immediately
|
|
self.addEventListener('install', (event) => {
|
|
self.skipWaiting();
|
|
});
|
|
|
|
// Activate: clean old caches (including old content cache), claim clients
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then(keys => Promise.all(
|
|
keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))
|
|
)).then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
// Fetch handler — ONLY cache player page and static assets.
|
|
// Content files (/uploads/content/) are NOT intercepted — the server sets
|
|
// Cache-Control: public, max-age=2592000, immutable which lets the browser
|
|
// cache them natively without SW complications (range requests, opaque
|
|
// responses, video seeking, etc.)
|
|
self.addEventListener('fetch', (event) => {
|
|
// Only handle GET requests
|
|
if (event.request.method !== 'GET') return;
|
|
|
|
const url = new URL(event.request.url);
|
|
|
|
// Player page and static assets: network-first, fall back to cache
|
|
if (url.pathname.startsWith('/player') || url.pathname === '/socket.io/socket.io.js') {
|
|
event.respondWith(
|
|
fetch(event.request).then(response => {
|
|
if (response.ok && response.type !== 'opaque') {
|
|
const clone = response.clone();
|
|
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
|
|
}
|
|
return response;
|
|
}).catch(() =>
|
|
caches.match(event.request, { ignoreSearch: true }).then(cached =>
|
|
cached || new Response('Offline', {
|
|
status: 503,
|
|
statusText: 'Service Unavailable',
|
|
headers: { 'Content-Type': 'text/plain' }
|
|
})
|
|
)
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Everything else (content files, API calls, etc.): don't intercept.
|
|
// Returning without event.respondWith lets the browser handle it natively.
|
|
});
|