This commit is contained in:
parent
c2bfa06faa
commit
a56ca40204
16 changed files with 816 additions and 234 deletions
170
frontend/src/lib/stores/screensaver.js
Normal file
170
frontend/src/lib/stores/screensaver.js
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import { writable, derived } from 'svelte/store';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
const defaultState = {
|
||||
// User settings (from backend)
|
||||
enabled: false,
|
||||
timeoutMinutes: 5,
|
||||
|
||||
// Runtime state
|
||||
active: false, // Is screensaver currently showing
|
||||
idleTime: 0, // Current idle time in seconds
|
||||
tabVisible: true, // Is tab currently visible
|
||||
mediaPlaying: false // Is any media currently playing
|
||||
};
|
||||
|
||||
function createScreensaverStore() {
|
||||
const { subscribe, set, update } = writable(defaultState);
|
||||
|
||||
let idleTimer = null;
|
||||
let activityListenersAdded = false;
|
||||
|
||||
// Activity events to track
|
||||
const activityEvents = ['mousemove', 'mousedown', 'keydown', 'touchstart', 'scroll'];
|
||||
|
||||
function resetIdleTimer() {
|
||||
update(state => {
|
||||
if (state.active) {
|
||||
// Dismiss screensaver on any activity
|
||||
return { ...state, active: false, idleTime: 0 };
|
||||
}
|
||||
return { ...state, idleTime: 0 };
|
||||
});
|
||||
}
|
||||
|
||||
function checkMediaPlaying() {
|
||||
if (!browser) return false;
|
||||
|
||||
// Check for playing video elements
|
||||
const videos = document.querySelectorAll('video');
|
||||
for (const video of videos) {
|
||||
if (!video.paused && !video.ended) return true;
|
||||
}
|
||||
|
||||
// Check for playing audio elements
|
||||
const audios = document.querySelectorAll('audio');
|
||||
for (const audio of audios) {
|
||||
if (!audio.paused && !audio.ended) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleVisibilityChange() {
|
||||
update(state => ({
|
||||
...state,
|
||||
tabVisible: document.visibilityState === 'visible',
|
||||
idleTime: 0 // Reset idle time on visibility change
|
||||
}));
|
||||
}
|
||||
|
||||
function startTracking() {
|
||||
if (!browser || activityListenersAdded) return;
|
||||
|
||||
// Handle visibility changes
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Handle activity events
|
||||
activityEvents.forEach(event => {
|
||||
document.addEventListener(event, resetIdleTimer, { passive: true });
|
||||
});
|
||||
|
||||
activityListenersAdded = true;
|
||||
|
||||
// Start idle timer (check every second)
|
||||
if (idleTimer) clearInterval(idleTimer);
|
||||
idleTimer = setInterval(() => {
|
||||
update(state => {
|
||||
// Don't do anything if disabled
|
||||
if (!state.enabled) return state;
|
||||
|
||||
const mediaPlaying = checkMediaPlaying();
|
||||
const newIdleTime = state.idleTime + 1;
|
||||
const newState = { ...state, idleTime: newIdleTime, mediaPlaying };
|
||||
|
||||
// Check if should activate
|
||||
// Don't activate if: disabled, tab not visible, media playing, already active
|
||||
if (!state.enabled || !state.tabVisible || mediaPlaying || state.active) {
|
||||
return newState;
|
||||
}
|
||||
|
||||
// Check if idle time exceeds timeout
|
||||
if (newIdleTime >= state.timeoutMinutes * 60) {
|
||||
return { ...newState, active: true };
|
||||
}
|
||||
|
||||
return newState;
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function stopTracking() {
|
||||
if (!browser) return;
|
||||
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
activityEvents.forEach(event => {
|
||||
document.removeEventListener(event, resetIdleTimer);
|
||||
});
|
||||
|
||||
activityListenersAdded = false;
|
||||
|
||||
if (idleTimer) {
|
||||
clearInterval(idleTimer);
|
||||
idleTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
|
||||
// Initialize from user settings
|
||||
init(settings) {
|
||||
const enabled = settings?.screensaverEnabled || false;
|
||||
const timeoutMinutes = settings?.screensaverTimeoutMinutes || 5;
|
||||
|
||||
update(state => ({
|
||||
...state,
|
||||
enabled,
|
||||
timeoutMinutes
|
||||
}));
|
||||
|
||||
if (browser && enabled) {
|
||||
startTracking();
|
||||
}
|
||||
},
|
||||
|
||||
// Update settings from API response
|
||||
updateSettings(enabled, timeoutMinutes) {
|
||||
update(state => ({
|
||||
...state,
|
||||
enabled,
|
||||
timeoutMinutes
|
||||
}));
|
||||
|
||||
if (browser) {
|
||||
if (enabled) {
|
||||
startTracking();
|
||||
} else {
|
||||
stopTracking();
|
||||
update(state => ({ ...state, active: false }));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Manually dismiss the screensaver
|
||||
dismiss() {
|
||||
update(state => ({ ...state, active: false, idleTime: 0 }));
|
||||
},
|
||||
|
||||
// Cleanup on component destroy
|
||||
cleanup() {
|
||||
stopTracking();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const screensaver = createScreensaverStore();
|
||||
|
||||
// Derived store for whether screensaver is active
|
||||
export const isScreensaverActive = derived(screensaver, $s => $s.active);
|
||||
Loading…
Add table
Add a link
Reference in a new issue