Fix DM rooms showing as regular rooms for invited users by syncing m.direct on join and routing push navigation correctly.
This commit is contained in:
parent
311ec4e615
commit
84eeac93d8
7 changed files with 179 additions and 46 deletions
65
docs/ai/desired_features.md
Normal file
65
docs/ai/desired_features.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
1. Звонки по аудиосвязи в чатах
|
||||||
|
|
||||||
|
2. Понятное сообщение при регистрации на сервере с забаненным ником
|
||||||
|
|
||||||
|
3. Хочу скинуть ник-диплинк и чтобы сразу открылся сразу чат с юзером в приложении
|
||||||
|
|
||||||
|
4. Разобраться с текущими доменами и навести там порядок. Сейчас caddy заведен неправильно, web тоже не совсем правильно. Предложение
|
||||||
|
Тогда структура:
|
||||||
|
vojo.chat — красивый лендинг с описанием проекта, скриншоты, кнопка "Попробовать"
|
||||||
|
app.vojo.chat — сам мессенджер (Cinny)
|
||||||
|
docs.vojo.chat — документация
|
||||||
|
matrix.vojo.chat — Matrix API (технический поддомен, пользователи туда не ходят)
|
||||||
|
matrix-rtc.vojo.chat — LiveKit (технический)
|
||||||
|
|
||||||
|
5. Связанное ограничение по федерации (сейчас в env lk-jwt-service):
|
||||||
|
`LIVEKIT_FULL_ACCESS_HOMESERVERS: "vojo.chat"` — инициировать звонки могут только
|
||||||
|
юзеры `@…:vojo.chat`, федеративные — только join к существующим. При первых
|
||||||
|
реальных кросс-homeserver кейсах ослабить до `"*"` или whitelist (правка в env,
|
||||||
|
клиент не меняется).
|
||||||
|
|
||||||
|
6. Миграция unstable-префикса MSC4143 → stable, когда спека финализируется.
|
||||||
|
Сейчас `.well-known/matrix/client` объявляет RTC-фокусы под ключом
|
||||||
|
`org.matrix.msc4143.rtc_foci` (unstable prefix). Когда MSC4143 примут, ключ
|
||||||
|
станет `m.rtc_foci`. Что понадобится:
|
||||||
|
- В `~/vojo/caddy/Caddyfile` блок `.well-known/matrix/client` — добавить
|
||||||
|
оба ключа на переходный период, потом выпилить старый.
|
||||||
|
- В клиенте [src/app/hooks/useLivekitSupport.ts](src/app/hooks/useLivekitSupport.ts)
|
||||||
|
сейчас читается только `org.matrix.msc4143.rtc_foci` — расширить на оба
|
||||||
|
ключа (или дождаться matrix-js-sdk апдейта, который сделает это сам).
|
||||||
|
- Этот же паттерн относится к о всем msc-событиям в RTC-стеке: MSC4075
|
||||||
|
(`org.matrix.msc4075.rtc.notification`), MSC4195, MSC4310
|
||||||
|
(`org.matrix.msc4310.rtc.decline`). При stable-переходе спек все
|
||||||
|
префиксы поменяются синхронно — миграцию делать вместе.
|
||||||
|
|
||||||
|
7. Пофиксить follow страницы с чатами 1-1. Потому что сейчас они не автоскролятся вниз. Происходит это на нативе когда открываешь клавиатуру (она не сдвигает ui вверх как это делают обычные приложения с клавиатурой, по ощущениям просто перекрывает часть страницы сдвигая просто форму с вводом)
|
||||||
|
|
||||||
|
8. **(из Phase 2 DM-звонков, 2026-04-19)** Верифицировать на сервере: шифрует ли
|
||||||
|
Element Call widget **сам** `m.rtc.notification` при отправке в encrypted room
|
||||||
|
(ring от A → B). Теоретически SDK шифрует любой `sendEvent` в encrypted room
|
||||||
|
прозрачно, и widget должен идти через тот же путь — но `sendRtcDecline`
|
||||||
|
(наш, не widget) делегирует в `client.sendEvent` и это проверено
|
||||||
|
([client.js:2457](../../node_modules/matrix-js-sdk/lib/client.js#L2457)),
|
||||||
|
а `m.rtc.notification` widget шлёт через свой postMessage канал — не
|
||||||
|
перепроверяли. Как проверить: в encrypted DM у A нажать startCall,
|
||||||
|
в DevTools Network tab смотреть PUT на `/rooms/{roomId}/send/...` — body
|
||||||
|
должно быть encrypted (ключи `algorithm`, `ciphertext`, `sender_key`,
|
||||||
|
`session_id`), не plain JSON с `"m.rtc.notification"`. Если plain —
|
||||||
|
widget игнорит encryption, нужен апстрим-фикс Element Call либо наша
|
||||||
|
обёртка. См. [docs/plans/dm_calls_techdebt.md](../plans/dm_calls_techdebt.md) 5.9.
|
||||||
|
|
||||||
|
9. Когда открываешь чат уведомления из пуш бара не исчезают
|
||||||
|
|
||||||
|
10. Пофиксить цвет полос снизу и сверху при разных бекграундах цветовых (Сейчас они не синхронны на темной теме)
|
||||||
|
|
||||||
|
11. Как то по умнее схлопывать переходы назад. Возможно уникальные переходы схлопывать. Контекст такой: приложение сохраняет все походы юзера по экрану и так можно навигироваться без конца. Особенно это странно работает при входящих уведомлениях из одного чата: можно положить в этот стеш очень много ивентов этого окна и сидеть жать кнопку назад очень долго
|
||||||
|
|
||||||
|
12. Не очень понятно что делать со звонками в пуше на андроиде. Если пришел пуш-звонок, надо показывать на экране чата внизу сам эвент со звонком как на вебе?
|
||||||
|
|
||||||
|
13. UI friendly боты (в т.ч. телеграм бот)
|
||||||
|
|
||||||
|
14. Убрать странное читат чат
|
||||||
|
|
||||||
|
15. ~~Баг с присоединться в комнату, перекидывает не в чат, а в комнаты. При чем у одного юзера отображается чат, а у другого комната. Из-за этого нельзя позовнить например~~ — пофикшено: глобальный `useAutoDirectSync` listener, прямая навигация в Invites, smart push routing, HomeRouteRoomProvider redirect.
|
||||||
|
|
||||||
|
16. **Тех-долг:** one-shot repair для исторически сломанных DM-комнат. После фикса #15 новые join-ы корректно обновляют `m.direct`, но комнаты у юзеров, которые уже приняли инвайт до фикса, остаются в "Комнатах". Нужна миграция или команда `/repairDMs`, которая пройдёт по joined rooms, проверит `getDMInviter()`, и добавит в `m.direct`. Осложнение: `prev_content.is_direct` не исчезает после `/converttoroom`, поэтому наивный sweep откатит ручную конвертацию — нужен tombstone-маркер или whitelist.
|
||||||
39
src/app/hooks/useAutoDirectSync.ts
Normal file
39
src/app/hooks/useAutoDirectSync.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { MatrixClient, Room, RoomEvent } from 'matrix-js-sdk';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { AccountDataEvent } from '../../types/matrix/accountData';
|
||||||
|
import { Membership } from '../../types/matrix/room';
|
||||||
|
import { getAccountData, getMDirects } from '../utils/room';
|
||||||
|
import { addRoomIdToMDirect } from '../utils/matrix';
|
||||||
|
|
||||||
|
export function useAutoDirectSync(mx: MatrixClient): void {
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMembership = (
|
||||||
|
room: Room,
|
||||||
|
_membership: string,
|
||||||
|
prevMembership?: string
|
||||||
|
) => {
|
||||||
|
if (prevMembership !== Membership.Invite) return;
|
||||||
|
if (room.getMyMembership() !== Membership.Join) return;
|
||||||
|
|
||||||
|
const dmInviter = room.getDMInviter();
|
||||||
|
if (!dmInviter) return;
|
||||||
|
|
||||||
|
const joinedAndInvited =
|
||||||
|
room.getJoinedMemberCount() + room.getInvitedMemberCount();
|
||||||
|
if (joinedAndInvited > 2) return;
|
||||||
|
|
||||||
|
const mDirectEvent = getAccountData(mx, AccountDataEvent.Direct);
|
||||||
|
if (mDirectEvent) {
|
||||||
|
const directs = getMDirects(mDirectEvent);
|
||||||
|
if (directs.has(room.roomId)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addRoomIdToMDirect(mx, room.roomId, dmInviter).catch(() => undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
mx.on(RoomEvent.MyMembership, handleMembership);
|
||||||
|
return () => {
|
||||||
|
mx.removeListener(RoomEvent.MyMembership, handleMembership);
|
||||||
|
};
|
||||||
|
}, [mx]);
|
||||||
|
}
|
||||||
|
|
@ -20,8 +20,9 @@ import {
|
||||||
unregisterPusher,
|
unregisterPusher,
|
||||||
urlBase64ToUint8Array,
|
urlBase64ToUint8Array,
|
||||||
} from '../utils/push';
|
} from '../utils/push';
|
||||||
import { getDirectRoomPath, getHomeRoomPath, getInboxInvitesPath } from '../pages/pathUtils';
|
import { getDirectRoomPath, getInboxInvitesPath } from '../pages/pathUtils';
|
||||||
import { pendingCallActionAtom } from '../state/pendingCallAction';
|
import { pendingCallActionAtom } from '../state/pendingCallAction';
|
||||||
|
import { useRoomNavigate } from './useRoomNavigate';
|
||||||
|
|
||||||
const noop = (): void => undefined;
|
const noop = (): void => undefined;
|
||||||
|
|
||||||
|
|
@ -274,6 +275,7 @@ export function usePushNotificationsLifecycle(): void {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const clientConfig = useClientConfig();
|
const clientConfig = useClientConfig();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { navigateRoom } = useRoomNavigate();
|
||||||
const setPendingCallAction = useSetAtom(pendingCallActionAtom);
|
const setPendingCallAction = useSetAtom(pendingCallActionAtom);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -300,7 +302,7 @@ export function usePushNotificationsLifecycle(): void {
|
||||||
navigate(getInboxInvitesPath());
|
navigate(getInboxInvitesPath());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (detail?.roomId) navigate(getHomeRoomPath(detail.roomId));
|
if (detail?.roomId) navigateRoom(detail.roomId);
|
||||||
};
|
};
|
||||||
const onSubChange = () => {
|
const onSubChange = () => {
|
||||||
if (isPushEnabled()) register().catch(noop);
|
if (isPushEnabled()) register().catch(noop);
|
||||||
|
|
@ -312,7 +314,7 @@ export function usePushNotificationsLifecycle(): void {
|
||||||
window.removeEventListener('vojo:pushNavigate', onNavigate);
|
window.removeEventListener('vojo:pushNavigate', onNavigate);
|
||||||
window.removeEventListener('vojo:pushSubscriptionChange', onSubChange);
|
window.removeEventListener('vojo:pushSubscriptionChange', onSubChange);
|
||||||
};
|
};
|
||||||
}, [navigate, register]);
|
}, [navigate, navigateRoom, register]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isNativePlatform()) return undefined;
|
if (!isNativePlatform()) return undefined;
|
||||||
|
|
@ -383,7 +385,7 @@ export function usePushNotificationsLifecycle(): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.room_id) navigate(getHomeRoomPath(data.room_id));
|
if (data.room_id) navigateRoom(data.room_id);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -473,7 +475,7 @@ export function usePushNotificationsLifecycle(): void {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
cleanups.forEach((c) => c());
|
cleanups.forEach((c) => c());
|
||||||
};
|
};
|
||||||
}, [navigate, mx, clientConfig, setPendingCallAction]);
|
}, [navigate, navigateRoom, mx, clientConfig, setPendingCallAction]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { isPushEnabled, getPushPlatform } from '../utils/push';
|
export { isPushEnabled, getPushPlatform } from '../utils/push';
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,31 @@
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { Navigate, useParams } from 'react-router-dom';
|
||||||
|
import { useAtomValue } from 'jotai';
|
||||||
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
|
||||||
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
|
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
|
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
|
||||||
import { useHomeRooms } from './useHomeRooms';
|
import { useHomeRooms } from './useHomeRooms';
|
||||||
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
|
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
|
||||||
|
import { mDirectAtom } from '../../../state/mDirectList';
|
||||||
|
import { getDirectRoomPath } from '../../pathUtils';
|
||||||
|
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
|
||||||
|
|
||||||
export function HomeRouteRoomProvider({ children }: { children: ReactNode }) {
|
export function HomeRouteRoomProvider({ children }: { children: ReactNode }) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const rooms = useHomeRooms();
|
const rooms = useHomeRooms();
|
||||||
|
const mDirects = useAtomValue(mDirectAtom);
|
||||||
|
|
||||||
const { roomIdOrAlias, eventId } = useParams();
|
const { roomIdOrAlias, eventId } = useParams();
|
||||||
const viaServers = useSearchParamsViaServers();
|
const viaServers = useSearchParamsViaServers();
|
||||||
const roomId = useSelectedRoom();
|
const roomId = useSelectedRoom();
|
||||||
const room = mx.getRoom(roomId);
|
const room = mx.getRoom(roomId);
|
||||||
|
|
||||||
|
if (room && mDirects.has(room.roomId)) {
|
||||||
|
const alias = getCanonicalAliasOrRoomId(mx, room.roomId);
|
||||||
|
return <Navigate to={getDirectRoomPath(alias, eventId)} replace />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!room || !rooms.includes(room.roomId)) {
|
if (!room || !rooms.includes(room.roomId)) {
|
||||||
return (
|
return (
|
||||||
<JoinBeforeNavigate
|
<JoinBeforeNavigate
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
import { RoomTopicEventContent } from 'matrix-js-sdk/lib/types';
|
import { RoomTopicEventContent } from 'matrix-js-sdk/lib/types';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
import { MatrixClient, MatrixError, Room } from 'matrix-js-sdk';
|
import { MatrixClient, MatrixError, Room } from 'matrix-js-sdk';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Page,
|
Page,
|
||||||
PageContent,
|
PageContent,
|
||||||
|
|
@ -48,10 +49,12 @@ import { nameInitials } from '../../../utils/common';
|
||||||
import { RoomAvatar } from '../../../components/room-avatar';
|
import { RoomAvatar } from '../../../components/room-avatar';
|
||||||
import {
|
import {
|
||||||
addRoomIdToMDirect,
|
addRoomIdToMDirect,
|
||||||
|
getCanonicalAliasOrRoomId,
|
||||||
getMxIdLocalPart,
|
getMxIdLocalPart,
|
||||||
guessDmRoomUserId,
|
guessDmRoomUserId,
|
||||||
rateLimitedActions,
|
rateLimitedActions,
|
||||||
} from '../../../utils/matrix';
|
} from '../../../utils/matrix';
|
||||||
|
import { getDirectRoomPath } from '../../pathUtils';
|
||||||
import { Time } from '../../../components/message';
|
import { Time } from '../../../components/message';
|
||||||
import { useElementSizeObserver } from '../../../hooks/useElementSizeObserver';
|
import { useElementSizeObserver } from '../../../hooks/useElementSizeObserver';
|
||||||
import { onEnterOrSpace, stopPropagation } from '../../../utils/keyboard';
|
import { onEnterOrSpace, stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
@ -162,6 +165,7 @@ function InviteCard({
|
||||||
}: InviteCardProps) {
|
}: InviteCardProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
const navigate = useNavigate();
|
||||||
const userId = mx.getSafeUserId();
|
const userId = mx.getSafeUserId();
|
||||||
|
|
||||||
const [viewTopic, setViewTopic] = useState(false);
|
const [viewTopic, setViewTopic] = useState(false);
|
||||||
|
|
@ -177,9 +181,12 @@ function InviteCard({
|
||||||
await mx.joinRoom(invite.roomId);
|
await mx.joinRoom(invite.roomId);
|
||||||
if (dmUserId) {
|
if (dmUserId) {
|
||||||
await addRoomIdToMDirect(mx, invite.roomId, dmUserId);
|
await addRoomIdToMDirect(mx, invite.roomId, dmUserId);
|
||||||
|
const alias = getCanonicalAliasOrRoomId(mx, invite.roomId);
|
||||||
|
navigate(getDirectRoomPath(alias));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
onNavigate(invite.roomId, invite.isSpace);
|
onNavigate(invite.roomId, invite.isSpace);
|
||||||
}, [mx, invite, userId, onNavigate])
|
}, [mx, invite, userId, onNavigate, navigate])
|
||||||
);
|
);
|
||||||
const [leaveState, leave] = useAsyncCallback<Record<string, never>, MatrixError, []>(
|
const [leaveState, leave] = useAsyncCallback<Record<string, never>, MatrixError, []>(
|
||||||
useCallback(() => mx.leave(invite.roomId), [mx, invite])
|
useCallback(() => mx.leave(invite.roomId), [mx, invite])
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ import { mDirectAtom, useBindMDirectAtom } from '../mDirectList';
|
||||||
import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../room/roomToUnread';
|
import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../room/roomToUnread';
|
||||||
import { roomToParentsAtom, useBindRoomToParentsAtom } from '../room/roomToParents';
|
import { roomToParentsAtom, useBindRoomToParentsAtom } from '../room/roomToParents';
|
||||||
import { roomIdToTypingMembersAtom, useBindRoomIdToTypingMembersAtom } from '../typingMembers';
|
import { roomIdToTypingMembersAtom, useBindRoomIdToTypingMembersAtom } from '../typingMembers';
|
||||||
|
import { useAutoDirectSync } from '../../hooks/useAutoDirectSync';
|
||||||
|
|
||||||
export const useBindAtoms = (mx: MatrixClient) => {
|
export const useBindAtoms = (mx: MatrixClient) => {
|
||||||
useBindMDirectAtom(mx, mDirectAtom);
|
useBindMDirectAtom(mx, mDirectAtom);
|
||||||
|
useAutoDirectSync(mx);
|
||||||
useBindAllInvitesAtom(mx, allInvitesAtom);
|
useBindAllInvitesAtom(mx, allInvitesAtom);
|
||||||
useBindAllRoomsAtom(mx, allRoomsAtom);
|
useBindAllRoomsAtom(mx, allRoomsAtom);
|
||||||
useBindRoomToParentsAtom(mx, roomToParentsAtom);
|
useBindRoomToParentsAtom(mx, roomToParentsAtom);
|
||||||
|
|
|
||||||
|
|
@ -252,57 +252,65 @@ export const guessDmRoomUserId = (room: Room, myUserId: string): string => {
|
||||||
return member1?.userId ?? myUserId;
|
return member1?.userId ?? myUserId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addRoomIdToMDirect = async (
|
const mDirectQueues = new WeakMap<MatrixClient, Promise<void>>();
|
||||||
|
const enqueueMDirectWrite = (mx: MatrixClient, fn: () => Promise<void>): Promise<void> => {
|
||||||
|
const prev = mDirectQueues.get(mx) ?? Promise.resolve();
|
||||||
|
const next = prev.then(fn, fn);
|
||||||
|
mDirectQueues.set(mx, next);
|
||||||
|
return next;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addRoomIdToMDirect = (
|
||||||
mx: MatrixClient,
|
mx: MatrixClient,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
userId: string
|
userId: string
|
||||||
): Promise<void> => {
|
): Promise<void> =>
|
||||||
const mDirectsEvent = mx.getAccountData(AccountDataEvent.Direct as any);
|
enqueueMDirectWrite(mx, async () => {
|
||||||
let userIdToRoomIds: Record<string, string[]> = {};
|
const mDirectsEvent = mx.getAccountData(AccountDataEvent.Direct as any);
|
||||||
|
let userIdToRoomIds: Record<string, string[]> = {};
|
||||||
|
|
||||||
if (typeof mDirectsEvent !== 'undefined')
|
if (typeof mDirectsEvent !== 'undefined')
|
||||||
userIdToRoomIds = structuredClone(mDirectsEvent.getContent());
|
userIdToRoomIds = structuredClone(mDirectsEvent.getContent());
|
||||||
|
|
||||||
// remove it from the lists of any others users
|
Object.keys(userIdToRoomIds).forEach((targetUserId) => {
|
||||||
// (it can only be a DM room for one person)
|
const roomIds = userIdToRoomIds[targetUserId];
|
||||||
Object.keys(userIdToRoomIds).forEach((targetUserId) => {
|
|
||||||
const roomIds = userIdToRoomIds[targetUserId];
|
|
||||||
|
|
||||||
if (targetUserId !== userId) {
|
if (targetUserId !== userId) {
|
||||||
|
const indexOfRoomId = roomIds.indexOf(roomId);
|
||||||
|
if (indexOfRoomId > -1) {
|
||||||
|
roomIds.splice(indexOfRoomId, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const roomIds = userIdToRoomIds[userId] || [];
|
||||||
|
if (roomIds.indexOf(roomId) === -1) {
|
||||||
|
roomIds.push(roomId);
|
||||||
|
}
|
||||||
|
userIdToRoomIds[userId] = roomIds;
|
||||||
|
|
||||||
|
await mx.setAccountData(AccountDataEvent.Direct as any, userIdToRoomIds as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeRoomIdFromMDirect = (mx: MatrixClient, roomId: string): Promise<void> =>
|
||||||
|
enqueueMDirectWrite(mx, async () => {
|
||||||
|
const mDirectsEvent = mx.getAccountData(AccountDataEvent.Direct as any);
|
||||||
|
let userIdToRoomIds: Record<string, string[]> = {};
|
||||||
|
|
||||||
|
if (typeof mDirectsEvent !== 'undefined')
|
||||||
|
userIdToRoomIds = structuredClone(mDirectsEvent.getContent());
|
||||||
|
|
||||||
|
Object.keys(userIdToRoomIds).forEach((targetUserId) => {
|
||||||
|
const roomIds = userIdToRoomIds[targetUserId];
|
||||||
const indexOfRoomId = roomIds.indexOf(roomId);
|
const indexOfRoomId = roomIds.indexOf(roomId);
|
||||||
if (indexOfRoomId > -1) {
|
if (indexOfRoomId > -1) {
|
||||||
roomIds.splice(indexOfRoomId, 1);
|
roomIds.splice(indexOfRoomId, 1);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
await mx.setAccountData(AccountDataEvent.Direct as any, userIdToRoomIds as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
const roomIds = userIdToRoomIds[userId] || [];
|
|
||||||
if (roomIds.indexOf(roomId) === -1) {
|
|
||||||
roomIds.push(roomId);
|
|
||||||
}
|
|
||||||
userIdToRoomIds[userId] = roomIds;
|
|
||||||
|
|
||||||
await mx.setAccountData(AccountDataEvent.Direct as any, userIdToRoomIds as any);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeRoomIdFromMDirect = async (mx: MatrixClient, roomId: string): Promise<void> => {
|
|
||||||
const mDirectsEvent = mx.getAccountData(AccountDataEvent.Direct as any);
|
|
||||||
let userIdToRoomIds: Record<string, string[]> = {};
|
|
||||||
|
|
||||||
if (typeof mDirectsEvent !== 'undefined')
|
|
||||||
userIdToRoomIds = structuredClone(mDirectsEvent.getContent());
|
|
||||||
|
|
||||||
Object.keys(userIdToRoomIds).forEach((targetUserId) => {
|
|
||||||
const roomIds = userIdToRoomIds[targetUserId];
|
|
||||||
const indexOfRoomId = roomIds.indexOf(roomId);
|
|
||||||
if (indexOfRoomId > -1) {
|
|
||||||
roomIds.splice(indexOfRoomId, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await mx.setAccountData(AccountDataEvent.Direct as any, userIdToRoomIds as any);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mxcUrlToHttp = (
|
export const mxcUrlToHttp = (
|
||||||
mx: MatrixClient,
|
mx: MatrixClient,
|
||||||
mxcUrl: string,
|
mxcUrl: string,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue