From 7b2ac780324a12325ca168c6211aaaecee2107ce Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Thu, 29 Jan 2026 22:54:00 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(matrix-web):=20add=20@mention?= =?UTF-8?q?=20autocomplete,=20message=20forwarding,=20and=20improved=20typ?= =?UTF-8?q?ing=20indicator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @mention autocomplete: - Detect @ symbol while typing and show user picker - Search room members by name or user ID - Arrow key navigation and Enter/Tab to select - Insert display name into message Message forwarding: - Add forward button to message actions - ForwardMessageDialog with room selection - Multi-select support for forwarding to multiple rooms - Search and filter rooms Typing indicator improvements: - Show user avatars (stacked, up to 3) - Improved visual design with rounded pill for dots - Better spacing and alignment Also adds: - sendMessageToRoom() store method for forwarding --- .../chat/ForwardMessageDialog.svelte | 173 ++++++++++++++++++ .../src/lib/components/chat/Message.svelte | 10 + .../lib/components/chat/MessageInput.svelte | 143 ++++++++++++++- .../src/lib/components/chat/Timeline.svelte | 4 +- .../components/chat/TypingIndicator.svelte | 55 +++++- .../apps/web/src/lib/matrix/store.svelte.ts | 15 ++ .../web/src/routes/(app)/chat/+page.svelte | 22 ++- 7 files changed, 408 insertions(+), 14 deletions(-) create mode 100644 apps/matrix/apps/web/src/lib/components/chat/ForwardMessageDialog.svelte diff --git a/apps/matrix/apps/web/src/lib/components/chat/ForwardMessageDialog.svelte b/apps/matrix/apps/web/src/lib/components/chat/ForwardMessageDialog.svelte new file mode 100644 index 000000000..d473f4df7 --- /dev/null +++ b/apps/matrix/apps/web/src/lib/components/chat/ForwardMessageDialog.svelte @@ -0,0 +1,173 @@ + + +{#if open && message} + + +{/if} diff --git a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte index e83b59b38..edcebda8c 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte @@ -5,6 +5,7 @@ import { de } from 'date-fns/locale'; import { ArrowBendUpLeft, + ArrowBendUpRight, PencilSimple, Trash, DotsThree, @@ -27,6 +28,7 @@ showEncryptionBadge?: boolean; onReply?: (message: SimpleMessage) => void; onEdit?: (message: SimpleMessage) => void; + onForward?: (message: SimpleMessage) => void; } let { @@ -36,6 +38,7 @@ showEncryptionBadge = false, onReply, onEdit, + onForward, }: Props = $props(); // Check if message is a decryption error (body starts with "Unable to decrypt:") @@ -623,6 +626,13 @@ > + {#if message.isOwn && message.type === 'm.text'} + {/each} + + {/if} +
void; onEdit?: (message: SimpleMessage) => void; + onForward?: (message: SimpleMessage) => void; } - let { onReply, onEdit }: Props = $props(); + let { onReply, onEdit, onForward }: Props = $props(); // Check if current room is encrypted let isRoomEncrypted = $derived(matrixStore.currentSimpleRoom?.isEncrypted ?? false); @@ -105,6 +106,7 @@ showEncryptionBadge={isRoomEncrypted} {onReply} {onEdit} + {onForward} /> {:else}
diff --git a/apps/matrix/apps/web/src/lib/components/chat/TypingIndicator.svelte b/apps/matrix/apps/web/src/lib/components/chat/TypingIndicator.svelte index 40cebb2c5..4b34ba135 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/TypingIndicator.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/TypingIndicator.svelte @@ -1,10 +1,25 @@ {#if users.length > 0} -
+
+ +
+ {#each typingUsers().slice(0, 3) as user, i} + {#if user.avatarUrl} + {user.name} + {:else} +
+ +
+ {/if} + {/each} +
+ - - - - - - {text()} +
+ + + +
+ + + {text()}
{/if} diff --git a/apps/matrix/apps/web/src/lib/matrix/store.svelte.ts b/apps/matrix/apps/web/src/lib/matrix/store.svelte.ts index 150795d6c..be806daf8 100644 --- a/apps/matrix/apps/web/src/lib/matrix/store.svelte.ts +++ b/apps/matrix/apps/web/src/lib/matrix/store.svelte.ts @@ -569,6 +569,21 @@ class MatrixStore { } } + /** + * Send a message to a specific room (for forwarding) + */ + async sendMessageToRoom(roomId: string, body: string): Promise { + if (!this._client) return false; + + try { + await this._client.sendTextMessage(roomId, body); + return true; + } catch (err) { + this._error = err instanceof Error ? err.message : 'Failed to send message'; + return false; + } + } + /** * Send typing indicator */ diff --git a/apps/matrix/apps/web/src/routes/(app)/chat/+page.svelte b/apps/matrix/apps/web/src/routes/(app)/chat/+page.svelte index 17778ee78..917aef49a 100644 --- a/apps/matrix/apps/web/src/routes/(app)/chat/+page.svelte +++ b/apps/matrix/apps/web/src/routes/(app)/chat/+page.svelte @@ -4,6 +4,7 @@ import CreateRoomDialog from '$lib/components/chat/CreateRoomDialog.svelte'; import RoomSettingsPanel from '$lib/components/chat/RoomSettingsPanel.svelte'; import SearchDialog from '$lib/components/chat/SearchDialog.svelte'; + import ForwardMessageDialog from '$lib/components/chat/ForwardMessageDialog.svelte'; import { CallView, IncomingCallDialog } from '$lib/components/call'; import { ChatCircle, Plus, Gear } from '@manacore/shared-icons'; import { browser } from '$app/environment'; @@ -17,10 +18,12 @@ let showCreateRoom = $state(false); let showRoomSettings = $state(false); let showSearch = $state(false); + let showForward = $state(false); - // Reply/Edit state + // Reply/Edit/Forward state let replyTo = $state(null); let editMessage = $state(null); + let forwardMessage = $state(null); // Check if mobile let isMobile = $state(browser ? window.innerWidth < 1024 : false); @@ -71,6 +74,11 @@ editMessage = message; } + function handleForward(message: SimpleMessage) { + forwardMessage = message; + showForward = true; + } + function handleRoomCreated(roomId: string) { matrixStore.selectRoom(roomId); } @@ -174,7 +182,7 @@ /> - + {/if} + + + { + showForward = false; + forwardMessage = null; + }} +/>