Update recent list.

- Don't use deprecated `groupCallEventHander` anymore (it used the old
  `m.call` state event.)
- make the recent list reactive (getting removed from a call removes the
  item from the list)
- support having rooms without shared secret but actual matrix
  encryption in the recent list
- change the share link creation button so that we create a link with
  pwd for sharedKey rooms and with `perParticipantE2EE=true` for matrix
  encrypted rooms.

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo K
2024-04-02 23:57:50 +02:00
committed by Andrew Ferrazzutti
parent 69deece0a3
commit 5bab9b0858
4 changed files with 89 additions and 67 deletions

View File

@@ -20,7 +20,8 @@ import { useLocation } from "react-router-dom";
import { Config } from "./config/Config";
export const PASSWORD_STRING = "password=";
export const PER_PARTICIPANT_STRING = "perParticipantE2EE=";
export const VIA_SERVERS_STRING = "viaServers=";
interface RoomIdentifier {
roomAlias: string | null;
roomId: string | null;

View File

@@ -26,7 +26,7 @@ import styles from "./CallList.module.css";
import { getAbsoluteRoomUrl, getRelativeRoomUrl } from "../matrix-utils";
import { Body } from "../typography/Typography";
import { GroupCallRoom } from "./useGroupCallRooms";
import { useRoomSharedKey } from "../e2ee/sharedKeyManagement";
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement";
interface CallListProps {
rooms: GroupCallRoom[];
@@ -66,16 +66,11 @@ interface CallTileProps {
}
const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
const roomSharedKey = useRoomSharedKey(room.roomId);
const roomEncryptionSystem = useRoomEncryptionSystem(room.roomId);
return (
<div className={styles.callTile}>
<Link
to={getRelativeRoomUrl(
room.roomId,
room.name,
roomSharedKey ?? undefined,
)}
to={getRelativeRoomUrl(room.roomId, roomEncryptionSystem, room.name)}
className={styles.callTileLink}
>
<Avatar id={room.roomId} name={name} size={Size.LG} src={avatarUrl} />
@@ -89,11 +84,8 @@ const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
<CopyButton
className={styles.copyButton}
variant="icon"
value={getAbsoluteRoomUrl(
room.roomId,
room.name,
roomSharedKey ?? undefined,
)}
// Todo add the viaServers to the created link
value={getAbsoluteRoomUrl(room.roomId, roomEncryptionSystem, room.name)}
/>
</div>
);

View File

@@ -15,20 +15,22 @@ limitations under the License.
*/
import { MatrixClient } from "matrix-js-sdk/src/client";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { Room } from "matrix-js-sdk/src/models/room";
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { GroupCallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/groupCallEventHandler";
import { useState, useEffect } from "react";
import { EventTimeline, EventType, JoinRule } from "matrix-js-sdk";
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
import { MatrixRTCSessionManagerEvents } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSessionManager";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { getKeyForRoom, isRoomE2EE } from "../e2ee/sharedKeyManagement";
import { getKeyForRoom } from "../e2ee/sharedKeyManagement";
export interface GroupCallRoom {
roomAlias?: string;
roomName: string;
avatarUrl: string;
room: Room;
groupCall: GroupCall;
session: MatrixRTCSession;
participants: RoomMember[];
}
const tsCache: { [index: string]: number } = {};
@@ -46,7 +48,7 @@ function getLastTs(client: MatrixClient, r: Room): number {
const myUserId = client.getUserId()!;
if (r.getMyMembership() !== "join") {
if (r.getMyMembership() !== KnownMembership.Join) {
const membershipEvent = r.currentState.getStateEvents(
"m.room.member",
myUserId,
@@ -80,38 +82,51 @@ function sortRooms(client: MatrixClient, rooms: Room[]): Room[] {
});
}
function roomIsJoinable(room: Room): boolean {
if (isRoomE2EE(room)) {
return Boolean(getKeyForRoom(room.roomId));
} else {
return true;
const roomIsJoinable = (room: Room): boolean => {
if (!room.hasEncryptionStateEvent() && !getKeyForRoom(room.roomId)) {
// if we have an non encrypted room (no encryption state event) we need a locally stored shared key.
// in case this key also does not exists we cannot join the room.
return false;
}
}
// otherwise we can always join rooms because we will automatically decide if we want to use perParticipant or password
const joinRule = room.getJoinRule();
return joinRule === JoinRule.Knock || joinRule === JoinRule.Public;
};
const roomIsJoinedWithCall = (room: Room): boolean => {
const roomStateEvents = room
.getLiveTimeline()
.getState(EventTimeline.FORWARDS)?.events;
return (
room.getMyMembership() === KnownMembership.Join &&
!!roomStateEvents?.get(EventType.GroupCallMemberPrefix)
);
};
export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] {
const [rooms, setRooms] = useState<GroupCallRoom[]>([]);
useEffect(() => {
function updateRooms(): void {
if (!client.groupCallEventHandler) {
return;
}
const groupCalls = client.groupCallEventHandler.groupCalls.values();
const rooms = Array.from(groupCalls)
.map((groupCall) => groupCall.room)
const rooms = client
.getRooms()
.filter(roomIsJoinedWithCall)
.filter(roomIsJoinable);
const sortedRooms = sortRooms(client, rooms);
const items = sortedRooms.map((room) => {
const groupCall = client.getGroupCallForRoom(room.roomId)!;
// const groupCall = client.getGroupCallForRoom(room.roomId)!;
const session = client.matrixRTC.getRoomSession(room);
session.memberships;
return {
roomAlias: room.getCanonicalAlias() ?? undefined,
roomName: room.name,
avatarUrl: room.getMxcAvatarUrl()!,
room,
groupCall,
participants: [...groupCall!.participants.keys()],
session,
participants: session.memberships
.filter((m) => m.sender)
.map((m) => room.getMember(m.sender!))
.filter((m) => m) as RoomMember[],
};
});
@@ -120,15 +135,17 @@ export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] {
updateRooms();
client.on(GroupCallEventHandlerEvent.Incoming, updateRooms);
client.on(GroupCallEventHandlerEvent.Participants, updateRooms);
client.matrixRTC.on(
MatrixRTCSessionManagerEvents.SessionStarted,
updateRooms,
);
client.on(RoomEvent.MyMembership, updateRooms);
return () => {
client.removeListener(GroupCallEventHandlerEvent.Incoming, updateRooms);
client.removeListener(
GroupCallEventHandlerEvent.Participants,
client.matrixRTC.off(
MatrixRTCSessionManagerEvents.SessionStarted,
updateRooms,
);
client.off(RoomEvent.MyMembership, updateRooms);
};
}, [client]);

View File

@@ -24,20 +24,21 @@ import { ClientEvent } from "matrix-js-sdk/src/client";
import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials";
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
import { logger } from "matrix-js-sdk/src/logger";
import {
GroupCallIntent,
GroupCallType,
} from "matrix-js-sdk/src/webrtc/groupCall";
import { secureRandomBase64Url } from "matrix-js-sdk/src/randomstring";
import type { MatrixClient } from "matrix-js-sdk/src/client";
import type { Room } from "matrix-js-sdk/src/models/room";
import IndexedDBWorker from "./IndexedDBWorker?worker";
import { getUrlParams, PASSWORD_STRING } from "./UrlParams";
import {
getUrlParams,
PASSWORD_STRING,
PER_PARTICIPANT_STRING,
VIA_SERVERS_STRING,
} from "./UrlParams";
import { loadOlm } from "./olm";
import { Config } from "./config/Config";
import { E2eeType } from "./e2ee/e2eeType";
import { saveKeyForRoom } from "./e2ee/sharedKeyManagement";
import { EncryptionSystem, saveKeyForRoom } from "./e2ee/sharedKeyManagement";
export const fallbackICEServerAllowed =
import.meta.env.VITE_FALLBACK_STUN_ALLOWED === "true";
@@ -340,14 +341,6 @@ export async function createRoom(
logger.log(`Creating group call in ${result.room_id}`);
await client.createGroupCall(
result.room_id,
GroupCallType.Video,
false,
GroupCallIntent.Room,
true,
);
let password;
if (e2ee == E2eeType.SHARED_KEY) {
password = secureRandomBase64Url(16);
@@ -365,39 +358,58 @@ export async function createRoom(
* Returns an absolute URL to that will load Element Call with the given room
* @param roomId ID of the room
* @param roomName Name of the room
* @param password e2e key for the room
* @param encryptionSystem what encryption (or EncryptionSystem.Unencrypted) the room uses
*/
export function getAbsoluteRoomUrl(
roomId: string,
encryptionSystem: EncryptionSystem,
roomName?: string,
password?: string,
viaServers?: string[],
): string {
return `${window.location.protocol}//${
window.location.host
}${getRelativeRoomUrl(roomId, roomName, password)}`;
}${getRelativeRoomUrl(roomId, encryptionSystem, roomName, viaServers)}`;
}
/**
* Returns a relative URL to that will load Element Call with the given room
* @param roomId ID of the room
* @param roomName Name of the room
* @param password e2e key for the room
* @param encryptionSystem what encryption (or EncryptionSystem.Unencrypted) the room uses
*/
export function getRelativeRoomUrl(
roomId: string,
encryptionSystem: EncryptionSystem,
roomName?: string,
password?: string,
viaServers?: string[],
): string {
// The password shouldn't need URL encoding here (we generate URL-safe ones) but encode
// it in case it came from another client that generated a non url-safe one
const encodedPassword = password ? encodeURIComponent(password) : undefined;
if (password && encodedPassword !== password) {
logger.info("Encoded call password used non URL-safe chars: buggy client?");
}
const encryptionPart = ((): string => {
switch (encryptionSystem?.kind) {
case E2eeType.SHARED_KEY: {
const encodedPassword = encodeURIComponent(encryptionSystem.secret);
if (encodedPassword !== encryptionSystem.secret) {
logger.info(
"Encoded call password used non URL-safe chars: buggy client?",
);
}
return "&" + PASSWORD_STRING + encodedPassword;
}
case E2eeType.PER_PARTICIPANT:
return "&" + PER_PARTICIPANT_STRING + "true";
case E2eeType.NONE:
return "";
}
})();
const roomIdPart = `roomId=${roomId}`;
const viaServersPart = viaServers
? viaServers.map((s) => "&" + VIA_SERVERS_STRING + s).join("")
: "";
return `/room/#${
roomName ? "/" + roomAliasLocalpartFromRoomName(roomName) : ""
}?roomId=${roomId}${password ? "&" + PASSWORD_STRING + encodedPassword : ""}`;
}?${roomIdPart}${encryptionPart}${viaServersPart}`;
}
export function getAvatarUrl(