From e81d24e0ea0c03545129aaeed784433af205bf3e Mon Sep 17 00:00:00 2001 From: 7w1 Date: Mon, 16 Mar 2026 23:32:00 -0500 Subject: [PATCH 1/5] fix colors and threads --- .changeset/fix-color-parser.md | 5 +++++ .changeset/fix-thread-replies.md | 5 +++++ src/app/features/room/ThreadDrawer.tsx | 10 ++++++++-- src/app/hooks/useUserProfile.ts | 3 ++- 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 .changeset/fix-color-parser.md create mode 100644 .changeset/fix-thread-replies.md diff --git a/.changeset/fix-color-parser.md b/.changeset/fix-color-parser.md new file mode 100644 index 000000000..0abdd8938 --- /dev/null +++ b/.changeset/fix-color-parser.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix opacity rendering in name colors. diff --git a/.changeset/fix-thread-replies.md b/.changeset/fix-thread-replies.md new file mode 100644 index 000000000..d10cf9d9f --- /dev/null +++ b/.changeset/fix-thread-replies.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix threads rendering fallback replies. diff --git a/src/app/features/room/ThreadDrawer.tsx b/src/app/features/room/ThreadDrawer.tsx index fd7075bfd..fb32d3bba 100644 --- a/src/app/features/room/ThreadDrawer.tsx +++ b/src/app/features/room/ThreadDrawer.tsx @@ -482,7 +482,12 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra const thread = room.getThread(threadRootId); const fromThread = thread?.events ?? []; if (fromThread.length > 0) { - return fromThread.filter((ev) => ev.getId() !== threadRootId && !reactionOrEditEvent(ev)); + return fromThread.filter( + (ev) => + ev.getId() !== threadRootId && + !reactionOrEditEvent(ev) && + ev.getContent()?.['m.relates_to']?.is_falling_back !== true + ); } return room .getUnfilteredTimelineSet() @@ -492,7 +497,8 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra (ev) => ev.threadRootId === threadRootId && ev.getId() !== threadRootId && - !reactionOrEditEvent(ev) + !reactionOrEditEvent(ev) && + ev.getContent()?.['m.relates_to']?.is_falling_back !== true ); })(); diff --git a/src/app/hooks/useUserProfile.ts b/src/app/hooks/useUserProfile.ts index 22ffa5b2a..8de5da050 100644 --- a/src/app/hooks/useUserProfile.ts +++ b/src/app/hooks/useUserProfile.ts @@ -69,7 +69,8 @@ const isValidHex = (c: any): string | undefined => { if (typeof c !== 'string') return undefined; // silly tuwunel smh const cleaned = c.replaceAll(/["']/g, '').trim(); - return /^#([0-9A-F]{3,6})$/i.test(cleaned) ? cleaned : undefined; + // Strictly allow only 3 or 6 digit hex codes, aka no opacity + return /^#([0-9A-F]{3}|[0-9A-F]{6})$/i.test(cleaned) ? cleaned : undefined; }; const sanitizeFont = (f: string) => f.replaceAll(/[;{}<>]/g, '').slice(0, 32); From 0f10e19e85b0dfb59a550bf7e4afd682f79d1026 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Mon, 16 Mar 2026 23:34:53 -0500 Subject: [PATCH 2/5] revert my stupidity --- src/app/features/room/ThreadDrawer.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/app/features/room/ThreadDrawer.tsx b/src/app/features/room/ThreadDrawer.tsx index fb32d3bba..fd7075bfd 100644 --- a/src/app/features/room/ThreadDrawer.tsx +++ b/src/app/features/room/ThreadDrawer.tsx @@ -482,12 +482,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra const thread = room.getThread(threadRootId); const fromThread = thread?.events ?? []; if (fromThread.length > 0) { - return fromThread.filter( - (ev) => - ev.getId() !== threadRootId && - !reactionOrEditEvent(ev) && - ev.getContent()?.['m.relates_to']?.is_falling_back !== true - ); + return fromThread.filter((ev) => ev.getId() !== threadRootId && !reactionOrEditEvent(ev)); } return room .getUnfilteredTimelineSet() @@ -497,8 +492,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra (ev) => ev.threadRootId === threadRootId && ev.getId() !== threadRootId && - !reactionOrEditEvent(ev) && - ev.getContent()?.['m.relates_to']?.is_falling_back !== true + !reactionOrEditEvent(ev) ); })(); From 31850dc9dcd27e9e4a0944917c4640e818b0ceb9 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Mon, 16 Mar 2026 23:42:57 -0500 Subject: [PATCH 3/5] fix thread fallback --- src/app/features/room/ThreadDrawer.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/features/room/ThreadDrawer.tsx b/src/app/features/room/ThreadDrawer.tsx index fd7075bfd..00957bc34 100644 --- a/src/app/features/room/ThreadDrawer.tsx +++ b/src/app/features/room/ThreadDrawer.tsx @@ -180,6 +180,11 @@ function ThreadMessage({ const { replyEventId } = mEvent; + const relation = mEvent.getRelation(); + const contentRelatesTo = mEvent.getContent()?.['m.relates_to']; + const isFallback = + relation?.is_falling_back === true || contentRelatesTo?.is_falling_back === true; + return ( Date: Mon, 16 Mar 2026 23:53:42 -0500 Subject: [PATCH 4/5] i didn't test this :p --- .changeset/fix-delated-file-uploads.md | 5 ++ src/app/features/room/RoomInput.tsx | 88 ++++++++++++++++++++------ 2 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 .changeset/fix-delated-file-uploads.md diff --git a/.changeset/fix-delated-file-uploads.md b/.changeset/fix-delated-file-uploads.md new file mode 100644 index 000000000..c3d5f936d --- /dev/null +++ b/.changeset/fix-delated-file-uploads.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix sending scheduled file attachments. diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index ad9d63141..60ee8d20b 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -512,28 +512,74 @@ export const RoomInput = forwardRef( } } - await Promise.all( - contents.map((content) => - mx - .sendMessage(roomId, threadRootId ?? null, content as any) - .then((res) => { - debugLog.info('message', 'Uploaded file message sent', { - roomId, - eventId: res.event_id, - msgtype: content.msgtype, - }); - return res; - }) - .catch((error: unknown) => { - debugLog.error('message', 'Failed to send uploaded file message', { - roomId, - error: error instanceof Error ? error.message : String(error), - }); - log.error('failed to send uploaded message', { roomId }, error); - throw error; + const invalidate = () => + queryClient.invalidateQueries({ queryKey: ['delayedEvents', roomId] }); + + if (scheduledTime) { + try { + const delayMs = computeDelayMs(scheduledTime); + if (editingScheduledDelayId) { + await cancelDelayedEvent(mx, editingScheduledDelayId); + } + + await Promise.all( + contents.map((content) => { + if (isEncrypted) { + return sendDelayedMessageE2EE(mx, roomId, room, content, delayMs); + } + return sendDelayedMessage(mx, roomId, content, delayMs); }) - ) - ); + ); + + invalidate(); + setEditingScheduledDelayId(null); + setScheduledTime(null); + } catch (error) { + debugLog.error('message', 'Failed to schedule uploaded file message', { + roomId, + error: error instanceof Error ? error.message : String(error), + }); + log.error('failed to schedule uploaded message', { roomId }, error); + throw error; + } + } else { + if (editingScheduledDelayId) { + try { + await cancelDelayedEvent(mx, editingScheduledDelayId); + invalidate(); + setEditingScheduledDelayId(null); + } catch { + debugLog.error( + 'message', + 'Failed to cancel scheduled event before immediate file send', + { roomId } + ); + } + } + + await Promise.all( + contents.map((content) => + mx + .sendMessage(roomId, threadRootId ?? null, content as any) + .then((res) => { + debugLog.info('message', 'Uploaded file message sent', { + roomId, + eventId: res.event_id, + msgtype: content.msgtype, + }); + return res; + }) + .catch((error: unknown) => { + debugLog.error('message', 'Failed to send uploaded file message', { + roomId, + error: error instanceof Error ? error.message : String(error), + }); + log.error('failed to send uploaded message', { roomId }, error); + throw error; + }) + ) + ); + } }; const handleCloseAutocomplete = useCallback(() => { From add946378ac7176636d4554f4a4eee8ac29622cd Mon Sep 17 00:00:00 2001 From: 7w1 Date: Mon, 16 Mar 2026 23:57:45 -0500 Subject: [PATCH 5/5] fix replies new lines --- .changeset/fix-reply-rendering.md | 5 +++++ src/app/components/message/Reply.tsx | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/fix-reply-rendering.md diff --git a/.changeset/fix-reply-rendering.md b/.changeset/fix-reply-rendering.md new file mode 100644 index 000000000..413388960 --- /dev/null +++ b/.changeset/fix-reply-rendering.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix replies rendering new lines when messages have lists. diff --git a/src/app/components/message/Reply.tsx b/src/app/components/message/Reply.tsx index 2afd2aae3..1e047e9e9 100644 --- a/src/app/components/message/Reply.tsx +++ b/src/app/components/message/Reply.tsx @@ -122,6 +122,8 @@ export const Reply = as<'div', ReplyProps>( .replaceAll(//gi, ' ') .replaceAll(/<\/p>\s*]*>/gi, ' ') .replaceAll(/<\/?p[^>]*>/gi, '') + .replaceAll(/<\/li>\s*]*>/gi, ' ') + .replaceAll(/<\/?(ul|ol|li|blockquote|h[1-6]|pre|div)[^>]*>/gi, '') .replaceAll(/(?:\r\n|\r|\n)/g, ' '); const parserOpts = getReactCustomHtmlParser(mx, room.roomId, { linkifyOpts: LINKIFY_OPTS,