fix(threads): drop the fallback root quote on every thread reply; add Threads + Widgets to the mobile room menu (#165, #211)
Inside the thread panel each reply rendered a 'Thread ↩ <root author> <root text…>' quote because the spec's fallback reply relation (is_falling_back + m.in_reply_to root) was treated like a real reply — noise on every row when the root is already pinned at the top. Genuine reply-to-a-reply quotes are kept. The Threads list and Widgets panel had desktop-only header buttons and no way to open them on a phone; both are now in the mobile ⋮ menu next to Members / Media Gallery. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -107,6 +107,10 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(
|
||||
const [reportRoomOpen, setReportRoomOpen] = useState(false);
|
||||
const [bookmarksOpen, setBookmarksOpen] = useAtom(bookmarksPanelAtom);
|
||||
const [mobileMembers, setMobileMembers] = useAtom(mobileMembersPanelAtom);
|
||||
// [Gitea #165 / #211] Threads list and Widgets had desktop-only header
|
||||
// buttons and no mobile entry point at all.
|
||||
const [threadsList, setThreadsList] = useAtom(threadsListAtom);
|
||||
const [widgets, setWidgets] = useAtom(widgetsPanelAtom);
|
||||
|
||||
const handleMarkAsRead = () => {
|
||||
markAsRead(mx, room.roomId, hideActivity);
|
||||
@@ -226,6 +230,38 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(
|
||||
</Text>
|
||||
</MenuItem>
|
||||
)}
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setThreadsList(!threadsList);
|
||||
requestClose();
|
||||
}}
|
||||
size="300"
|
||||
after={<Icon size="100" src={Icons.Thread} filled={threadsList} />}
|
||||
radii="300"
|
||||
aria-pressed={threadsList}
|
||||
>
|
||||
<Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
|
||||
Threads
|
||||
</Text>
|
||||
</MenuItem>
|
||||
)}
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setWidgets(!widgets);
|
||||
requestClose();
|
||||
}}
|
||||
size="300"
|
||||
after={<Icon size="100" src={Icons.Category} filled={widgets} />}
|
||||
radii="300"
|
||||
aria-pressed={widgets}
|
||||
>
|
||||
<Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
|
||||
Widgets
|
||||
</Text>
|
||||
</MenuItem>
|
||||
)}
|
||||
{!isServerNotice && (
|
||||
<MenuItem
|
||||
onClick={handleInvite}
|
||||
|
||||
@@ -734,8 +734,20 @@ export function ThreadTimeline({ room, thread, editor }: ThreadTimelineProps) {
|
||||
const reactionRelations = getEventReactions(timelineSet, mEventId);
|
||||
const reactions = reactionRelations?.getSortedAnnotationsByKey();
|
||||
const hasReactions = !!reactions && reactions.length > 0;
|
||||
const { replyEventId, threadRootId } = mEvent;
|
||||
const { replyEventId } = mEvent;
|
||||
const forwardedMeta = getForwardedMeta(mEvent.getContent());
|
||||
// Inside the panel every reply carries the spec's *fallback* reply
|
||||
// relation to the thread root (`is_falling_back: true`) so non-threaded
|
||||
// clients can render it as a reply. Here the root is already at the top,
|
||||
// so that quote — and the "Thread" chip — is pure noise on every row
|
||||
// (Gitea #165). Only a genuine reply-to-a-reply keeps its quote.
|
||||
const relation = mEvent.getWireContent()?.['m.relates_to'] as
|
||||
| { is_falling_back?: boolean }
|
||||
| undefined;
|
||||
const genuineReplyId =
|
||||
replyEventId && !(relation?.is_falling_back && replyEventId === thread.id)
|
||||
? replyEventId
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<Message
|
||||
@@ -770,12 +782,11 @@ export function ThreadTimeline({ room, thread, editor }: ThreadTimelineProps) {
|
||||
onJump={() => navigateRoom(forwardedMeta.room_id, forwardedMeta.event_id)}
|
||||
/>
|
||||
)}
|
||||
{replyEventId && (
|
||||
{genuineReplyId && (
|
||||
<Reply
|
||||
room={room}
|
||||
timelineSet={timelineSet}
|
||||
replyEventId={replyEventId}
|
||||
threadRootId={threadRootId}
|
||||
replyEventId={genuineReplyId}
|
||||
onClick={handleOpenReply}
|
||||
getMemberPowerTag={getMemberPowerTag}
|
||||
accessibleTagColors={accessiblePowerTagColors}
|
||||
@@ -811,6 +822,7 @@ export function ThreadTimeline({ room, thread, editor }: ThreadTimelineProps) {
|
||||
},
|
||||
[
|
||||
navigateRoom,
|
||||
thread.id,
|
||||
room,
|
||||
messageSpacing,
|
||||
messageLayout,
|
||||
|
||||
Reference in New Issue
Block a user