1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-23 17:02:25 +03:00
Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo K
2025-10-07 14:44:57 +02:00
parent a343e8c92a
commit 11f610d7c7
5 changed files with 52 additions and 35 deletions

View File

@@ -171,14 +171,6 @@ describe("CallMembership", () => {
});
describe("RtcMembershipData", () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
const membershipTemplate: RtcMembershipData = {
"slot_id": "m.call#",
"application": { "type": "m.call", "m.call.id": "", "m.call.intent": "voice" },

View File

@@ -46,6 +46,7 @@ export interface RtcMembershipData {
};
"rtc_transports": Transport[];
"versions": string[];
"msc4354_sticky_key"?: string;
"sticky_key"?: string;
/**
* The intent of the call from the perspective of this user. This may be an audio call, video call or
@@ -93,7 +94,8 @@ const checkRtcMembershipData = (
}
// optional fields
if (data.sticky_key !== undefined && typeof data.sticky_key !== "string") {
const stickyKey = data.sticky_key ?? data.msc4354_sticky_key;
if (stickyKey !== undefined && typeof stickyKey !== "string") {
errors.push(prefix + "sticky_key must be a string");
}
if (data["m.call.intent"] !== undefined && typeof data["m.call.intent"] !== "string") {
@@ -210,16 +212,20 @@ const checkSessionsMembershipData = (
type MembershipData = { kind: "rtc"; data: RtcMembershipData } | { kind: "session"; data: SessionMembershipData };
// TODO: Rename to RtcMembership once we removed the legacy SessionMembership from this file.
export class CallMembership {
public static equal(a: CallMembership, b: CallMembership): boolean {
public static equal(a?: CallMembership, b?: CallMembership): boolean {
if (a === undefined || b === undefined) return a === b;
return deepCompare(a.membershipData, b.membershipData);
}
private membershipData: MembershipData;
private parentEventData: { eventId: string; sender: string };
/** The parsed data from the Matrix event.
* To access checked eventId and sender from the matrixEvent.
* Class construction will fail if these values cannot get obtained. */
private matrixEventData: { eventId: string; sender: string };
public constructor(
private parentEvent: MatrixEvent,
/** The Matrix event that this membership is based on */
private matrixEvent: MatrixEvent,
data: any,
) {
const sessionErrors: string[] = [];
@@ -237,12 +243,12 @@ export class CallMembership {
);
}
const eventId = parentEvent.getId();
const sender = parentEvent.getSender();
const eventId = matrixEvent.getId();
const sender = matrixEvent.getSender();
if (eventId === undefined) throw new Error("parentEvent is missing eventId field");
if (sender === undefined) throw new Error("parentEvent is missing sender field");
this.parentEventData = { eventId, sender };
this.matrixEventData = { eventId, sender };
}
public get sender(): string {
@@ -250,13 +256,14 @@ export class CallMembership {
switch (kind) {
case "rtc":
return data.member.user_id;
default: // "session":
return this.parentEventData.sender;
case "session":
default:
return this.matrixEventData.sender;
}
}
public get eventId(): string {
return this.parentEventData.eventId;
return this.matrixEventData.eventId;
}
/**
@@ -268,7 +275,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return data.slot_id;
default: // "session":
case "session":
default:
return slotDescriptionToId({ application: this.application, id: data.call_id });
}
}
@@ -278,7 +286,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return data.member.device_id;
default: // "session":
case "session":
default:
return data.device_id;
}
}
@@ -299,7 +308,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return data.application.type;
default: // "session":
case "session":
default:
return data.application;
}
}
@@ -308,7 +318,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return data.application;
default: // "session":
case "session":
default:
return { "type": data.application, "m.call.intent": data["m.call.intent"] };
}
}
@@ -319,7 +330,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return undefined;
default: // "session":
case "session":
default:
return data.scope;
}
}
@@ -332,7 +344,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return data.member.id;
default: // "session":
case "session":
default:
return (this.createdTs() ?? "").toString();
}
}
@@ -342,9 +355,10 @@ export class CallMembership {
switch (kind) {
case "rtc":
// TODO we need to read the referenced (relation) event if available to get the real created_ts
return this.parentEvent.getTs();
default: // "session":
return data.created_ts ?? this.parentEvent.getTs();
return this.matrixEvent.getTs();
case "session":
default:
return data.created_ts ?? this.matrixEvent.getTs();
}
}
@@ -357,7 +371,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return undefined;
default: // "session":
case "session":
default:
// TODO: calculate this from the MatrixRTCSession join configuration directly
return this.createdTs() + (data.expires ?? DEFAULT_EXPIRE_DURATION);
}
@@ -371,7 +386,8 @@ export class CallMembership {
switch (kind) {
case "rtc":
return undefined;
default: // "session":
case "session":
default:
// Assume that local clock is sufficiently in sync with other clocks in the distributed system.
// We used to try and adjust for the local clock being skewed, but there are cases where this is not accurate.
// The current implementation allows for the local clock to be -infinity to +MatrixRTCSession.MEMBERSHIP_EXPIRY_TIME/2
@@ -387,14 +403,15 @@ export class CallMembership {
switch (kind) {
case "rtc":
return false;
default: // "session":
case "session":
default:
return this.getMsUntilExpiry()! <= 0;
}
}
/**
* ## RTC Membership
* Gets the transport to use for this RTC membership (m.rtc.member).
* Gets the primary transport to use for this RTC membership (m.rtc.member).
* This will return the primary transport that is used by this call membership to publish their media.
* Directly relates to the `rtc_transports` field.
*
@@ -409,7 +426,6 @@ export class CallMembership {
* Always required to make the consumer not care if it deals with RTC or session memberships.
* @returns The transport this membership uses to publish media or undefined if no transport is available.
*/
// TODO: make this return all transports used to publish media once this is supported.
public getTransport(oldestMembership: CallMembership): Transport | undefined {
const { kind, data } = this.membershipData;
switch (kind) {
@@ -427,12 +443,17 @@ export class CallMembership {
}
return undefined;
}
/**
* The value of the `rtc_transports` field for RTC memberships (m.rtc.member).
* Or the value of the `foci_preferred` field for legacy session memberships (m.call.member).
*/
public get transports(): Transport[] {
const { kind, data } = this.membershipData;
switch (kind) {
case "rtc":
return data.rtc_transports;
default: // "session":
case "session":
default:
return data.foci_preferred;
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2023 New Vector Ltd
Copyright 2025 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -363,6 +363,10 @@ export class MatrixRTCSession extends TypedEventEmitter<
if (membershipContents.length === 0) continue;
for (const membershipData of membershipContents) {
if (!("application" in membershipData)) {
// This is a left membership event, ignore it here to not log warnings.
continue;
}
try {
const membership = new CallMembership(memberEvent, membershipData);

View File

@@ -388,7 +388,7 @@ export class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomMemberEve
}
}
export const MXID_PATTERN = /@[^@:]+:[^@:]+/;
export const MXID_PATTERN = /@.+:.+/;
const LTR_RTL_PATTERN = /[\u200E\u200F\u202A-\u202F]/;
function shouldDisambiguate(selfUserId: string, displayName?: string, roomState?: RoomState): boolean {