refactor: move dashboard screens into shell routes

This commit is contained in:
2026-04-17 13:20:00 +01:00
parent e97a54ac8d
commit fac6409ec4
16 changed files with 113 additions and 21 deletions

View File

@@ -13,7 +13,7 @@
</head> </head>
<body <body
data-sveltekit-preload-data="hover" data-sveltekit-preload-data="hover"
class="h-screen overflow-hidden flex bg-[#0a0a0c] text-gray-200" class="bg-[#0a0a0c] text-gray-200"
style="background:#0a0a0c; color:#e5e7eb" style="background:#0a0a0c; color:#e5e7eb"
> >
<div style="display: contents">%sveltekit.body%</div> <div style="display: contents">%sveltekit.body%</div>

View File

@@ -2,12 +2,12 @@
// @ts-nocheck // @ts-nocheck
const PAGE_PATHS = { const PAGE_PATHS = {
auth: '/', auth: '/auth/login',
onboarding: '/onboarding', onboarding: '/app/onboarding',
camera: '/camera', camera: '/app/camera',
client: '/client', client: '/app/client',
activity: '/activity', activity: '/app/activity',
settings: '/settings' settings: '/app/settings'
}; };
const DEVICE_STORAGE_KEY = 'mobileSimDevice'; const DEVICE_STORAGE_KEY = 'mobileSimDevice';
@@ -66,15 +66,21 @@ const normalizePath = (path) => path.replace(/\/+$/, '') || '/';
export const pageFromPath = (path) => { export const pageFromPath = (path) => {
switch (normalizePath(path)) { switch (normalizePath(path)) {
case '/onboarding': case '/auth':
case '/auth/login':
case '/auth/signup':
return 'auth';
case '/app':
return 'app';
case '/app/onboarding':
return 'onboarding'; return 'onboarding';
case '/camera': case '/app/camera':
return 'camera'; return 'camera';
case '/client': case '/app/client':
return 'client'; return 'client';
case '/activity': case '/app/activity':
return 'activity'; return 'activity';
case '/settings': case '/app/settings':
return 'settings'; return 'settings';
default: default:
return 'auth'; return 'auth';

View File

@@ -55,6 +55,9 @@ let initPromise = null;
let socket = null; let socket = null;
let pollInterval = null; let pollInterval = null;
let socketHeartbeatInterval = null; let socketHeartbeatInterval = null;
const handleBeforeUnload = () => {
void cleanupConnectionState();
};
let clientVideoElement = null; let clientVideoElement = null;
@@ -839,7 +842,13 @@ const invalidateSavedDevice = async (message, options = {}) => {
const enforceRouteForSession = () => { const enforceRouteForSession = () => {
const state = getAppState(); const state = getAppState();
const page = pageFromPath(window.location.pathname); const page = pageFromPath(window.location.pathname);
setAppState({ page }); const initialPage =
page === 'app'
? state.deviceToken
? getHomePageKeyForRole(state.device?.role)
: 'onboarding'
: page;
setAppState({ page: initialPage });
if (!state.session) { if (!state.session) {
if (page !== 'auth') { if (page !== 'auth') {
@@ -856,7 +865,7 @@ const enforceRouteForSession = () => {
} }
const expectedHome = getHomePageKeyForRole(state.device?.role); const expectedHome = getHomePageKeyForRole(state.device?.role);
if ((page === 'auth' || page === 'onboarding') && expectedHome) { if ((page === 'auth' || page === 'onboarding' || page === 'app') && expectedHome) {
navigateToScreen('home', { replace: true, role: state.device?.role }); navigateToScreen('home', { replace: true, role: state.device?.role });
return; return;
} }
@@ -903,9 +912,7 @@ const init = async () => {
void refreshCameraInputDevices(); void refreshCameraInputDevices();
applyMotionDetectionReadiness(); applyMotionDetectionReadiness();
window.addEventListener('beforeunload', () => { window.addEventListener('beforeunload', handleBeforeUnload);
void cleanupConnectionState();
});
initialized = true; initialized = true;
})().finally(() => { })().finally(() => {
@@ -923,6 +930,7 @@ const destroy = async () => {
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
document.removeEventListener('visibilitychange', onVisibilityChange); document.removeEventListener('visibilitychange', onVisibilityChange);
} }
window.removeEventListener('beforeunload', handleBeforeUnload);
initialized = false; initialized = false;
await cleanupConnectionState(); await cleanupConnectionState();
}; };
@@ -955,6 +963,10 @@ const actions = {
patchAppState((state) => ({ isRegistering: !state.isRegistering })); patchAppState((state) => ({ isRegistering: !state.isRegistering }));
}, },
setAuthMode(isRegistering) {
setAppState({ isRegistering });
},
async submitAuth() { async submitAuth() {
const state = getAppState(); const state = getAppState();
const { email, password, name } = state.authForm; const { email, password, name } = state.authForm;

View File

@@ -0,0 +1,19 @@
<script lang="ts">
// @ts-nocheck
import { onMount } from 'svelte';
import { appController } from '$lib/app/controller';
let { children } = $props();
onMount(() => {
void appController.init();
return () => {
void appController.destroy();
};
});
</script>
<div class="flex h-screen overflow-hidden bg-[#0a0a0c] text-gray-200">
{@render children()}
</div>

View File

@@ -0,0 +1,34 @@
<script lang="ts">
// @ts-nocheck
import { appController } from '$lib/app/controller';
import { appState } from '$lib/app/store';
let redirected = $state(false);
$effect(() => {
if (redirected || $appState.loading) {
return;
}
if (!$appState.session) {
appController.navigate('auth');
redirected = true;
return;
}
if (!$appState.deviceToken) {
appController.navigate('onboarding');
redirected = true;
return;
}
appController.navigate('home');
redirected = true;
});
</script>
<section class="flex flex-1 items-center justify-center px-6">
<div class="rounded-full border border-white/10 bg-white/5 px-4 py-2 text-sm text-slate-300">
Preparing your workspace…
</div>
</section>

View File

@@ -3,7 +3,6 @@
import '../app.css'; import '../app.css';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import favicon from '$lib/assets/favicon.svg'; import favicon from '$lib/assets/favicon.svg';
import { appController } from '$lib/app/controller';
import { import {
clearInstallPrompt, clearInstallPrompt,
setInstallPrompt, setInstallPrompt,
@@ -85,14 +84,11 @@
window.addEventListener('online', handleOnline); window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline); window.addEventListener('offline', handleOffline);
void appController.init();
return () => { return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
window.removeEventListener('appinstalled', handleAppInstalled); window.removeEventListener('appinstalled', handleAppInstalled);
window.removeEventListener('online', handleOnline); window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline); window.removeEventListener('offline', handleOffline);
void appController.destroy();
}; };
}); });
</script> </script>

View File

@@ -0,0 +1,5 @@
import { redirect } from '@sveltejs/kit';
export function load() {
throw redirect(307, '/app/activity');
}

View File

@@ -0,0 +1,5 @@
import { redirect } from '@sveltejs/kit';
export function load() {
throw redirect(307, '/app/camera');
}

View File

@@ -0,0 +1,5 @@
import { redirect } from '@sveltejs/kit';
export function load() {
throw redirect(307, '/app/client');
}

View File

@@ -0,0 +1,5 @@
import { redirect } from '@sveltejs/kit';
export function load() {
throw redirect(307, '/app/onboarding');
}

View File

@@ -0,0 +1,5 @@
import { redirect } from '@sveltejs/kit';
export function load() {
throw redirect(307, '/app/settings');
}