fix(direct): exclude bridged portal rooms from Direct tab so Telegram chats live exclusively in their per-bridge personal filtering space

This commit is contained in:
heaven 2026-05-16 00:31:38 +03:00
parent bfd72dc1ff
commit c78984a6d8
2 changed files with 21 additions and 6 deletions

View file

@ -2,7 +2,7 @@ import { MatrixClient, Room, RoomEvent } from 'matrix-js-sdk';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AccountDataEvent } from '../../types/matrix/accountData'; import { AccountDataEvent } from '../../types/matrix/accountData';
import { Membership } from '../../types/matrix/room'; import { Membership } from '../../types/matrix/room';
import { getAccountData, getMDirects } from '../utils/room'; import { getAccountData, getMDirects, isBridgedRoom } from '../utils/room';
import { addRoomIdToMDirect } from '../utils/matrix'; import { addRoomIdToMDirect } from '../utils/matrix';
export function useAutoDirectSync(mx: MatrixClient): void { export function useAutoDirectSync(mx: MatrixClient): void {
@ -17,6 +17,13 @@ export function useAutoDirectSync(mx: MatrixClient): void {
const joinedAndInvited = room.getJoinedMemberCount() + room.getInvitedMemberCount(); const joinedAndInvited = room.getJoinedMemberCount() + room.getInvitedMemberCount();
if (joinedAndInvited > 2) return; if (joinedAndInvited > 2) return;
// Bridged portal rooms (mautrix-telegram/whatsapp/discord/…) live in the
// per-bridge personal filtering space (Channels tab) — don't pollute
// `m.direct` with them. `useDirectRooms` already filters bridged rooms
// out of the Direct tab visually; this prevents the underlying account
// data from accruing stale entries for other clients to misclassify.
if (isBridgedRoom(room)) return;
const mDirectEvent = getAccountData(mx, AccountDataEvent.Direct); const mDirectEvent = getAccountData(mx, AccountDataEvent.Direct);
if (mDirectEvent) { if (mDirectEvent) {
const directs = getMDirects(mDirectEvent); const directs = getMDirects(mDirectEvent);

View file

@ -8,12 +8,17 @@ import { useDirects, useOrphanRooms } from '../../../state/hooks/roomList';
import { roomToParentsAtom } from '../../../state/room/roomToParents'; import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { useBotPresets } from '../../../features/bots/catalog'; import { useBotPresets } from '../../../features/bots/catalog';
import { isBridgeStateEvent, isCatalogBotControlRoom } from '../../../features/bots/room'; import { isBridgeStateEvent, isCatalogBotControlRoom } from '../../../features/bots/room';
import { isBridgedRoom } from '../../../utils/room';
// After P3c the Direct tab is universal: every joined non-space «orphan» // After P3c the Direct tab is universal: every joined non-space «orphan»
// room renders here, regardless of `m.direct`, except curated bot control DMs. // room renders here, regardless of `m.direct`, except curated bot control DMs
// Those belong to the Robots tab, while bridged Telegram portal rooms remain // (Robots tab) and bridged portal rooms (mautrix-telegram/whatsapp/discord/…).
// normal Direct candidates. Implementation = // Bridged rooms are filtered out here so they live exclusively in their
// `useOrphanRooms useDirects`: // per-bridge personal filtering space (Channels tab) — otherwise a 1:1
// Telegram chat that the bridge tagged `m.direct` AND placed inside the
// Telegram space would appear twice. Implementation =
// `useOrphanRooms useDirects`, minus catalog bot control rooms and bridged
// portals:
// //
// - `useOrphanRooms` = `isRoom && !mDirects.has && !roomToParents.has` → // - `useOrphanRooms` = `isRoom && !mDirects.has && !roomToParents.has` →
// non-space rooms that don't live inside any space and aren't m.direct- // non-space rooms that don't live inside any space and aren't m.direct-
@ -79,7 +84,10 @@ export const useDirectRooms = (): string[] => {
const out: string[] = []; const out: string[] = [];
const isVisibleDirect = (id: string): boolean => { const isVisibleDirect = (id: string): boolean => {
const room = mx.getRoom(id); const room = mx.getRoom(id);
return !room || !isCatalogBotControlRoom(mx, room, bots); if (!room) return true;
if (isCatalogBotControlRoom(mx, room, bots)) return false;
if (isBridgedRoom(room)) return false;
return true;
}; };
orphanRooms.forEach((id) => { orphanRooms.forEach((id) => {