From f00e20b1accd769b1fa6f0bda299a046ce60aa1c Mon Sep 17 00:00:00 2001 From: Litruv Date: Sat, 21 Feb 2026 22:21:56 +1100 Subject: [PATCH] feat: enhance room data retrieval with server domain extraction --- .../autocomplete/RoomMentionAutocomplete.tsx | 53 +++++++++++++++++-- src/app/paarrot-api.ts | 24 ++++++--- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx index b0c64f6..4a2b66f 100644 --- a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx @@ -5,7 +5,7 @@ import { JoinRule, MatrixClient } from 'matrix-js-sdk'; import { useAtomValue } from 'jotai'; import { createMentionElement, moveCursor, replaceWithElement } from '../utils'; -import { getDirectRoomAvatarUrl } from '../../../utils/room'; +import { getDirectRoomAvatarUrl, guessPerfectParent } from '../../../utils/room'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { AutocompleteQuery } from './autocompleteQuery'; import { AutocompleteMenu } from './AutocompleteMenu'; @@ -18,6 +18,7 @@ import { allRoomsAtom } from '../../../state/room-list/roomList'; import { factoryRoomIdByActivity } from '../../../utils/sort'; import { RoomAvatar, RoomIcon } from '../../room-avatar'; import { getViaServers } from '../../../plugins/via-servers'; +import { roomToParentsAtom } from '../../../state/room/roomToParents'; type MentionAutoCompleteHandler = (roomAliasOrId: string, name: string) => void; @@ -78,6 +79,7 @@ export function RoomMentionAutocomplete({ }: RoomMentionAutocompleteProps) { const mx = useMatrixClient(); const mDirects = useAtomValue(mDirectAtom); + const roomToParents = useAtomValue(roomToParentsAtom); const allRooms = useAtomValue(allRoomsAtom).sort(factoryRoomIdByActivity(mx)); @@ -103,6 +105,42 @@ export function RoomMentionAutocomplete({ else resetSearch(); }, [query.text, search, resetSearch]); + // Build space hierarchy path for a room + const getSpacePath = useCallback((rId: string): string | null => { + // Skip DMs + if (mDirects.has(rId)) return null; + + const buildParentChain = (currentRoomId: string): string[] => { + const parents = roomToParents.get(currentRoomId); + if (!parents || parents.size === 0) { + return []; + } + + const parentsList = Array.from(parents); + let chosenParent: string; + + if (parentsList.length === 1) { + chosenParent = parentsList[0]; + } else { + const orphanParents = parentsList.filter(p => !roomToParents.has(p)); + if (orphanParents.length > 0) { + chosenParent = guessPerfectParent(mx, currentRoomId, orphanParents) ?? orphanParents[0]; + } else { + chosenParent = parentsList[0]; + } + } + + const parentRoom = mx.getRoom(chosenParent); + if (!parentRoom?.name) return []; + + const upperChain = buildParentChain(chosenParent); + return [...upperChain, parentRoom.name]; + }; + + const parentChain = buildParentChain(rId); + return parentChain.length > 0 ? parentChain.join(' > ') : null; + }, [mx, mDirects, roomToParents]); + const handleAutocomplete: MentionAutoCompleteHandler = (roomAliasOrId, name) => { const mentionRoom = mx.getRoom(roomAliasOrId); const viaServers = mentionRoom ? getViaServers(mentionRoom) : undefined; @@ -144,6 +182,11 @@ export function RoomMentionAutocomplete({ const handleSelect = () => handleAutocomplete(room.getCanonicalAlias() ?? rId, room.name); + // Get space hierarchy path or canonical alias or empty string + const spacePath = getSpacePath(rId); + const alias = room.getCanonicalAlias(); + const secondaryText = spacePath || alias || ''; + return ( - {room.getCanonicalAlias() ?? ''} - + secondaryText && ( + + {secondaryText} + + ) } before={ diff --git a/src/app/paarrot-api.ts b/src/app/paarrot-api.ts index 95966d7..59afbd1 100644 --- a/src/app/paarrot-api.ts +++ b/src/app/paarrot-api.ts @@ -305,12 +305,19 @@ async function getChannels(matrixClient: any) { return rooms .filter((room: any) => !room.isSpaceRoom()) - .map((room: any) => ({ - roomId: room.roomId, - name: room.name || 'Unnamed Room', - isDirect: room.getMyMembership() === 'invite' ? false : room.guessDMUserId() !== null, - avatar: room.getMxcAvatarUrl() || null, - })) + .map((room: any) => { + // Extract server domain from roomId (!localpart:domain.tld) + const serverMatch = room.roomId.match(/:(.+)$/); + const server = serverMatch ? serverMatch[1] : null; + + return { + roomId: room.roomId, + name: room.name || 'Unnamed Room', + server: server, + isDirect: room.getMyMembership() === 'invite' ? false : room.guessDMUserId() !== null, + avatar: room.getMxcAvatarUrl() || null, + }; + }) .sort((a: any, b: any) => a.name.localeCompare(b.name)); } @@ -362,9 +369,14 @@ async function getCurrentRoom(matrixClient: any) { throw new Error(`Current room not found: ${currentRoomId}`); } + // Extract server domain from roomId (!localpart:domain.tld) + const serverMatch = room.roomId.match(/:(.+)$/); + const server = serverMatch ? serverMatch[1] : null; + return { roomId: room.roomId, name: room.name || 'Unnamed Room', + server: server, avatar: room.getMxcAvatarUrl() || null, isDirect: room.guessDMUserId() !== null, };