1<script lang="ts">
2 import '../app.css';;
3 import { chatState } from '$lib/states/chat.svelte';
4 import { onMount, onDestroy } from 'svelte';
5 import { browser } from '$app/environment';
6
7 let { children, data } = $props();
8 let isVisible = $state(true);
9 let statusInterval = $state<number | undefined>(undefined);
10
11 async function updateUserStatus(status: 'online' | 'offline' | 'busy') {
12 if (!data.user?.id) return;
13
14 const timestamp = new Date().toISOString();
15 console.debug('Updating user status:', {
16 userId: data.user.id,
17 status,
18 timestamp,
19 trigger: new Error().stack?.split('\n')[2]?.trim()
20 });
21
22 try {
23 const response = await fetch('/api/status', {
24 method: 'POST',
25 headers: { 'Content-Type': 'application/json' },
26 body: JSON.stringify({
27 status,
28 lastSeen: timestamp
29 })
30 });
31
32 if (!response.ok) {
33 console.error('Failed to update user status:', await response.json());
34 }
35 } catch (error) {
36 console.error('Error updating user status:', error);
37 }
38 }
39
40 function startStatusTicker() {
41 if (statusInterval) return;
42
43 console.debug('Starting status ticker');
44 const tickHandler = () => {
45 if (isVisible) {
46 updateUserStatus('online');
47 }
48 };
49
50 statusInterval = window.setInterval(tickHandler, 60000); // Every minute
51 // Run immediately on start
52 tickHandler();
53 }
54
55 function stopStatusTicker() {
56 if (!statusInterval) return;
57 console.debug('Stopping status ticker');
58 window.clearInterval(statusInterval);
59 statusInterval = undefined;
60 }
61
62 function handleVisibilityChange() {
63 if (!browser) return;
64 isVisible = document.visibilityState === 'visible';
65 updateUserStatus(isVisible ? 'online' : 'busy');
66
67 // Start or stop the ticker based on visibility
68 if (isVisible) {
69 startStatusTicker();
70 } else {
71 stopStatusTicker();
72 }
73 }
74
75 function cleanup() {
76 if (!browser) return;
77 console.debug('Running cleanup - Setting user status to offline');
78 document.removeEventListener('visibilitychange', handleVisibilityChange);
79 stopStatusTicker();
80 updateUserStatus('offline');
81 }
82
83 onMount(() => {
84 if (!browser) return;
85
86 // Set initial online status
87 updateUserStatus('online');
88
89 // Start the status ticker
90 startStatusTicker();
91
92 // Add visibility change listener
93 document.addEventListener('visibilitychange', handleVisibilityChange);
94
95 // Add beforeunload listener
96 window.addEventListener('beforeunload', cleanup);
97
98 // Cleanup on unmount
99 return () => {
100 window.removeEventListener('beforeunload', cleanup);
101 cleanup();
102 };
103 });
104
105 // Additional safety net for clean navigation within the app
106 onDestroy(() => {
107 if (!browser) return;
108 console.debug('Component being destroyed - Running cleanup');
109 window.removeEventListener('beforeunload', cleanup);
110 cleanup();
111 });
112
113 $effect(() => {
114 if (!browser) return;
115 console.debug('Layout reactive update - Session state:', {
116 timestamp: new Date().toISOString(),
117 hasUser: !!data.user,
118 userDetails: data.user ? {
119 nickname: data.user.nickname,
120 status: data.user.status,
121 id: data.user.id
122 } : null,
123 hasSession: !!data.session,
124 sessionDetails: data.session ? {
125 id: data.session.id,
126 expiresAt: new Date(data.session.expiresAt)
127 } : null,
128 cookiePresent: document.cookie.includes('session='),
129 isVisible
130 });
131 });
132
133 $effect(() => {
134 console.debug('Updating global chatState with data.user:', data.user);
135 chatState.setCurrentUser(data.user);
136 });
137</script>
138
139<div class="windows-xp-bg">
140 {@render children()}
141</div>
142
143<style>
144 .windows-xp-bg {
145 position: fixed;
146 top: 0;
147 left: 0;
148 width: 100vw;
149 height: 100vh;
150 margin: 0;
151 padding: 0;
152 overflow: hidden;
153 background: linear-gradient(
154 to bottom,
155 #2195f2 0%, /* Lighter blue at the top */
156 #4aa6e2 25%, /* Mid-light blue */
157 #7ab8e0 50%, /* Mid blue */
158 #86c55c 75%, /* Blue-green transition */
159 #90d047 100% /* Bright green at the bottom */
160 );
161 }
162</style>