mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +00:00
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:
committed by
Andrew Ferrazzutti
parent
69deece0a3
commit
5bab9b0858
@@ -20,7 +20,8 @@ import { useLocation } from "react-router-dom";
|
|||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
|
|
||||||
export const PASSWORD_STRING = "password=";
|
export const PASSWORD_STRING = "password=";
|
||||||
|
export const PER_PARTICIPANT_STRING = "perParticipantE2EE=";
|
||||||
|
export const VIA_SERVERS_STRING = "viaServers=";
|
||||||
interface RoomIdentifier {
|
interface RoomIdentifier {
|
||||||
roomAlias: string | null;
|
roomAlias: string | null;
|
||||||
roomId: string | null;
|
roomId: string | null;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import styles from "./CallList.module.css";
|
|||||||
import { getAbsoluteRoomUrl, getRelativeRoomUrl } from "../matrix-utils";
|
import { getAbsoluteRoomUrl, getRelativeRoomUrl } from "../matrix-utils";
|
||||||
import { Body } from "../typography/Typography";
|
import { Body } from "../typography/Typography";
|
||||||
import { GroupCallRoom } from "./useGroupCallRooms";
|
import { GroupCallRoom } from "./useGroupCallRooms";
|
||||||
import { useRoomSharedKey } from "../e2ee/sharedKeyManagement";
|
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement";
|
||||||
|
|
||||||
interface CallListProps {
|
interface CallListProps {
|
||||||
rooms: GroupCallRoom[];
|
rooms: GroupCallRoom[];
|
||||||
@@ -66,16 +66,11 @@ interface CallTileProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
|
const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
|
||||||
const roomSharedKey = useRoomSharedKey(room.roomId);
|
const roomEncryptionSystem = useRoomEncryptionSystem(room.roomId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.callTile}>
|
<div className={styles.callTile}>
|
||||||
<Link
|
<Link
|
||||||
to={getRelativeRoomUrl(
|
to={getRelativeRoomUrl(room.roomId, roomEncryptionSystem, room.name)}
|
||||||
room.roomId,
|
|
||||||
room.name,
|
|
||||||
roomSharedKey ?? undefined,
|
|
||||||
)}
|
|
||||||
className={styles.callTileLink}
|
className={styles.callTileLink}
|
||||||
>
|
>
|
||||||
<Avatar id={room.roomId} name={name} size={Size.LG} src={avatarUrl} />
|
<Avatar id={room.roomId} name={name} size={Size.LG} src={avatarUrl} />
|
||||||
@@ -89,11 +84,8 @@ const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
|
|||||||
<CopyButton
|
<CopyButton
|
||||||
className={styles.copyButton}
|
className={styles.copyButton}
|
||||||
variant="icon"
|
variant="icon"
|
||||||
value={getAbsoluteRoomUrl(
|
// Todo add the viaServers to the created link
|
||||||
room.roomId,
|
value={getAbsoluteRoomUrl(room.roomId, roomEncryptionSystem, room.name)}
|
||||||
room.name,
|
|
||||||
roomSharedKey ?? undefined,
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,20 +15,22 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
|
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
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 { 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 {
|
export interface GroupCallRoom {
|
||||||
roomAlias?: string;
|
roomAlias?: string;
|
||||||
roomName: string;
|
roomName: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
room: Room;
|
room: Room;
|
||||||
groupCall: GroupCall;
|
session: MatrixRTCSession;
|
||||||
participants: RoomMember[];
|
participants: RoomMember[];
|
||||||
}
|
}
|
||||||
const tsCache: { [index: string]: number } = {};
|
const tsCache: { [index: string]: number } = {};
|
||||||
@@ -46,7 +48,7 @@ function getLastTs(client: MatrixClient, r: Room): number {
|
|||||||
|
|
||||||
const myUserId = client.getUserId()!;
|
const myUserId = client.getUserId()!;
|
||||||
|
|
||||||
if (r.getMyMembership() !== "join") {
|
if (r.getMyMembership() !== KnownMembership.Join) {
|
||||||
const membershipEvent = r.currentState.getStateEvents(
|
const membershipEvent = r.currentState.getStateEvents(
|
||||||
"m.room.member",
|
"m.room.member",
|
||||||
myUserId,
|
myUserId,
|
||||||
@@ -80,38 +82,51 @@ function sortRooms(client: MatrixClient, rooms: Room[]): Room[] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function roomIsJoinable(room: Room): boolean {
|
const roomIsJoinable = (room: Room): boolean => {
|
||||||
if (isRoomE2EE(room)) {
|
if (!room.hasEncryptionStateEvent() && !getKeyForRoom(room.roomId)) {
|
||||||
return Boolean(getKeyForRoom(room.roomId));
|
// if we have an non encrypted room (no encryption state event) we need a locally stored shared key.
|
||||||
} else {
|
// in case this key also does not exists we cannot join the room.
|
||||||
return true;
|
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[] {
|
export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] {
|
||||||
const [rooms, setRooms] = useState<GroupCallRoom[]>([]);
|
const [rooms, setRooms] = useState<GroupCallRoom[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function updateRooms(): void {
|
function updateRooms(): void {
|
||||||
if (!client.groupCallEventHandler) {
|
const rooms = client
|
||||||
return;
|
.getRooms()
|
||||||
}
|
.filter(roomIsJoinedWithCall)
|
||||||
|
|
||||||
const groupCalls = client.groupCallEventHandler.groupCalls.values();
|
|
||||||
const rooms = Array.from(groupCalls)
|
|
||||||
.map((groupCall) => groupCall.room)
|
|
||||||
.filter(roomIsJoinable);
|
.filter(roomIsJoinable);
|
||||||
const sortedRooms = sortRooms(client, rooms);
|
const sortedRooms = sortRooms(client, rooms);
|
||||||
const items = sortedRooms.map((room) => {
|
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 {
|
return {
|
||||||
roomAlias: room.getCanonicalAlias() ?? undefined,
|
roomAlias: room.getCanonicalAlias() ?? undefined,
|
||||||
roomName: room.name,
|
roomName: room.name,
|
||||||
avatarUrl: room.getMxcAvatarUrl()!,
|
avatarUrl: room.getMxcAvatarUrl()!,
|
||||||
room,
|
room,
|
||||||
groupCall,
|
session,
|
||||||
participants: [...groupCall!.participants.keys()],
|
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();
|
updateRooms();
|
||||||
|
|
||||||
client.on(GroupCallEventHandlerEvent.Incoming, updateRooms);
|
client.matrixRTC.on(
|
||||||
client.on(GroupCallEventHandlerEvent.Participants, updateRooms);
|
MatrixRTCSessionManagerEvents.SessionStarted,
|
||||||
|
updateRooms,
|
||||||
|
);
|
||||||
|
client.on(RoomEvent.MyMembership, updateRooms);
|
||||||
return () => {
|
return () => {
|
||||||
client.removeListener(GroupCallEventHandlerEvent.Incoming, updateRooms);
|
client.matrixRTC.off(
|
||||||
client.removeListener(
|
MatrixRTCSessionManagerEvents.SessionStarted,
|
||||||
GroupCallEventHandlerEvent.Participants,
|
|
||||||
updateRooms,
|
updateRooms,
|
||||||
);
|
);
|
||||||
|
client.off(RoomEvent.MyMembership, updateRooms);
|
||||||
};
|
};
|
||||||
}, [client]);
|
}, [client]);
|
||||||
|
|
||||||
|
|||||||
@@ -24,20 +24,21 @@ import { ClientEvent } from "matrix-js-sdk/src/client";
|
|||||||
import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials";
|
import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials";
|
||||||
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
|
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
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 { secureRandomBase64Url } from "matrix-js-sdk/src/randomstring";
|
||||||
|
|
||||||
import type { MatrixClient } from "matrix-js-sdk/src/client";
|
import type { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import type { Room } from "matrix-js-sdk/src/models/room";
|
import type { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import IndexedDBWorker from "./IndexedDBWorker?worker";
|
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 { loadOlm } from "./olm";
|
||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
import { E2eeType } from "./e2ee/e2eeType";
|
import { E2eeType } from "./e2ee/e2eeType";
|
||||||
import { saveKeyForRoom } from "./e2ee/sharedKeyManagement";
|
import { EncryptionSystem, saveKeyForRoom } from "./e2ee/sharedKeyManagement";
|
||||||
|
|
||||||
export const fallbackICEServerAllowed =
|
export const fallbackICEServerAllowed =
|
||||||
import.meta.env.VITE_FALLBACK_STUN_ALLOWED === "true";
|
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}`);
|
logger.log(`Creating group call in ${result.room_id}`);
|
||||||
|
|
||||||
await client.createGroupCall(
|
|
||||||
result.room_id,
|
|
||||||
GroupCallType.Video,
|
|
||||||
false,
|
|
||||||
GroupCallIntent.Room,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let password;
|
let password;
|
||||||
if (e2ee == E2eeType.SHARED_KEY) {
|
if (e2ee == E2eeType.SHARED_KEY) {
|
||||||
password = secureRandomBase64Url(16);
|
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
|
* Returns an absolute URL to that will load Element Call with the given room
|
||||||
* @param roomId ID of the room
|
* @param roomId ID of the room
|
||||||
* @param roomName Name 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(
|
export function getAbsoluteRoomUrl(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
|
encryptionSystem: EncryptionSystem,
|
||||||
roomName?: string,
|
roomName?: string,
|
||||||
password?: string,
|
viaServers?: string[],
|
||||||
): string {
|
): string {
|
||||||
return `${window.location.protocol}//${
|
return `${window.location.protocol}//${
|
||||||
window.location.host
|
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
|
* Returns a relative URL to that will load Element Call with the given room
|
||||||
* @param roomId ID of the room
|
* @param roomId ID of the room
|
||||||
* @param roomName Name 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(
|
export function getRelativeRoomUrl(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
|
encryptionSystem: EncryptionSystem,
|
||||||
roomName?: string,
|
roomName?: string,
|
||||||
password?: string,
|
viaServers?: string[],
|
||||||
): string {
|
): string {
|
||||||
// The password shouldn't need URL encoding here (we generate URL-safe ones) but encode
|
// 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
|
// it in case it came from another client that generated a non url-safe one
|
||||||
const encodedPassword = password ? encodeURIComponent(password) : undefined;
|
const encryptionPart = ((): string => {
|
||||||
if (password && encodedPassword !== password) {
|
switch (encryptionSystem?.kind) {
|
||||||
logger.info("Encoded call password used non URL-safe chars: buggy client?");
|
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/#${
|
return `/room/#${
|
||||||
roomName ? "/" + roomAliasLocalpartFromRoomName(roomName) : ""
|
roomName ? "/" + roomAliasLocalpartFromRoomName(roomName) : ""
|
||||||
}?roomId=${roomId}${password ? "&" + PASSWORD_STRING + encodedPassword : ""}`;
|
}?${roomIdPart}${encryptionPart}${viaServersPart}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAvatarUrl(
|
export function getAvatarUrl(
|
||||||
|
|||||||
Reference in New Issue
Block a user