From 5b6f231e1a91824133a696db91042f143ea1370e Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:53:49 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(todo,matrix):=20improve=20cl?= =?UTF-8?q?ick=20targets=20and=20type=20safety?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - todo: Make task-content button fill full height for better click target - matrix: Fix TypeScript errors by using Boolean() for template expressions Co-Authored-By: Claude Opus 4.5 --- .../src/lib/components/chat/Timeline.svelte | 77 ++++++++++++++----- .../web/src/lib/components/TaskItem.svelte | 35 +-------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/apps/matrix/apps/web/src/lib/components/chat/Timeline.svelte b/apps/matrix/apps/web/src/lib/components/chat/Timeline.svelte index ce2d80c74..a4c26fc21 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/Timeline.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/Timeline.svelte @@ -2,7 +2,7 @@ import { matrixStore, type SimpleMessage } from '$lib/matrix'; import Message from './Message.svelte'; import TypingIndicator from './TypingIndicator.svelte'; - import { onMount, tick } from 'svelte'; + import { tick } from 'svelte'; import { CircleNotch, ArrowDown } from '@manacore/shared-icons'; interface Props { @@ -20,16 +20,46 @@ let showScrollButton = $state(false); let loadingMore = $state(false); let prevMessageCount = $state(0); + let hasInitiallyScrolled = $state(false); + let currentRoomId = $state(null); - // Auto-scroll to bottom on new messages (if already at bottom) + // Reset state when room changes + $effect(() => { + const roomId = matrixStore.currentRoomId; + if (roomId !== currentRoomId) { + currentRoomId = roomId; + hasInitiallyScrolled = false; + prevMessageCount = 0; + loadingMore = false; + showScrollButton = false; + } + }); + + // Initial scroll to bottom when messages first load, and auto-scroll on new messages $effect(() => { const messageCount = matrixStore.messages.length; - if (messageCount > prevMessageCount && container) { + + // Initial scroll when messages first appear for this room + if (messageCount > 0 && !hasInitiallyScrolled && container) { + tick().then(() => { + if (container) { + container.scrollTop = container.scrollHeight; + hasInitiallyScrolled = true; + prevMessageCount = messageCount; + } + }); + return; + } + + // Auto-scroll on new messages (if already at bottom) + if (messageCount > prevMessageCount && container && hasInitiallyScrolled) { const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 100; if (isAtBottom) { tick().then(() => { - container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); + if (container) { + container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); + } }); } } @@ -44,8 +74,13 @@ container.scrollHeight - container.scrollTop - container.clientHeight; showScrollButton = distanceFromBottom > 200; - // Load more when scrolled to top - if (container.scrollTop < 100 && !loadingMore) { + // Load more when scrolled to top (only after initial scroll and with messages present) + if ( + container.scrollTop < 100 && + !loadingMore && + hasInitiallyScrolled && + matrixStore.messages.length > 0 + ) { loadMore(); } } @@ -59,10 +94,11 @@ await matrixStore.loadMoreMessages(50); // Maintain scroll position after loading - tick().then(() => { + await tick(); + if (container) { const newScrollHeight = container.scrollHeight; container.scrollTop = newScrollHeight - prevScrollHeight; - }); + } loadingMore = false; } @@ -70,13 +106,6 @@ function scrollToBottom() { container?.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); } - - onMount(() => { - // Scroll to bottom on mount - if (container) { - container.scrollTop = container.scrollHeight; - } - });
@@ -93,16 +122,24 @@ {/if} -
+
{#each matrixStore.messages as message, index (message.id)} {@const prevMessage = matrixStore.messages[index - 1]} - {@const showAvatar = !prevMessage || prevMessage.sender !== message.sender} - {@const showTimestamp = - !prevMessage || message.timestamp - prevMessage.timestamp > 5 * 60 * 1000} + {@const nextMessage = matrixStore.messages[index + 1]} + {@const isSameSender = Boolean(prevMessage && prevMessage.sender === message.sender)} + {@const isNextSameSender = Boolean(nextMessage && nextMessage.sender === message.sender)} + {@const prevDate = prevMessage ? new Date(prevMessage.timestamp).toDateString() : null} + {@const currentDate = new Date(message.timestamp).toDateString()} + {@const nextDate = nextMessage ? new Date(nextMessage.timestamp).toDateString() : null} + {@const showDateSeparator = Boolean(prevMessage && prevDate !== currentDate)} + {@const showAvatar = !isSameSender || showDateSeparator} + {@const isLastInGroup = !isNextSameSender || Boolean(nextDate && nextDate !== currentDate)}