Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit 8bc58d3

Browse files
committed
chat room read markers
1 parent 5960dd0 commit 8bc58d3

1 file changed

Lines changed: 42 additions & 2 deletions

File tree

src/routes/(protected)/user/chat/[chatRoomId]/+page.svelte

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
if (!existingIds.has(msg.chat_message_id)) {
6262
messages = [...messages, msg];
6363
scrollToBottom();
64+
// If mouse is in the messages area, mark as read
65+
if (mouseInMessages) markAsReadIfNeeded();
6466
}
6567
}
6668
@@ -281,13 +283,16 @@
281283
onMount(() => {
282284
connectSSE();
283285
loadReactions();
284-
// Mark room as read when entering
285-
fetch(`/api/chat/${data.chatRoom.chat_room_id}/read-marker`, { method: 'PUT' });
286+
// Mark as read on initial load (mouse is likely already in the area)
287+
markAsReadIfNeeded();
288+
window.addEventListener('focus', handleWindowFocus);
286289
});
287290
288291
onDestroy(() => {
289292
eventSource?.close();
290293
if (pollInterval) clearInterval(pollInterval);
294+
if (readMarkerTimeout) clearTimeout(readMarkerTimeout);
295+
window.removeEventListener('focus', handleWindowFocus);
291296
});
292297
293298
async function sendMessage(event: SubmitEvent) {
@@ -482,6 +487,39 @@
482487
return segments.length > 0 ? segments : [{ type: 'text', text: content }];
483488
}
484489
490+
// --- Read marker logic ---
491+
// Track when we last marked the room as read, to avoid redundant PUTs.
492+
let lastMarkedReadAt: string = $state('');
493+
let readMarkerTimeout: ReturnType<typeof setTimeout> | null = null;
494+
let mouseInMessages = $state(false);
495+
496+
function markAsReadIfNeeded() {
497+
if (!latestTimestamp || latestTimestamp === lastMarkedReadAt) return;
498+
if (!document.hasFocus()) return;
499+
500+
// Debounce: wait 1s of inactivity before sending
501+
if (readMarkerTimeout) clearTimeout(readMarkerTimeout);
502+
readMarkerTimeout = setTimeout(() => {
503+
lastMarkedReadAt = latestTimestamp;
504+
fetch(`/api/chat/${data.chatRoom.chat_room_id}/read-marker`, { method: 'PUT' });
505+
}, 1000);
506+
}
507+
508+
function handleMessagesMouseEnter() {
509+
mouseInMessages = true;
510+
markAsReadIfNeeded();
511+
}
512+
513+
function handleMessagesMouseLeave() {
514+
mouseInMessages = false;
515+
}
516+
517+
function handleWindowFocus() {
518+
if (mouseInMessages) {
519+
markAsReadIfNeeded();
520+
}
521+
}
522+
485523
// Close emoji picker when clicking outside
486524
function handleWindowClick(event: MouseEvent) {
487525
const target = event.target as HTMLElement;
@@ -584,6 +622,8 @@
584622
class="mb-4 space-y-3 rounded-lg border border-surface-300-600 bg-surface-50-900 p-4"
585623
style="min-height: 300px; max-height: 60vh; overflow-y: auto;"
586624
data-testid="messages-container"
625+
onmouseenter={handleMessagesMouseEnter}
626+
onmouseleave={handleMessagesMouseLeave}
587627
>
588628
{#if messages.length > 0}
589629
{#each messages as message (message.chat_message_id)}

0 commit comments

Comments
 (0)