170 lines
4.9 KiB
JavaScript
170 lines
4.9 KiB
JavaScript
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);
|