/* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-nocheck const PAGE_PATHS = { auth: '/auth/login', onboarding: '/app/onboarding', camera: '/app/camera', client: '/app/client', activity: '/app/activity', settings: '/app/settings' }; const DEVICE_STORAGE_KEY = 'mobileSimDevice'; const MOTION_DETECTION_SETTINGS_STORAGE_KEY = 'securecam-motion-detection-settings'; export const INVALID_DEVICE_TOKEN_ERRORS = new Set([ 'Missing device token', 'Invalid device token', 'Device not found', 'Token role does not match device role' ]); export const MOTION_DETECTION_PROFILES = { low_power: { profile: 'low_power', label: 'Low Power', description: 'Least heat and battery usage, slower trigger response.', sampleIntervalMs: 1400, burstIntervalMs: 500, triggerThreshold: 0.15, releaseThreshold: 0.05, consecutiveTriggerFrames: 3, cooldownMs: 12000, minimumEventMs: 6000 }, balanced: { profile: 'balanced', label: 'Balanced', description: 'Recommended default for a plugged-in foreground browser.', sampleIntervalMs: 1000, burstIntervalMs: 300, triggerThreshold: 0.12, releaseThreshold: 0.04, consecutiveTriggerFrames: 3, cooldownMs: 9000, minimumEventMs: 6000 }, responsive: { profile: 'responsive', label: 'Responsive', description: 'Faster trigger response with higher CPU and thermal cost.', sampleIntervalMs: 700, burstIntervalMs: 220, triggerThreshold: 0.1, releaseThreshold: 0.035, consecutiveTriggerFrames: 2, cooldownMs: 7000, minimumEventMs: 6000 } }; export const MAX_STREAM_DIAGNOSTIC_SESSIONS = 12; export const MAX_STREAM_DIAGNOSTIC_ENTRIES = 24; const normalizePath = (path) => path.replace(/\/+$/, '') || '/'; export const pageFromPath = (path) => { switch (normalizePath(path)) { case '/auth': case '/auth/login': case '/auth/signup': return 'auth'; case '/app': return 'app'; case '/app/onboarding': return 'onboarding'; case '/app/camera': return 'camera'; case '/app/client': return 'client'; case '/app/activity': return 'activity'; case '/app/settings': return 'settings'; default: return 'auth'; } }; export const getHomePageKeyForRole = (role) => (role === 'camera' ? 'camera' : 'client'); export const getMotionDetectionProfile = (profile) => MOTION_DETECTION_PROFILES[profile] ?? MOTION_DETECTION_PROFILES.balanced; const getDefaultMotionDetectionState = () => ({ enabled: false, profile: MOTION_DETECTION_PROFILES.balanced.profile, state: 'idle', score: 0, debug: false, lastTriggeredAt: null }); const buildMotionDetectionState = (overrides = {}) => { const defaults = getDefaultMotionDetectionState(); const nextProfile = getMotionDetectionProfile(overrides.profile ?? defaults.profile); return { ...defaults, ...nextProfile, ...overrides, profile: nextProfile.profile }; }; const sanitizeMotionDetectionSettings = (value) => { if (!value || typeof value !== 'object') { return buildMotionDetectionState(); } return buildMotionDetectionState({ enabled: Boolean(value.enabled), profile: typeof value.profile === 'string' ? value.profile : undefined, debug: Boolean(value.debug) }); }; const normalizeMotionDetectionState = (value) => { if (!value || typeof value !== 'object') { return buildMotionDetectionState(); } return buildMotionDetectionState({ enabled: Boolean(value.enabled), profile: typeof value.profile === 'string' ? value.profile : undefined, state: typeof value.state === 'string' ? value.state : undefined, score: typeof value.score === 'number' ? value.score : undefined, debug: Boolean(value.debug), lastTriggeredAt: typeof value.lastTriggeredAt === 'string' ? value.lastTriggeredAt : null }); }; export const createControllerShared = ({ api, getAppState, setAppState, patchAppState }) => { const makeId = () => { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } return `${Date.now()}-${Math.random().toString(16).slice(2)}`; }; const getCurrentPath = () => normalizePath(window.location.pathname); const getPathForScreen = (screen, role) => { if (screen === 'home') { return PAGE_PATHS[getHomePageKeyForRole(role)]; } return PAGE_PATHS[screen] || null; }; const navigateToScreen = (screen, options = {}) => { const { replace = false, role = getAppState().device?.role } = options; const targetPath = getPathForScreen(screen, role); if (!targetPath) return false; if (getCurrentPath() !== normalizePath(targetPath)) { if (replace) { window.location.replace(targetPath); } else { window.location.assign(targetPath); } return true; } setAppState({ page: pageFromPath(targetPath) }); return false; }; const pushToast = (message, type = 'info') => { const id = makeId(); patchAppState((state) => ({ toasts: [...state.toasts, { id, message, type }].slice(-6) })); setTimeout(() => { patchAppState((state) => ({ toasts: state.toasts.filter((toast) => toast.id !== id) })); }, 3200); }; const removeToast = (id) => { patchAppState((state) => ({ toasts: state.toasts.filter((toast) => toast.id !== id) })); }; const addActivity = (type, message) => { const item = { id: makeId(), type, message, createdAt: new Date().toISOString() }; patchAppState((state) => ({ activityLog: [item, ...state.activityLog].slice(0, 200) })); }; const pruneStreamDiagnostics = (diagnostics) => { const entries = Object.entries(diagnostics || {}); if (entries.length <= MAX_STREAM_DIAGNOSTIC_SESSIONS) { return diagnostics; } return Object.fromEntries( entries .sort( (left, right) => new Date(right[1]?.updatedAt || 0).getTime() - new Date(left[1]?.updatedAt || 0).getTime() ) .slice(0, MAX_STREAM_DIAGNOSTIC_SESSIONS) ); }; const pushStreamDiagnostic = (streamSessionId, stage, message, level = 'info', meta = {}) => { if (!streamSessionId || !stage || !message) { return; } const createdAt = new Date().toISOString(); patchAppState((state) => { const current = state.streamDiagnostics?.[streamSessionId] ?? { streamSessionId, cameraDeviceId: null, updatedAt: createdAt, entries: [] }; const next = { ...current, ...meta, streamSessionId, updatedAt: createdAt, entries: [ { id: makeId(), stage, message, level, createdAt }, ...(current.entries || []) ].slice(0, MAX_STREAM_DIAGNOSTIC_ENTRIES) }; return { streamDiagnostics: pruneStreamDiagnostics({ ...(state.streamDiagnostics || {}), [streamSessionId]: next }) }; }); }; const loadMotionDetectionSettings = () => { if (typeof localStorage === 'undefined') { return buildMotionDetectionState(); } try { const saved = localStorage.getItem(MOTION_DETECTION_SETTINGS_STORAGE_KEY); if (!saved) { return buildMotionDetectionState(); } return sanitizeMotionDetectionSettings(JSON.parse(saved)); } catch (error) { console.error('Failed to load motion detection settings', error); return buildMotionDetectionState(); } }; const persistMotionDetectionSettings = (motionDetection) => { if (typeof localStorage === 'undefined') { return; } try { localStorage.setItem( MOTION_DETECTION_SETTINGS_STORAGE_KEY, JSON.stringify({ enabled: Boolean(motionDetection?.enabled), profile: motionDetection?.profile ?? MOTION_DETECTION_PROFILES.balanced.profile, debug: Boolean(motionDetection?.debug) }) ); } catch (error) { console.error('Failed to save motion detection settings', error); } }; const updateMotionDetectionState = (updates) => { const current = normalizeMotionDetectionState(getAppState().motionDetection); const next = typeof updates === 'function' ? normalizeMotionDetectionState({ ...current, ...updates(current) }) : normalizeMotionDetectionState({ ...current, ...updates }); setAppState({ motionDetection: next }); persistMotionDetectionSettings(next); return next; }; const readSavedDeviceRecord = () => { if (typeof localStorage === 'undefined') { return null; } const saved = localStorage.getItem(DEVICE_STORAGE_KEY); if (!saved) { return null; } try { return JSON.parse(saved); } catch (error) { console.error('Failed to parse saved device', error); localStorage.removeItem(DEVICE_STORAGE_KEY); return null; } }; const persistSavedDeviceRecord = ({ device, deviceToken, userId }) => { if (typeof localStorage === 'undefined') { return; } localStorage.setItem( DEVICE_STORAGE_KEY, JSON.stringify({ device, deviceToken, userId }) ); }; const clearSavedDeviceRecord = () => { if (typeof localStorage === 'undefined') { return; } localStorage.removeItem(DEVICE_STORAGE_KEY); }; const applySavedDeviceState = (device, deviceToken) => { setAppState({ device, deviceToken, onboardingForm: { ...getAppState().onboardingForm, name: device?.name ?? '', role: device?.role ?? 'client', pushToken: '' } }); }; const clearDeviceState = () => { setAppState({ device: null, deviceToken: null, socketConnected: false, isMotionActive: false, activeMotionSource: null, cameraStatus: 'idle', cameraPreviewReady: false, linkedCameras: [], recordings: [], activeCameraDeviceId: null, activeStreamSessionId: null, openLinkedCameraMenuId: null, cameraSessions: {}, connectedStreamSessionIds: [], clientStreamMode: 'none', clientPlaceholderText: 'Select a camera to view', onboardingForm: { ...getAppState().onboardingForm, name: '', role: 'client', pushToken: '' } }); }; const restoreSavedDeviceForSession = async (session, options = {}) => { const { showMissingToast = false, showInvalidToast = false } = options; const saved = readSavedDeviceRecord(); if (!saved) { if (showMissingToast) { pushToast('No saved device found', 'info'); } return false; } const sessionUserId = session?.user?.id; const savedUserId = typeof saved.userId === 'string' ? saved.userId : null; const savedDeviceId = saved?.device?.id; const savedDeviceToken = typeof saved?.deviceToken === 'string' ? saved.deviceToken : ''; if (!sessionUserId || !savedDeviceId || !savedDeviceToken) { clearSavedDeviceRecord(); clearDeviceState(); if (showInvalidToast) { pushToast('Saved device is incomplete. Please register again.', 'error'); } return false; } if (savedUserId && savedUserId !== sessionUserId) { clearSavedDeviceRecord(); clearDeviceState(); if (showInvalidToast) { pushToast('Saved device belongs to a different account.', 'info'); } return false; } try { const result = await api.devices.list(); const matchingDevice = result.devices?.find((device) => device.id === savedDeviceId); if (!matchingDevice) { clearSavedDeviceRecord(); clearDeviceState(); if (showInvalidToast) { pushToast('Saved device was not found for this account.', 'info'); } return false; } applySavedDeviceState(matchingDevice, savedDeviceToken); persistSavedDeviceRecord({ device: matchingDevice, deviceToken: savedDeviceToken, userId: sessionUserId }); return true; } catch (error) { console.error('Failed to restore saved device', error); if (showInvalidToast) { pushToast('Unable to restore saved device right now.', 'error'); } return false; } }; return { makeId, pushToast, removeToast, addActivity, pushStreamDiagnostic, loadMotionDetectionSettings, updateMotionDetectionState, navigateToScreen, persistSavedDeviceRecord, clearSavedDeviceRecord, applySavedDeviceState, clearDeviceState, restoreSavedDeviceForSession }; };