From c9f3d8ae47e744a454fcff6d68b3a3a2c18a3094 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Jan 2026 23:54:24 +0000 Subject: [PATCH] feat(matrix): add Phase 2 features File Upload & Media: - Send images, videos, audio, and files - Display media in timeline with thumbnails - Download links for files - Upload progress indicator Message Actions: - Reply to messages with quote preview - Edit own text messages - Delete (redact) own messages - React with emoji (store only, UI pending) Room Management: - Create room dialog (DM or group) - User search and invite - Room settings panel with member list - Leave room functionality UI Improvements: - German translations - Message hover actions - Reply/Edit preview in input - Date separators in timeline https://claude.ai/code/session_01RUrt2qN1D3nVh9HcGpwoby --- .../components/chat/CreateRoomDialog.svelte | 285 +++++++++++++++++ .../src/lib/components/chat/Message.svelte | 169 +++++++++- .../lib/components/chat/MessageInput.svelte | 254 ++++++++++++--- .../src/lib/components/chat/RoomHeader.svelte | 5 +- .../src/lib/components/chat/RoomList.svelte | 10 +- .../components/chat/RoomSettingsPanel.svelte | 225 +++++++++++++ .../src/lib/components/chat/Timeline.svelte | 11 +- .../apps/web/src/lib/components/chat/index.ts | 2 + .../apps/web/src/lib/matrix/store.svelte.ts | 298 +++++++++++++++++- apps/matrix/apps/web/src/lib/matrix/types.ts | 17 + .../web/src/routes/(app)/chat/+page.svelte | 119 +++++-- 11 files changed, 1304 insertions(+), 91 deletions(-) create mode 100644 apps/matrix/apps/web/src/lib/components/chat/CreateRoomDialog.svelte create mode 100644 apps/matrix/apps/web/src/lib/components/chat/RoomSettingsPanel.svelte diff --git a/apps/matrix/apps/web/src/lib/components/chat/CreateRoomDialog.svelte b/apps/matrix/apps/web/src/lib/components/chat/CreateRoomDialog.svelte new file mode 100644 index 000000000..ea6411966 --- /dev/null +++ b/apps/matrix/apps/web/src/lib/components/chat/CreateRoomDialog.svelte @@ -0,0 +1,285 @@ + + +{#if open} + +
+ +
e.stopPropagation()} + role="dialog" + aria-modal="true" + > + +
+

Neuer Chat

