improve parent selection when opening a room (#2388)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Fri, 11 Jul 2025 11:03:55 +0000 (16:33 +0530)
committerGitHub <noreply@github.com>
Fri, 11 Jul 2025 11:03:55 +0000 (21:03 +1000)
when a room has more than one orphan parent, we will select parent which has highest number of special users who have special powers in selected room.

src/app/hooks/useRoomNavigate.ts
src/app/utils/room.ts

index e626c06bff74eda83d7d900e1c4699f4c5129190..b2d7a91a18163794422cda87a3fd3bd53d2d17e2 100644 (file)
@@ -9,7 +9,7 @@ import {
   getSpaceRoomPath,
 } from '../pages/pathUtils';
 import { useMatrixClient } from './useMatrixClient';
-import { getOrphanParents } from '../utils/room';
+import { getOrphanParents, guessPerfectParent } from '../utils/room';
 import { roomToParentsAtom } from '../state/room/roomToParents';
 import { mDirectAtom } from '../state/mDirectList';
 import { useSelectedSpace } from './router/useSelectedSpace';
@@ -39,19 +39,19 @@ export const useRoomNavigate = () => {
 
       const orphanParents = openSpaceTimeline ? [roomId] : getOrphanParents(roomToParents, roomId);
       if (orphanParents.length > 0) {
-        const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(
-          mx,
-          spaceSelectedId && orphanParents.includes(spaceSelectedId)
-            ? spaceSelectedId
-            : orphanParents[0] // TODO: better orphan parent selection.
-        );
-
-        if (openSpaceTimeline) {
-          navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomId, eventId), opts);
-          return;
+        let parentSpace: string;
+        if (spaceSelectedId && orphanParents.includes(spaceSelectedId)) {
+          parentSpace = spaceSelectedId;
+        } else {
+          parentSpace = guessPerfectParent(mx, roomId, orphanParents) ?? orphanParents[0];
         }
 
-        navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomIdOrAlias, eventId), opts);
+        const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(mx, parentSpace);
+
+        navigate(
+          getSpaceRoomPath(pSpaceIdOrAlias, openSpaceTimeline ? roomId : roomIdOrAlias, eventId),
+          opts
+        );
         return;
       }
 
index 79dcff9ec04f7032ce304b789f627d6a69b61f71..cae23514651ce86efb4e65275eb74aa6c0182531 100644 (file)
@@ -5,6 +5,7 @@ import {
   EventTimelineSet,
   EventType,
   IMentions,
+  IPowerLevelsContent,
   IPushRule,
   IPushRules,
   JoinRule,
@@ -473,3 +474,43 @@ export const bannedInRooms = (mx: MatrixClient, rooms: string[], otherUserId: st
     const banned = room.hasMembershipState(otherUserId, Membership.Ban);
     return banned;
   });
+
+export const guessPerfectParent = (
+  mx: MatrixClient,
+  roomId: string,
+  parents: string[]
+): string | undefined => {
+  if (parents.length === 1) {
+    return parents[0];
+  }
+
+  const getSpecialUsers = (rId: string): string[] => {
+    const r = mx.getRoom(rId);
+    const powerLevels =
+      r && getStateEvent(r, StateEvent.RoomPowerLevels)?.getContent<IPowerLevelsContent>();
+
+    const { users_default: usersDefault, users } = powerLevels ?? {};
+    if (typeof users !== 'object') return [];
+
+    const defaultPower = typeof usersDefault === 'number' ? usersDefault : 0;
+    return Object.keys(users).filter((userId) => users[userId] > defaultPower);
+  };
+
+  let perfectParent: string | undefined;
+  let score = 0;
+
+  const roomSpecialUsers = getSpecialUsers(roomId);
+  parents.forEach((parentId) => {
+    const parentSpecialUsers = getSpecialUsers(parentId);
+    const matchedUsersCount = parentSpecialUsers.filter((userId) =>
+      roomSpecialUsers.includes(userId)
+    ).length;
+
+    if (matchedUsersCount > score) {
+      score = matchedUsersCount;
+      perfectParent = parentId;
+    }
+  });
+
+  return perfectParent;
+};