fix: offline status not updating in public buddy list view
- commit
- 17f3fb182e17d1bd12be107a521a3d67f8de9eb1
- author
- Kit · · 2025-02-17 10:25 UTC
- parent
- 42d61be
- signature
- SSH Unverified
SHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6TkThe key is unknown
2 files · +40 -16
M src/lib/states/chat.svelte.ts +1 -0
| @@ -35,6 +35,7 @@ class ChatState { | |||
| 35 | 35 | private lastBuddyListUpdate = 0; | |
| 36 | 36 | private lastBuddyListHash = ''; | |
| 37 | 37 | private hasMoreMessages = $state(false); | |
| 38 | + | private publicPollingInterval: ReturnType<typeof setInterval> | null = null; | |
| 38 | 39 | ||
| 39 | 40 | public async reinitialize() { | |
| 40 | 41 | if (this.isInitializing) { | |
| @@ -35,6 +35,7 @@ class ChatState { | |||||
| 35 | private lastBuddyListUpdate = 0; | 35 | private lastBuddyListUpdate = 0; | ||
| 36 | private lastBuddyListHash = ''; | 36 | private lastBuddyListHash = ''; | ||
| 37 | private hasMoreMessages = $state(false); | 37 | private hasMoreMessages = $state(false); | ||
| 38 | + | private publicPollingInterval: ReturnType<typeof setInterval> | null = null; | |||
| 38 | 39 | ||||
| 39 | public async reinitialize() { | 40 | public async reinitialize() { | ||
| 40 | if (this.isInitializing) { | 41 | if (this.isInitializing) { | ||
M src/routes/api/rooms/[roomId]/+server.ts +39 -16
| @@ -7,6 +7,8 @@ import { createLogger } from '$lib/utils/logger.server'; | |||
| 7 | 7 | ||
| 8 | 8 | const log = createLogger('rooms-server'); | |
| 9 | 9 | ||
| 10 | + | const ONLINE_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes | |
| 11 | + | ||
| 10 | 12 | export async function GET({ params, url, locals }): Promise<Response> { | |
| 11 | 13 | const { roomId } = params; | |
| 12 | 14 | const isPublic = !locals.user || url.searchParams.get('public') === 'true'; | |
| @@ -18,25 +20,45 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||
| 18 | 20 | requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public' | |
| 19 | 21 | }); | |
| 20 | 22 | ||
| 21 | - | // Fetch messages for the room (most recent then reversed for chronological order) | |
| 22 | - | const messageQuery = db.select() | |
| 23 | - | .from(messages) | |
| 24 | - | .where(eq(messages.chatRoomId, roomId)) | |
| 25 | - | .orderBy(desc(messages.timestamp)); | |
| 26 | - | ||
| 27 | - | // Limit messages for public requests | |
| 28 | - | if (isPublic) { | |
| 29 | - | messageQuery.limit(10); | |
| 30 | - | } else { | |
| 31 | - | messageQuery.limit(50); | |
| 32 | - | } | |
| 23 | + | // Calculate timeout threshold | |
| 24 | + | const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS; | |
| 33 | 25 | ||
| 34 | - | // Fetch buddy list: get all users in the room (including offline) | |
| 26 | + | // Fetch buddy list: get all users and mark them as offline if they've timed out | |
| 35 | 27 | const fetchedUsers = await db.select() | |
| 36 | - | .from(users); | |
| 28 | + | .from(users) | |
| 29 | + | .execute(); | |
| 30 | + | ||
| 31 | + | // Process users to mark them as offline if they've timed out | |
| 32 | + | const processedUsers = fetchedUsers.map(user => { | |
| 33 | + | const shouldBeOffline = user.status === 'online' && user.lastSeen < timeoutThreshold; | |
| 34 | + | return { | |
| 35 | + | ...user, | |
| 36 | + | status: shouldBeOffline ? 'offline' : user.status | |
| 37 | + | }; | |
| 38 | + | }); | |
| 39 | + | ||
| 40 | + | // Update any users that should be offline in the database | |
| 41 | + | const usersToUpdate = processedUsers.filter(user => | |
| 42 | + | user.status === 'offline' && | |
| 43 | + | fetchedUsers.find(u => u.id === user.id)?.status === 'online' | |
| 44 | + | ); | |
| 45 | + | ||
| 46 | + | if (usersToUpdate.length > 0) { | |
| 47 | + | await Promise.all(usersToUpdate.map(user => | |
| 48 | + | db.update(users) | |
| 49 | + | .set({ status: 'offline', lastSeen: Date.now() }) | |
| 50 | + | .where(eq(users.id, user.id)) | |
| 51 | + | .execute() | |
| 52 | + | )); | |
| 53 | + | ||
| 54 | + | log.debug('Updated offline status for users', { | |
| 55 | + | count: usersToUpdate.length, | |
| 56 | + | userIds: usersToUpdate.map(u => u.id) | |
| 57 | + | }); | |
| 58 | + | } | |
| 37 | 59 | ||
| 38 | 60 | // Sanitize user data using createSafeUser | |
| 39 | - | const sanitizedUsers = fetchedUsers.map(user => createSafeUser(user)); | |
| 61 | + | const sanitizedUsers = processedUsers.map(user => createSafeUser(user)); | |
| 40 | 62 | ||
| 41 | 63 | const responseData: PublicRoomResponse = { | |
| 42 | 64 | success: true, | |
| @@ -46,7 +68,8 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||
| 46 | 68 | log.debug('Successfully fetched room data', { | |
| 47 | 69 | roomId, | |
| 48 | 70 | userCount: sanitizedUsers.length, | |
| 49 | - | isPublic | |
| 71 | + | isPublic, | |
| 72 | + | offlineUpdates: usersToUpdate.length | |
| 50 | 73 | }); | |
| 51 | 74 | ||
| 52 | 75 | return new Response(JSON.stringify(responseData), { | |
| @@ -7,6 +7,8 @@ import { createLogger } from '$lib/utils/logger.server'; | |||||
| 7 | 7 | ||||
| 8 | const log = createLogger('rooms-server'); | 8 | const log = createLogger('rooms-server'); | ||
| 9 | 9 | ||||
| 10 | + | const ONLINE_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes | |||
| 11 | + | ||||
| 10 | export async function GET({ params, url, locals }): Promise<Response> { | 12 | export async function GET({ params, url, locals }): Promise<Response> { | ||
| 11 | const { roomId } = params; | 13 | const { roomId } = params; | ||
| 12 | const isPublic = !locals.user || url.searchParams.get('public') === 'true'; | 14 | const isPublic = !locals.user || url.searchParams.get('public') === 'true'; | ||
| @@ -18,25 +20,45 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||||
| 18 | requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public' | 20 | requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public' | ||
| 19 | }); | 21 | }); | ||
| 20 | 22 | ||||
| 21 | - | // Fetch messages for the room (most recent then reversed for chronological order) | 23 | + | // Calculate timeout threshold |
| 22 | - | const messageQuery = db.select() | 24 | + | const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS; |
| 23 | - | .from(messages) | |||
| 24 | - | .where(eq(messages.chatRoomId, roomId)) | |||
| 25 | - | .orderBy(desc(messages.timestamp)); | |||
| 26 | - | ||||
| 27 | - | // Limit messages for public requests | |||
| 28 | - | if (isPublic) { | |||
| 29 | - | messageQuery.limit(10); | |||
| 30 | - | } else { | |||
| 31 | - | messageQuery.limit(50); | |||
| 32 | - | } | |||
| 33 | 25 | ||||
| 34 | - | // Fetch buddy list: get all users in the room (including offline) | 26 | + | // Fetch buddy list: get all users and mark them as offline if they've timed out |
| 35 | const fetchedUsers = await db.select() | 27 | const fetchedUsers = await db.select() | ||
| 36 | - | .from(users); | 28 | + | .from(users) |
| 29 | + | .execute(); | |||
| 30 | + | ||||
| 31 | + | // Process users to mark them as offline if they've timed out | |||
| 32 | + | const processedUsers = fetchedUsers.map(user => { | |||
| 33 | + | const shouldBeOffline = user.status === 'online' && user.lastSeen < timeoutThreshold; | |||
| 34 | + | return { | |||
| 35 | + | ...user, | |||
| 36 | + | status: shouldBeOffline ? 'offline' : user.status | |||
| 37 | + | }; | |||
| 38 | + | }); | |||
| 39 | + | ||||
| 40 | + | // Update any users that should be offline in the database | |||
| 41 | + | const usersToUpdate = processedUsers.filter(user => | |||
| 42 | + | user.status === 'offline' && | |||
| 43 | + | fetchedUsers.find(u => u.id === user.id)?.status === 'online' | |||
| 44 | + | ); | |||
| 45 | + | ||||
| 46 | + | if (usersToUpdate.length > 0) { | |||
| 47 | + | await Promise.all(usersToUpdate.map(user => | |||
| 48 | + | db.update(users) | |||
| 49 | + | .set({ status: 'offline', lastSeen: Date.now() }) | |||
| 50 | + | .where(eq(users.id, user.id)) | |||
| 51 | + | .execute() | |||
| 52 | + | )); | |||
| 53 | + | ||||
| 54 | + | log.debug('Updated offline status for users', { | |||
| 55 | + | count: usersToUpdate.length, | |||
| 56 | + | userIds: usersToUpdate.map(u => u.id) | |||
| 57 | + | }); | |||
| 58 | + | } | |||
| 37 | 59 | ||||
| 38 | // Sanitize user data using createSafeUser | 60 | // Sanitize user data using createSafeUser | ||
| 39 | - | const sanitizedUsers = fetchedUsers.map(user => createSafeUser(user)); | 61 | + | const sanitizedUsers = processedUsers.map(user => createSafeUser(user)); |
| 40 | 62 | ||||
| 41 | const responseData: PublicRoomResponse = { | 63 | const responseData: PublicRoomResponse = { | ||
| 42 | success: true, | 64 | success: true, | ||
| @@ -46,7 +68,8 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||||
| 46 | log.debug('Successfully fetched room data', { | 68 | log.debug('Successfully fetched room data', { | ||
| 47 | roomId, | 69 | roomId, | ||
| 48 | userCount: sanitizedUsers.length, | 70 | userCount: sanitizedUsers.length, | ||
| 49 | - | isPublic | 71 | + | isPublic, |
| 72 | + | offlineUpdates: usersToUpdate.length | |||
| 50 | }); | 73 | }); | ||
| 51 | 74 | ||||
| 52 | return new Response(JSON.stringify(responseData), { | 75 | return new Response(JSON.stringify(responseData), { | ||