+ +
+ + +
+ +
+ + +
+ + + {#if !isDirect} +
+ + +
+ +
+ + +
+ + +
+ +

+ {isPrivate + ? 'Nur eingeladene Benutzer können beitreten' + : 'Jeder kann diesen Raum finden und beitreten'} +

+
+ {/if} + + +
+ +
+ + {#if searching} + + {/if} +
+ + + {#if searchResults.length > 0} + + {/if} +
+ + + {#if selectedUsers.length > 0} +
+ {#each selectedUsers as user} + + {user.displayName || user.userId} + + + {/each} +
+ {/if} + + + {#if error} +
+ {error} +
+ {/if} +
+ + +
+ + +
+
+
+{/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 34570f18f..e6a180563 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte @@ -1,15 +1,32 @@ @@ -40,7 +85,14 @@ {/if} -
+
(showActions = true)} + onmouseleave={() => (showActions = false)} +>
{#if showAvatar && !message.isOwn} @@ -66,9 +118,89 @@
{/if} + + {#if message.replyTo && message.replyToBody} +
+ + {message.replyToBody} +
+ {/if} +
- {#if message.type === 'm.emote'} + {#if message.redacted} +

Nachricht wurde gelöscht

+ {:else if message.type === 'm.image' && thumbnailUrl} + +
+ {#if imageLoading} +
+ +
+ {/if} + {#if imageError} +
+

Bild konnte nicht geladen werden

+
+ {:else} + {message.body} (imageLoading = false)} + onerror={() => { + imageLoading = false; + imageError = true; + }} + onclick={() => mediaUrl && window.open(mediaUrl, '_blank')} + /> + {/if} +
+ {:else if message.type === 'm.video' && thumbnailUrl} + +
+
+ {message.body} +
+ +
+
+ {#if message.media?.duration} + + {Math.floor(message.media.duration / 60)}:{(message.media.duration % 60) + .toString() + .padStart(2, '0')} + + {/if} +
+ {:else if message.type === 'm.file' || message.type === 'm.audio'} + + +
+ +
+
+

{message.media?.filename || message.body}

+

+ {formatFileSize(message.media?.size)} + {#if message.media?.mimetype} + • {message.media.mimetype.split('/')[1]?.toUpperCase()} + {/if} +

+
+ +
+ {:else if message.type === 'm.emote'}

* {message.senderName} {message.body}

{:else if message.type === 'm.notice'}

{message.body}

@@ -78,12 +210,37 @@ {#if !showAvatar} -
+ + + {#if showActions && !message.redacted} +
+ + {#if message.isOwn && message.type === 'm.text'} + + {/if} + {#if message.isOwn} + + {/if} +
+ {/if}
diff --git a/apps/matrix/apps/web/src/lib/components/chat/MessageInput.svelte b/apps/matrix/apps/web/src/lib/components/chat/MessageInput.svelte index dc643bae0..aa80ff2c0 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/MessageInput.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/MessageInput.svelte @@ -1,18 +1,56 @@ -
-
- - - - -
- - - +
+ + {#if replyTo || editMessage} +
+
+ {#if editMessage} +

Nachricht bearbeiten

+

{editMessage.body}

+ {:else if replyTo} +

+ Antwort auf {replyTo.senderName} +

+

{replyTo.body}

+ {/if} +
+
+ {/if} + + + {#if uploading} +
+ +
+
+
+
+
+ {uploadProgress}% +
+ {/if} + + +
+
+ + + + + + + +
+ + + + +
+ + +
- - + +

+ {#if editMessage} + Enter zum Speichern, Escape zum Abbrechen + {:else} + Enter zum Senden, Shift+Enter für neue Zeile + {/if} +

- - -

- Press Enter to send, Shift+Enter for new line -

diff --git a/apps/matrix/apps/web/src/lib/components/chat/RoomHeader.svelte b/apps/matrix/apps/web/src/lib/components/chat/RoomHeader.svelte index 972e3f722..7d33a4b8e 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/RoomHeader.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/RoomHeader.svelte @@ -4,9 +4,10 @@ interface Props { onMenuClick?: () => void; + onInfoClick?: () => void; } - let { onMenuClick }: Props = $props(); + let { onMenuClick, onInfoClick }: Props = $props(); let room = $derived(matrixStore.currentSimpleRoom); @@ -57,7 +58,7 @@ -
diff --git a/apps/matrix/apps/web/src/lib/components/chat/RoomList.svelte b/apps/matrix/apps/web/src/lib/components/chat/RoomList.svelte index 1e0d34239..1d63931ca 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/RoomList.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/RoomList.svelte @@ -3,6 +3,12 @@ import RoomItem from './RoomItem.svelte'; import { Search, Plus, Users, MessageCircle } from 'lucide-svelte'; + interface Props { + onCreateRoom?: () => void; + } + + let { onCreateRoom }: Props = $props(); + let search = $state(''); let showDMs = $state(true); @@ -68,9 +74,9 @@
-
diff --git a/apps/matrix/apps/web/src/lib/components/chat/RoomSettingsPanel.svelte b/apps/matrix/apps/web/src/lib/components/chat/RoomSettingsPanel.svelte new file mode 100644 index 000000000..97608febb --- /dev/null +++ b/apps/matrix/apps/web/src/lib/components/chat/RoomSettingsPanel.svelte @@ -0,0 +1,225 @@ + + +{#if open && room} + +
+ +
+

Raum-Details

+ +
+ + +
+
+
+ {#if room.avatar} + {room.name} + {:else} + {room.name.charAt(0).toUpperCase()} + {/if} +
+
+

{room.name}

+ {#if room.topic} +

{room.topic}

+ {/if} +

+ {room.memberCount} Mitglieder + {#if room.isEncrypted} + • Verschlüsselt + {/if} +

+
+ + +
+ + +
+ + +
+ {#if activeTab === 'members'} + +
+
+ + {#if searching} + + {/if} +
+ + + {#if searchResults.length > 0} + + {/if} +
+ + + + {:else} + +
+ + + + + +
+ {/if} +
+
+{/if} 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 b24325e21..eb04b363a 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/Timeline.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/Timeline.svelte @@ -1,10 +1,17 @@
@@ -29,27 +51,36 @@

Mana Matrix

- @@ -58,13 +89,16 @@

{matrixStore.userId}

- {matrixStore.syncState === 'SYNCING' ? 'Connected' : matrixStore.syncState} + {matrixStore.syncState === 'SYNCING' ? 'Verbunden' : matrixStore.syncState} + {#if matrixStore.totalUnreadCount > 0} + {matrixStore.totalUnreadCount} + {/if}

- + (showCreateRoom = true)} />
@@ -72,34 +106,57 @@
{#if matrixStore.currentRoom} - + (showRoomSettings = true)} + /> - + - + (replyTo = null)} + onCancelEdit={() => (editMessage = null)} + /> {:else}
-

Welcome to Mana Matrix

-

Select a conversation from the sidebar to start chatting

+

Willkommen bei Mana Matrix

+

Wähle eine Unterhaltung aus der Seitenleiste oder starte einen neuen Chat

+ +

{matrixStore.rooms.length}

-

Rooms

+

Räume

{matrixStore.totalUnreadCount}

-

Unread

+

Ungelesen

{/if}
+ + + (showRoomSettings = false)} /> + + + (showCreateRoom = false)} + onCreated={handleRoomCreated} +/>