chore(deps): bump matrix-js-sdk 39.4.0 → 40.2.0 adapting sessionMembershipsForRoom removal and dedup-key trichotomy
This commit is contained in:
parent
0d93a223d0
commit
3ea01a9c3f
11 changed files with 66 additions and 36 deletions
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -52,7 +52,7 @@
|
|||
"jotai": "2.6.0",
|
||||
"linkify-react": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
"matrix-js-sdk": "39.4.0",
|
||||
"matrix-js-sdk": "40.2.0",
|
||||
"matrix-widget-api": "1.17.0",
|
||||
"millify": "6.1.0",
|
||||
"pdfjs-dist": "4.2.67",
|
||||
|
|
@ -2856,9 +2856,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
|
||||
"version": "15.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-15.3.0.tgz",
|
||||
"integrity": "sha512-QyxHvncvkl7nf+tnn92PjQ54gMNV8hMSpiukiDgNrqF6IYwgySTlcSdkPYdw8QjZJ0NR6fnVrNzMec0OohM3wA==",
|
||||
"version": "17.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-17.1.0.tgz",
|
||||
"integrity": "sha512-yKPqBvKlHSqkt/UJh+Z+zLKQP8bd19OxokXYXh3VkKbW0+C44nPHsidSwd3SH+RxT+Ck2PDRwVcVXEnUft+/2g==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
|
|
@ -10647,20 +10647,20 @@
|
|||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/matrix-js-sdk": {
|
||||
"version": "39.4.0",
|
||||
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-39.4.0.tgz",
|
||||
"integrity": "sha512-0RZLcwbMxMTU+ORPhpFUnX8nDbKwLtdW20T6VesSxEwjKL5j2TM/mIt4u3h0HJiMh63PpAb2QUcNr0R82YfTOA==",
|
||||
"version": "40.2.0",
|
||||
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-40.2.0.tgz",
|
||||
"integrity": "sha512-wqb1Oq34WB9r0njxw8XiNsm8DIvYeGfCn3wrVrDwj8HMoTI0TvLSY1sQ+x6J2Eg27abfVwInxLKyxLp+dROFXQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm": "^15.3.0",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm": "^17.0.0",
|
||||
"another-json": "^0.2.0",
|
||||
"bs58": "^6.0.0",
|
||||
"content-type": "^1.0.4",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"loglevel": "^1.9.2",
|
||||
"matrix-events-sdk": "0.0.1",
|
||||
"matrix-widget-api": "^1.14.0",
|
||||
"matrix-widget-api": "^1.16.1",
|
||||
"oidc-client-ts": "^3.0.1",
|
||||
"p-retry": "7",
|
||||
"sdp-transform": "^3.0.0",
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
"jotai": "2.6.0",
|
||||
"linkify-react": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
"matrix-js-sdk": "39.4.0",
|
||||
"matrix-js-sdk": "40.2.0",
|
||||
"matrix-widget-api": "1.17.0",
|
||||
"millify": "6.1.0",
|
||||
"pdfjs-dist": "4.2.67",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export function CallMemberCard({ member }: CallMemberCardProps) {
|
|||
|
||||
const openUserProfile = useOpenUserRoomProfile();
|
||||
|
||||
const userId = member.sender;
|
||||
const { userId } = member;
|
||||
if (!userId) return null;
|
||||
|
||||
const name = getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId;
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ function DmCallButton({ room }: { room: Room }) {
|
|||
const inCallHere = currentEmbed?.roomId === room.roomId;
|
||||
if (inCallHere) return null;
|
||||
|
||||
const ongoingByOthers = members.length > 0 && !members.some((m) => m.sender === myUserId);
|
||||
const ongoingByOthers = members.length > 0 && !members.some((m) => m.userId === myUserId);
|
||||
const disabled = !livekitSupported;
|
||||
let tooltipText: string;
|
||||
if (!livekitSupported) tooltipText = t('Call.unavailable');
|
||||
|
|
|
|||
|
|
@ -34,13 +34,11 @@ export const useCallSession = (room: Room): MatrixRTCSession => {
|
|||
};
|
||||
|
||||
export const useCallMembers = (room: Room, session: MatrixRTCSession): CallMembership[] => {
|
||||
const [memberships, setMemberships] = useState<CallMembership[]>(
|
||||
MatrixRTCSession.sessionMembershipsForRoom(room, session.slotDescription)
|
||||
);
|
||||
const [memberships, setMemberships] = useState<CallMembership[]>(session.memberships);
|
||||
|
||||
useEffect(() => {
|
||||
const updateMemberships = () => {
|
||||
setMemberships(MatrixRTCSession.sessionMembershipsForRoom(room, session.slotDescription));
|
||||
setMemberships(session.memberships);
|
||||
};
|
||||
|
||||
updateMemberships();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { createContext, RefObject, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { MatrixRTCSession } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSession';
|
||||
import { MatrixClient, Room } from 'matrix-js-sdk';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import {
|
||||
|
|
@ -46,8 +45,7 @@ export const createCallEmbed = (
|
|||
voiceOnly = false
|
||||
): CallEmbed => {
|
||||
const rtcSession = mx.matrixRTC.getRoomSession(room);
|
||||
const ongoing =
|
||||
MatrixRTCSession.sessionMembershipsForRoom(room, rtcSession.slotDescription).length > 0;
|
||||
const ongoing = rtcSession.memberships.length > 0;
|
||||
|
||||
const intent = CallEmbed.getIntent(dm, ongoing, voiceOnly);
|
||||
const widget = CallEmbed.getWidget(mx, room, intent, themeKind);
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const useCallerAutoHangup = (): void => {
|
|||
const selfDeviceId = mx.getDeviceId();
|
||||
|
||||
const isSelf = (m: CallMembership): boolean =>
|
||||
m.sender === selfId && (!selfDeviceId || m.deviceId === selfDeviceId);
|
||||
m.userId === selfId && (!selfDeviceId || m.deviceId === selfDeviceId);
|
||||
|
||||
const session = mx.matrixRTC.getRoomSession(callEmbed.room);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,12 +43,28 @@ import {
|
|||
} from '../utils/rtcNotification';
|
||||
import { callForegroundService } from '../plugins/call/callForegroundService';
|
||||
|
||||
// Returns "" for room-scoped calls (MSC4143/MSC3401v2 — empty call_id / slot id
|
||||
// means "the only call in this room"). Returns undefined when no membership is
|
||||
// known. matrix-js-sdk 39+ exposes the room-scoped id as
|
||||
// `slotDescription.id` (parsed from `slot_id = '{application}#{id}'` for
|
||||
// `m.rtc.member` or computed from legacy `call_id` for `m.call.member`); the
|
||||
// runtime `CallMembership.callId` getter was removed.
|
||||
// Extract the room-scoped call slot id from a raw membership state event,
|
||||
// covering both the legacy `m.call.member` shape (SessionMembershipData with
|
||||
// `call_id` string) and the `m.rtc.member` shape (RtcMembershipData with
|
||||
// `slot_id` of form `'{application}#{id}'`). Returns the bare id portion;
|
||||
// caller normalizes the legacy `''` ↔ `'ROOM'` equivalence at the dedup-key
|
||||
// boundary (see `getIncomingCallKey`). Returns undefined for malformed events.
|
||||
const extractCallIdFromMembershipEvent = (ev: MatrixEvent): string | undefined => {
|
||||
if (ev.getType() === EventType.RTCMembership) {
|
||||
const slotId = (ev.getContent() as { slot_id?: unknown }).slot_id;
|
||||
if (typeof slotId !== 'string') return undefined;
|
||||
const hashIdx = slotId.indexOf('#');
|
||||
return hashIdx >= 0 ? slotId.slice(hashIdx + 1) : '';
|
||||
}
|
||||
return ev.getContent<SessionMembershipData>().call_id ?? '';
|
||||
};
|
||||
|
||||
// Returns the room-scoped slot id ('' for legacy or new with implicit default,
|
||||
// 'ROOM' after SDK 40.2 compat-hack via senderMembership.slotDescription.id;
|
||||
// dedup-key normalization handled in getIncomingCallKey). Returns undefined
|
||||
// when no membership is known. matrix-js-sdk 39+ exposes the slot id as
|
||||
// `slotDescription.id` parsed from new `slot_id` or computed from legacy
|
||||
// `call_id`; the runtime `CallMembership.callId` getter was removed.
|
||||
const findCallIdSync = (
|
||||
mx: MatrixClient,
|
||||
room: Room,
|
||||
|
|
@ -61,20 +77,27 @@ const findCallIdSync = (
|
|||
return senderMembership.slotDescription.id;
|
||||
}
|
||||
|
||||
const stateEvents = room.currentState.getStateEvents(EventType.GroupCallMemberPrefix);
|
||||
// Race-recovery: ring arrived before /sync delivered the membership state
|
||||
// event into the SDK session. Fall back to direct state lookup, covering
|
||||
// both legacy `m.call.member` and `m.rtc.member` event types so a peer on
|
||||
// an MSC4354-capable homeserver doesn't silently produce an empty key here.
|
||||
const stateEvents = [
|
||||
...room.currentState.getStateEvents(EventType.GroupCallMemberPrefix),
|
||||
...room.currentState.getStateEvents(EventType.RTCMembership),
|
||||
];
|
||||
const senderStateEv = stateEvents.find((ev) => ev.getSender() === sender);
|
||||
if (senderStateEv) {
|
||||
return senderStateEv.getContent<SessionMembershipData>().call_id ?? '';
|
||||
return extractCallIdFromMembershipEvent(senderStateEv);
|
||||
}
|
||||
|
||||
const timelineEv = room.findEventById(membershipEventId);
|
||||
if (timelineEv) {
|
||||
return timelineEv.getContent<SessionMembershipData>().call_id ?? '';
|
||||
return extractCallIdFromMembershipEvent(timelineEv);
|
||||
}
|
||||
|
||||
const stateEv = stateEvents.find((ev) => ev.getId() === membershipEventId);
|
||||
if (stateEv) {
|
||||
return stateEv.getContent<SessionMembershipData>().call_id ?? '';
|
||||
return extractCallIdFromMembershipEvent(stateEv);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
|
@ -112,7 +135,7 @@ const resolveCallId = async (
|
|||
try {
|
||||
const raw = await mx.fetchRoomEvent(room.roomId, membershipEventId);
|
||||
const fetched = new MatrixEvent(raw);
|
||||
return fetched.getContent<SessionMembershipData>().call_id ?? '';
|
||||
return extractCallIdFromMembershipEvent(fetched);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export const useSwitchOrStartDmCall = (): ((roomId: string) => Promise<void>) =>
|
|||
const selfDeviceId = mx.getDeviceId();
|
||||
const selfPresent = (memberships: CallMembership[]): boolean =>
|
||||
memberships.some(
|
||||
(m) => m.sender === selfUserId && (!selfDeviceId || m.deviceId === selfDeviceId)
|
||||
(m) => m.userId === selfUserId && (!selfDeviceId || m.deviceId === selfDeviceId)
|
||||
);
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
|
|
@ -118,7 +118,7 @@ export const useSwitchOrStartDmCall = (): ((roomId: string) => Promise<void>) =>
|
|||
.getRoomSession(prev.room)
|
||||
.memberships.some(
|
||||
(m: CallMembership) =>
|
||||
m.sender !== selfUserId || (!!selfDeviceId && m.deviceId !== selfDeviceId)
|
||||
m.userId !== selfUserId || (!!selfDeviceId && m.deviceId !== selfDeviceId)
|
||||
);
|
||||
if (prev.joined && peerPresent) return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { App } from '@capacitor/app';
|
|||
import { incomingCallsAtom } from '../state/incomingCalls';
|
||||
import { useMatrixClient } from '../hooks/useMatrixClient';
|
||||
import { IncomingCallStrip } from '../features/call-status';
|
||||
import { getIncomingCallKey } from '../utils/rtcNotification';
|
||||
import { isAndroidPlatform } from '../utils/capacitor';
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import RingSoundOgg from '../../../public/sound/ring.ogg';
|
||||
|
|
@ -126,7 +127,7 @@ export function IncomingCallStripRenderer() {
|
|||
if (!room) return null;
|
||||
return (
|
||||
<IncomingCallStrip
|
||||
key={`call_${call.callId}_${call.roomId}`}
|
||||
key={getIncomingCallKey(call.callId, call.roomId)}
|
||||
call={call}
|
||||
room={room}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -17,5 +17,15 @@ export const isRtcNotificationExpired = (ev: MatrixEvent, now = Date.now()): boo
|
|||
return now - getNotificationEventSendTs(ev) > lifetime;
|
||||
};
|
||||
|
||||
export const getIncomingCallKey = (callId: string, roomId: string): string =>
|
||||
`call_${callId}_${roomId}`;
|
||||
// Normalize the legacy '' ↔ 'ROOM' equivalence for room-scoped DM call slots.
|
||||
// matrix-js-sdk 40.2 introduced a `slotDescription.id` compat hack that
|
||||
// surfaces 'ROOM' for room-scoped m.call.member memberships (which still go on
|
||||
// the wire as `call_id: ''`); pre-40.2 SDKs and direct raw-event reads in
|
||||
// `findCallIdSync` race-recovery paths still yield ''. Without this normalize
|
||||
// step, a single logical ring acquires three distinct dedup keys depending on
|
||||
// peer SDK version and whether we read happy-path vs fallback — and registry
|
||||
// dedup, decline-IDs and atom REMOVE/ADD all key on the result.
|
||||
export const getIncomingCallKey = (callId: string, roomId: string): string => {
|
||||
const normalized = callId === '' ? 'ROOM' : callId;
|
||||
return `call_${normalized}_${roomId}`;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue