diff --git a/src/app/components/create-room/utils.ts b/src/app/components/create-room/utils.ts index 60f334e..83a9c0f 100644 --- a/src/app/components/create-room/utils.ts +++ b/src/app/components/create-room/utils.ts @@ -82,6 +82,12 @@ export const createRoomEncryptionState = () => ({ }, }); +/** + * Partial power levels for MatrixRTC call membership (MSC3401). + * Do NOT put this in createRoom initial_state — homeservers reject incomplete + * m.room.power_levels with 403. Apply after create via sendStateEvent instead, + * or rely on TrustedPrivateChat (both users PL 100) for DMs. + */ export const createRoomPowerLevelsState = () => ({ type: StateEvent.RoomPowerLevels, state_key: '', diff --git a/src/app/features/create-chat/CreateChat.tsx b/src/app/features/create-chat/CreateChat.tsx index 553c779..95cebe4 100644 --- a/src/app/features/create-chat/CreateChat.tsx +++ b/src/app/features/create-chat/CreateChat.tsx @@ -10,7 +10,7 @@ import { useMatrixClient } from '../../hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { ErrorCode } from '../../cs-errorcode'; import { millisecondsToMinutes } from '../../utils/common'; -import { createRoomEncryptionState, createRoomPowerLevelsState } from '../../components/create-room'; +import { createRoomEncryptionState } from '../../components/create-room'; import { useAlive } from '../../hooks/useAlive'; import { getDirectRoomPath } from '../../pages/pathUtils'; @@ -28,9 +28,10 @@ export function CreateChat({ defaultUserId }: CreateChatProps) { const [createState, create] = useAsyncCallback( useCallback( async (userId, encrypted) => { + // Do not put m.room.power_levels in initial_state — incomplete PL events + // (used for org.matrix.msc3401.call.member) cause M_FORBIDDEN / 403 on create. + // TrustedPrivateChat already gives both users PL 100, so call membership works. const initialState: ICreateRoomStateEvent[] = []; - - initialState.push(createRoomPowerLevelsState()); if (encrypted) initialState.push(createRoomEncryptionState()); const result = await mx.createRoom({ diff --git a/src/app/hooks/useCommands.ts b/src/app/hooks/useCommands.ts index d116661..0b09439 100644 --- a/src/app/hooks/useCommands.ts +++ b/src/app/hooks/useCommands.ts @@ -26,7 +26,7 @@ import { useRoomNavigate } from './useRoomNavigate'; import { Membership, StateEvent } from '../../types/matrix/room'; import { getStateEvent } from '../utils/room'; import { splitWithSpace } from '../utils/common'; -import { createRoomEncryptionState, createRoomPowerLevelsState } from '../components/create-room'; +import { createRoomEncryptionState } from '../components/create-room'; export const SHRUG = '¯\\_(ツ)_/¯'; export const TABLEFLIP = '(╯°□°)╯︵ ┻━┻'; @@ -218,7 +218,8 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => { invite: userIds, visibility: Visibility.Private, preset: Preset.TrustedPrivateChat, - initial_state: [createRoomPowerLevelsState(), createRoomEncryptionState()], + // No power_levels in initial_state — incomplete PL for call.member causes 403 + initial_state: [createRoomEncryptionState()], }); addRoomIdToMDirect(mx, result.room_id, userIds[0]); navigateRoom(result.room_id);