matrix-js-sdk 41.4.0 → 41.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/lib/@types/json.d.ts +7 -0
- package/lib/@types/json.d.ts.map +1 -0
- package/lib/@types/json.js +1 -0
- package/lib/@types/json.js.map +1 -0
- package/lib/@types/requests.d.ts +6 -9
- package/lib/@types/requests.d.ts.map +1 -1
- package/lib/@types/requests.js.map +1 -1
- package/lib/client.d.ts +17 -2
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +27 -12
- package/lib/client.js.map +1 -1
- package/lib/filter.d.ts +20 -5
- package/lib/filter.d.ts.map +1 -1
- package/lib/filter.js +21 -0
- package/lib/filter.js.map +1 -1
- package/lib/models/user.d.ts +5 -0
- package/lib/models/user.d.ts.map +1 -1
- package/lib/models/user.js +5 -0
- package/lib/models/user.js.map +1 -1
- package/lib/oidc/authorize.d.ts +60 -0
- package/lib/oidc/authorize.d.ts.map +1 -1
- package/lib/oidc/authorize.js +115 -2
- package/lib/oidc/authorize.js.map +1 -1
- package/lib/oidc/register.d.ts.map +1 -1
- package/lib/oidc/register.js +5 -0
- package/lib/oidc/register.js.map +1 -1
- package/lib/rendezvous/MSC4108SignInWithQR.d.ts +19 -2
- package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +1 -1
- package/lib/rendezvous/MSC4108SignInWithQR.js +126 -36
- package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -1
- package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts.map +1 -1
- package/lib/rendezvous/channels/MSC4108SecureChannel.js +4 -2
- package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +1 -1
- package/lib/rendezvous/index.d.ts +36 -0
- package/lib/rendezvous/index.d.ts.map +1 -1
- package/lib/rendezvous/index.js +115 -0
- package/lib/rendezvous/index.js.map +1 -1
- package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts +1 -1
- package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts.map +1 -1
- package/lib/rendezvous/transports/MSC4108RendezvousSession.js.map +1 -1
- package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
- package/lib/rust-crypto/rust-crypto.js +2 -2
- package/lib/rust-crypto/rust-crypto.js.map +1 -1
- package/lib/store/index.d.ts +17 -1
- package/lib/store/index.d.ts.map +1 -1
- package/lib/store/index.js.map +1 -1
- package/lib/store/indexeddb-backend.d.ts +4 -0
- package/lib/store/indexeddb-backend.d.ts.map +1 -1
- package/lib/store/indexeddb-backend.js.map +1 -1
- package/lib/store/indexeddb-local-backend.d.ts +4 -1
- package/lib/store/indexeddb-local-backend.d.ts.map +1 -1
- package/lib/store/indexeddb-local-backend.js +45 -3
- package/lib/store/indexeddb-local-backend.js.map +1 -1
- package/lib/store/indexeddb-remote-backend.d.ts +4 -0
- package/lib/store/indexeddb-remote-backend.d.ts.map +1 -1
- package/lib/store/indexeddb-remote-backend.js +21 -3
- package/lib/store/indexeddb-remote-backend.js.map +1 -1
- package/lib/store/indexeddb-store-worker.d.ts.map +1 -1
- package/lib/store/indexeddb-store-worker.js +10 -1
- package/lib/store/indexeddb-store-worker.js.map +1 -1
- package/lib/store/indexeddb.d.ts +4 -0
- package/lib/store/indexeddb.d.ts.map +1 -1
- package/lib/store/indexeddb.js +18 -0
- package/lib/store/indexeddb.js.map +1 -1
- package/lib/store/memory.d.ts +5 -1
- package/lib/store/memory.d.ts.map +1 -1
- package/lib/store/memory.js +19 -0
- package/lib/store/memory.js.map +1 -1
- package/lib/store/stub.d.ts +3 -0
- package/lib/store/stub.d.ts.map +1 -1
- package/lib/store/stub.js +15 -0
- package/lib/store/stub.js.map +1 -1
- package/lib/sync-accumulator.d.ts +15 -0
- package/lib/sync-accumulator.d.ts.map +1 -1
- package/lib/sync-accumulator.js +4 -0
- package/lib/sync-accumulator.js.map +1 -1
- package/lib/sync.d.ts +9 -1
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +51 -9
- package/lib/sync.js.map +1 -1
- package/lib/webrtc/call.d.ts.map +1 -1
- package/lib/webrtc/call.js +1 -2
- package/lib/webrtc/call.js.map +1 -1
- package/package.json +7 -7
- package/src/@types/json.ts +16 -0
- package/src/@types/requests.ts +6 -9
- package/src/client.ts +40 -12
- package/src/filter.ts +31 -5
- package/src/models/user.ts +6 -0
- package/src/oidc/authorize.ts +135 -2
- package/src/oidc/register.ts +5 -0
- package/src/rendezvous/MSC4108SignInWithQR.ts +117 -4
- package/src/rendezvous/channels/MSC4108SecureChannel.ts +10 -2
- package/src/rendezvous/index.ts +115 -0
- package/src/rendezvous/transports/MSC4108RendezvousSession.ts +1 -1
- package/src/rust-crypto/rust-crypto.ts +6 -3
- package/src/store/index.ts +20 -1
- package/src/store/indexeddb-backend.ts +4 -0
- package/src/store/indexeddb-local-backend.ts +32 -1
- package/src/store/indexeddb-remote-backend.ts +13 -0
- package/src/store/indexeddb-store-worker.ts +9 -0
- package/src/store/indexeddb.ts +13 -0
- package/src/store/memory.ts +14 -1
- package/src/store/stub.ts +12 -0
- package/src/sync-accumulator.ts +16 -1
- package/src/sync.ts +48 -4
- package/src/webrtc/call.ts +1 -2
package/src/rendezvous/index.ts
CHANGED
|
@@ -14,6 +14,14 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { type MatrixClient, OAuthGrantType, type OidcClientConfig } from "../matrix.ts";
|
|
18
|
+
import { MSC4108FailureReason, type RendezvousFailureListener } from "./RendezvousFailureReason.ts";
|
|
19
|
+
import { MSC4108SignInWithQR } from "./MSC4108SignInWithQR.ts";
|
|
20
|
+
import { MSC4108RendezvousSession } from "./transports/MSC4108RendezvousSession.ts";
|
|
21
|
+
import { MSC4108SecureChannel } from "./channels/MSC4108SecureChannel.ts";
|
|
22
|
+
import { RendezvousIntent } from "./RendezvousIntent.ts";
|
|
23
|
+
import { logger } from "../logger.ts";
|
|
24
|
+
|
|
17
25
|
export * from "./MSC4108SignInWithQR.ts";
|
|
18
26
|
export type * from "./RendezvousChannel.ts";
|
|
19
27
|
export type * from "./RendezvousCode.ts";
|
|
@@ -23,3 +31,110 @@ export * from "./RendezvousIntent.ts";
|
|
|
23
31
|
export type * from "./RendezvousTransport.ts";
|
|
24
32
|
export * from "./transports/index.ts";
|
|
25
33
|
export * from "./channels/index.ts";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if the homeserver that the client is connected to supports a variant of sign-in with QR that we can use.
|
|
37
|
+
*
|
|
38
|
+
* @param client the client to check for sign-in with QR support
|
|
39
|
+
* @returns true if the homeserver that the client is connected to supports a variant of sign-in with QR that we can use, false otherwise.
|
|
40
|
+
*/
|
|
41
|
+
export async function isSignInWithQRAvailable(client: MatrixClient): Promise<boolean> {
|
|
42
|
+
let metadata: OidcClientConfig;
|
|
43
|
+
try {
|
|
44
|
+
metadata = await client.getAuthMetadata();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
logger.warn("Failed to fetch auth metadata, assuming sign-in with QR is unavailable", e);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// check for support of device authorization grant
|
|
51
|
+
if (!metadata.grant_types_supported.includes(OAuthGrantType.DeviceAuthorization)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// check for unstable support for MSC4108 2024 version
|
|
56
|
+
return client.doesServerSupportUnstableFeature("org.matrix.msc4108");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Start a linking flow from an existing authenticated client by generating a QR code that can be scanned by the new device.
|
|
61
|
+
* The new device will then authenticate with the server and link itself to the same account as the existing client and
|
|
62
|
+
* share the end-to-end encryption keys.
|
|
63
|
+
*
|
|
64
|
+
* @param client the existing client
|
|
65
|
+
* @param onFailure callback for when the linking process fails
|
|
66
|
+
* @param abortSignal an AbortSignal that can be used to cancel the linking process,
|
|
67
|
+
* for example when the user cancels out of the flow.
|
|
68
|
+
* This will unbind the {@link onFailure} callback and prevent any further steps in the flow from being executed.
|
|
69
|
+
* @returns a promise that resolves to an instance of the linking flow
|
|
70
|
+
*/
|
|
71
|
+
export async function linkNewDeviceByGeneratingQR(
|
|
72
|
+
client: MatrixClient,
|
|
73
|
+
onFailure: RendezvousFailureListener,
|
|
74
|
+
abortSignal: AbortSignal,
|
|
75
|
+
): Promise<MSC4108SignInWithQR> {
|
|
76
|
+
// we assume rust crypto is already initialised
|
|
77
|
+
return initGenerateQrFlow(RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE, client, onFailure, abortSignal);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Start a sign-in flow by generating a QR code that can be scanned by an existing authenticated client.
|
|
82
|
+
* The existing client will then help complete the authentication of the new device and link it to the same account,
|
|
83
|
+
* sharing the end-to-end encryption keys.
|
|
84
|
+
*
|
|
85
|
+
* @param tempClient temporary client used during the flow for the rendezvous channel
|
|
86
|
+
* @param onFailure callback for when the sign-in process fails
|
|
87
|
+
* @param abortSignal an AbortSignal that can be used to cancel the linking process,
|
|
88
|
+
* for example when the user cancels out of the flow.
|
|
89
|
+
* This will unbind the {@link onFailure} callback and prevent any further steps in the flow from being executed.
|
|
90
|
+
* @returns a promise that resolves to an instance of the sign-in flow
|
|
91
|
+
*/
|
|
92
|
+
export async function signInByGeneratingQR(
|
|
93
|
+
tempClient: MatrixClient,
|
|
94
|
+
onFailure: RendezvousFailureListener,
|
|
95
|
+
abortSignal: AbortSignal,
|
|
96
|
+
): Promise<MSC4108SignInWithQR> {
|
|
97
|
+
// ensure rust crypto is initialized as needed for the secure channel
|
|
98
|
+
const RustSdkCryptoJs = await import("@matrix-org/matrix-sdk-crypto-wasm");
|
|
99
|
+
await RustSdkCryptoJs.initAsync();
|
|
100
|
+
|
|
101
|
+
return initGenerateQrFlow(RendezvousIntent.LOGIN_ON_NEW_DEVICE, tempClient, onFailure, abortSignal);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function initGenerateQrFlow(
|
|
105
|
+
intent: RendezvousIntent,
|
|
106
|
+
client: MatrixClient,
|
|
107
|
+
onFailure: RendezvousFailureListener,
|
|
108
|
+
abortSignal: AbortSignal,
|
|
109
|
+
): Promise<MSC4108SignInWithQR> {
|
|
110
|
+
const session = new MSC4108RendezvousSession({
|
|
111
|
+
onFailure,
|
|
112
|
+
client,
|
|
113
|
+
});
|
|
114
|
+
const channel = new MSC4108SecureChannel(session, undefined, onFailure);
|
|
115
|
+
const flow = new MSC4108SignInWithQR(
|
|
116
|
+
channel,
|
|
117
|
+
false,
|
|
118
|
+
intent === RendezvousIntent.LOGIN_ON_NEW_DEVICE ? undefined : client,
|
|
119
|
+
onFailure,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (abortSignal.aborted) return flow;
|
|
123
|
+
|
|
124
|
+
abortSignal.onabort = (): void => {
|
|
125
|
+
// Detach failure handlers
|
|
126
|
+
session.onFailure = undefined;
|
|
127
|
+
channel.onFailure = undefined;
|
|
128
|
+
flow.onFailure = undefined;
|
|
129
|
+
// Cancel the session
|
|
130
|
+
flow.cancel(MSC4108FailureReason.UserCancelled);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
await session.send(""); // open channel
|
|
134
|
+
|
|
135
|
+
if (!abortSignal.aborted) {
|
|
136
|
+
await flow.generateCode();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return flow;
|
|
140
|
+
}
|
|
@@ -30,7 +30,7 @@ export class MSC4108RendezvousSession {
|
|
|
30
30
|
private readonly client?: MatrixClient;
|
|
31
31
|
private readonly fallbackRzServer?: string;
|
|
32
32
|
private readonly fetchFn?: typeof globalThis.fetch;
|
|
33
|
-
|
|
33
|
+
public onFailure?: RendezvousFailureListener;
|
|
34
34
|
private etag?: string;
|
|
35
35
|
private expiresAt?: Date;
|
|
36
36
|
private expiresTimer?: ReturnType<typeof setTimeout>;
|
|
@@ -2596,7 +2596,7 @@ function rustEncryptionInfoToJsEncryptionInfo(
|
|
|
2596
2596
|
}
|
|
2597
2597
|
|
|
2598
2598
|
interface RoomKeyBundleMessage {
|
|
2599
|
-
type: "io.element.msc4268.room_key_bundle";
|
|
2599
|
+
type: "m.room_key_bundle" | "io.element.msc4268.room_key_bundle";
|
|
2600
2600
|
content: {
|
|
2601
2601
|
room_id: string;
|
|
2602
2602
|
};
|
|
@@ -2606,13 +2606,16 @@ interface RoomKeyBundleMessage {
|
|
|
2606
2606
|
* Determines if the given payload is a RoomKeyBundleMessage.
|
|
2607
2607
|
*
|
|
2608
2608
|
* A RoomKeyBundleMessage is identified by having a specific message type
|
|
2609
|
-
* ("
|
|
2609
|
+
* ("m.room_key_bundle") and a valid room_id in its content.
|
|
2610
2610
|
*
|
|
2611
2611
|
* @param message - The received to-device message to check.
|
|
2612
2612
|
* @returns True if the payload matches the RoomKeyBundleMessage structure, false otherwise.
|
|
2613
2613
|
*/
|
|
2614
2614
|
function isRoomKeyBundleMessage(message: IToDeviceEvent): message is IToDeviceEvent & RoomKeyBundleMessage {
|
|
2615
|
-
return
|
|
2615
|
+
return (
|
|
2616
|
+
(message.type === "io.element.msc4268.room_key_bundle" || message.type === "m.room_key_bundle") &&
|
|
2617
|
+
typeof message.content.room_id === "string"
|
|
2618
|
+
);
|
|
2616
2619
|
}
|
|
2617
2620
|
|
|
2618
2621
|
type CryptoEvents = (typeof CryptoEvent)[keyof typeof CryptoEvent];
|
package/src/store/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
import { type EventType } from "../@types/event.ts";
|
|
18
18
|
import { type Room } from "../models/room.ts";
|
|
19
|
-
import { type User } from "../models/user.ts";
|
|
19
|
+
import { type SyncUserProfile, type User } from "../models/user.ts";
|
|
20
20
|
import { type IEvent, type MatrixEvent } from "../models/event.ts";
|
|
21
21
|
import { type Filter } from "../filter.ts";
|
|
22
22
|
import { type RoomSummary } from "../models/room-summary.ts";
|
|
@@ -254,6 +254,25 @@ export interface IStore {
|
|
|
254
254
|
*/
|
|
255
255
|
removeToDeviceBatch(id: number): Promise<void>;
|
|
256
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Store user profile details from a sync. Existing profiles will be overwritten.
|
|
259
|
+
* @param userProfiles - A map of userIds to profiles.
|
|
260
|
+
*/
|
|
261
|
+
storeUserProfiles(userProfiles: Map<string, SyncUserProfile>): Promise<void>;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Delete stored profiles for the given users.
|
|
265
|
+
* @param userIds - The user IDs whose profiles should be deleted.
|
|
266
|
+
*/
|
|
267
|
+
removeUserProfiles(userIds: string[]): Promise<void>;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Retrieve a stored user profile for the given user ID, if it exists.
|
|
271
|
+
* @param userId - The user ID to retrieve the profile for.
|
|
272
|
+
* @returns The stored profile, or undefined if no profile is stored for this user ID.
|
|
273
|
+
*/
|
|
274
|
+
getUserProfile(userId: string): Promise<SyncUserProfile | undefined>;
|
|
275
|
+
|
|
257
276
|
/**
|
|
258
277
|
* Stop the store and perform any appropriate cleanup
|
|
259
278
|
*/
|
|
@@ -17,6 +17,7 @@ limitations under the License.
|
|
|
17
17
|
import { type ISavedSync } from "./index.ts";
|
|
18
18
|
import { type IEvent, type IStateEventWithRoomId, type IStoredClientOpts, type ISyncResponse } from "../matrix.ts";
|
|
19
19
|
import { type IndexedToDeviceBatch, type ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage.ts";
|
|
20
|
+
import { type SyncUserProfile } from "../models/user.ts";
|
|
20
21
|
|
|
21
22
|
export interface IIndexedDBBackend {
|
|
22
23
|
connect(onClose?: () => void): Promise<void>;
|
|
@@ -35,6 +36,9 @@ export interface IIndexedDBBackend {
|
|
|
35
36
|
saveToDeviceBatches(batches: ToDeviceBatchWithTxnId[]): Promise<void>;
|
|
36
37
|
getOldestToDeviceBatch(): Promise<IndexedToDeviceBatch | null>;
|
|
37
38
|
removeToDeviceBatch(id: number): Promise<void>;
|
|
39
|
+
getUserProfile(userId: string): Promise<SyncUserProfile | undefined>;
|
|
40
|
+
storeUserProfiles(userProfiles: Map<string, SyncUserProfile>): Promise<void>;
|
|
41
|
+
removeUserProfiles(userIds: string[]): Promise<void>;
|
|
38
42
|
destroy(): Promise<void>;
|
|
39
43
|
}
|
|
40
44
|
|
|
@@ -18,7 +18,7 @@ import { type IMinimalEvent, type ISyncData, type ISyncResponse, SyncAccumulator
|
|
|
18
18
|
import { deepCopy, promiseTry } from "../utils.ts";
|
|
19
19
|
import { exists as idbExists } from "../indexeddb-helpers.ts";
|
|
20
20
|
import { logger } from "../logger.ts";
|
|
21
|
-
import {
|
|
21
|
+
import type { SyncUserProfile, IStateEventWithRoomId, IStoredClientOpts } from "../matrix.ts";
|
|
22
22
|
import { type ISavedSync } from "./index.ts";
|
|
23
23
|
import { type IIndexedDBBackend, type UserTuple } from "./indexeddb-backend.ts";
|
|
24
24
|
import { type IndexedToDeviceBatch, type ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage.ts";
|
|
@@ -48,6 +48,9 @@ const DB_MIGRATIONS: DbMigration[] = [
|
|
|
48
48
|
(db): void => {
|
|
49
49
|
db.createObjectStore("to_device_queue", { autoIncrement: true });
|
|
50
50
|
},
|
|
51
|
+
(db): void => {
|
|
52
|
+
db.createObjectStore("user_profile", { keyPath: ["userId"] });
|
|
53
|
+
},
|
|
51
54
|
// Expand as needed.
|
|
52
55
|
];
|
|
53
56
|
const VERSION = DB_MIGRATIONS.length;
|
|
@@ -602,6 +605,34 @@ export class LocalIndexedDBStoreBackend implements IIndexedDBBackend {
|
|
|
602
605
|
await txnAsPromise(txn);
|
|
603
606
|
}
|
|
604
607
|
|
|
608
|
+
public async getUserProfile(userId: string): Promise<SyncUserProfile | undefined> {
|
|
609
|
+
return Promise.resolve().then(() => {
|
|
610
|
+
const txn = this.db!.transaction(["user_profile"], "readonly");
|
|
611
|
+
const store = txn.objectStore("user_profile");
|
|
612
|
+
return selectQuery(store, [userId], (cursor) => {
|
|
613
|
+
return cursor.value?.profile;
|
|
614
|
+
}).then((results) => results[0]);
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
public async storeUserProfiles(userProfiles: Map<string, SyncUserProfile>): Promise<void> {
|
|
619
|
+
const txn = this.db!.transaction(["user_profile"], "readwrite");
|
|
620
|
+
const store = txn.objectStore("user_profile");
|
|
621
|
+
for (const [userId, profile] of userProfiles.entries()) {
|
|
622
|
+
store.put({ profile, userId });
|
|
623
|
+
}
|
|
624
|
+
await txnAsPromise(txn);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
public async removeUserProfiles(userIds: string[]): Promise<void> {
|
|
628
|
+
const txn = this.db!.transaction(["user_profile"], "readwrite");
|
|
629
|
+
const store = txn.objectStore("user_profile");
|
|
630
|
+
for (const userId of userIds) {
|
|
631
|
+
store.delete([userId]);
|
|
632
|
+
}
|
|
633
|
+
await txnAsPromise(txn);
|
|
634
|
+
}
|
|
635
|
+
|
|
605
636
|
/*
|
|
606
637
|
* Close the database
|
|
607
638
|
*/
|
|
@@ -20,6 +20,7 @@ import { type IStoredClientOpts } from "../client.ts";
|
|
|
20
20
|
import { type IStateEventWithRoomId, type ISyncResponse } from "../matrix.ts";
|
|
21
21
|
import { type IIndexedDBBackend, type UserTuple } from "./indexeddb-backend.ts";
|
|
22
22
|
import { type IndexedToDeviceBatch, type ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage.ts";
|
|
23
|
+
import { type SyncUserProfile } from "../models/user.ts";
|
|
23
24
|
|
|
24
25
|
export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
|
25
26
|
private worker?: Worker;
|
|
@@ -145,6 +146,18 @@ export class RemoteIndexedDBStoreBackend implements IIndexedDBBackend {
|
|
|
145
146
|
return this.doCmd("removeToDeviceBatch", [id]);
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
public async getUserProfile(userId: string): Promise<SyncUserProfile | undefined> {
|
|
150
|
+
return this.doCmd("getUserProfile", [userId]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public async storeUserProfiles(userProfiles: Map<string, SyncUserProfile>): Promise<void> {
|
|
154
|
+
await this.doCmd("storeUserProfiles", [userProfiles]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public async removeUserProfiles(userIds: string[]): Promise<void> {
|
|
158
|
+
await this.doCmd("removeUserProfiles", [userIds]);
|
|
159
|
+
}
|
|
160
|
+
|
|
148
161
|
private ensureStarted(): Promise<void> {
|
|
149
162
|
if (!this.startPromise) {
|
|
150
163
|
this.worker = this.workerFactory();
|
|
@@ -120,6 +120,15 @@ export class IndexedDBStoreWorker {
|
|
|
120
120
|
case "removeToDeviceBatch":
|
|
121
121
|
prom = this.backend?.removeToDeviceBatch(msg.args[0]);
|
|
122
122
|
break;
|
|
123
|
+
case "getUserProfile":
|
|
124
|
+
prom = this.backend?.getUserProfile(msg.args[0]);
|
|
125
|
+
break;
|
|
126
|
+
case "storeUserProfiles":
|
|
127
|
+
prom = this.backend?.storeUserProfiles(msg.args[0]);
|
|
128
|
+
break;
|
|
129
|
+
case "removeUserProfiles":
|
|
130
|
+
prom = this.backend?.removeUserProfiles(msg.args[0]);
|
|
131
|
+
break;
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
if (prom === undefined) {
|
package/src/store/indexeddb.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { type EventEmitterEvents, TypedEventEmitter } from "../models/typed-even
|
|
|
28
28
|
import { type IStateEventWithRoomId } from "../@types/search.ts";
|
|
29
29
|
import { type IndexedToDeviceBatch, type ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage.ts";
|
|
30
30
|
import { type IStoredClientOpts } from "../client.ts";
|
|
31
|
+
import { type SyncUserProfile } from "../models/user.ts";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* This is an internal module. See {@link IndexedDBStore} for the public class.
|
|
@@ -384,6 +385,18 @@ export class IndexedDBStore extends MemoryStore {
|
|
|
384
385
|
public removeToDeviceBatch(id: number): Promise<void> {
|
|
385
386
|
return this.backend.removeToDeviceBatch(id);
|
|
386
387
|
}
|
|
388
|
+
|
|
389
|
+
public async getUserProfile(userId: string): Promise<SyncUserProfile | undefined> {
|
|
390
|
+
return this.backend.getUserProfile(userId);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
public async storeUserProfiles(userProfiles: Map<string, SyncUserProfile>): Promise<void> {
|
|
394
|
+
return this.backend.storeUserProfiles(userProfiles);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
public async removeUserProfiles(userIds: string[]): Promise<void> {
|
|
398
|
+
return this.backend.removeUserProfiles(userIds);
|
|
399
|
+
}
|
|
387
400
|
}
|
|
388
401
|
|
|
389
402
|
/**
|
package/src/store/memory.ts
CHANGED
|
@@ -20,7 +20,7 @@ limitations under the License.
|
|
|
20
20
|
|
|
21
21
|
import { type EventType } from "../@types/event.ts";
|
|
22
22
|
import { type Room } from "../models/room.ts";
|
|
23
|
-
import { type User } from "../models/user.ts";
|
|
23
|
+
import { type SyncUserProfile, type User } from "../models/user.ts";
|
|
24
24
|
import { type IEvent, type MatrixEvent } from "../models/event.ts";
|
|
25
25
|
import { type RoomState, RoomStateEvent } from "../models/room-state.ts";
|
|
26
26
|
import { type RoomMember } from "../models/room-member.ts";
|
|
@@ -65,6 +65,7 @@ export class MemoryStore implements IStore {
|
|
|
65
65
|
private pendingToDeviceBatches: IndexedToDeviceBatch[] = [];
|
|
66
66
|
private nextToDeviceBatchId = 0;
|
|
67
67
|
protected createUser?: UserCreator;
|
|
68
|
+
public readonly userProfiles = new Map<string, SyncUserProfile>();
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
71
|
* Construct a new in-memory data store for the Matrix Client.
|
|
@@ -442,6 +443,18 @@ export class MemoryStore implements IStore {
|
|
|
442
443
|
return Promise.resolve();
|
|
443
444
|
}
|
|
444
445
|
|
|
446
|
+
public async getUserProfile(userId: string): Promise<SyncUserProfile | undefined> {
|
|
447
|
+
return this.userProfiles.get(userId);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
public async storeUserProfiles(userProfiles: Map<string, SyncUserProfile>): Promise<void> {
|
|
451
|
+
userProfiles.forEach((profile, userId) => this.userProfiles.set(userId, profile));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
public async removeUserProfiles(userIds: string[]): Promise<void> {
|
|
455
|
+
userIds.forEach((userId) => this.userProfiles.delete(userId));
|
|
456
|
+
}
|
|
457
|
+
|
|
445
458
|
public async destroy(): Promise<void> {
|
|
446
459
|
// Nothing to do
|
|
447
460
|
}
|
package/src/store/stub.ts
CHANGED
|
@@ -274,6 +274,18 @@ export class StubStore implements IStore {
|
|
|
274
274
|
return Promise.resolve();
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
public async getUserProfile(): Promise<undefined> {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public async storeUserProfiles(): Promise<void> {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public async removeUserProfiles(): Promise<void> {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
277
289
|
public async destroy(): Promise<void> {
|
|
278
290
|
// Nothing to do
|
|
279
291
|
}
|
package/src/sync-accumulator.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { type EventType } from "./@types/event.ts";
|
|
|
26
26
|
import { UNREAD_THREAD_NOTIFICATIONS } from "./@types/sync.ts";
|
|
27
27
|
import { ReceiptAccumulator } from "./receipt-accumulator.ts";
|
|
28
28
|
import { type OlmEncryptionInfo } from "./crypto-api/index.ts";
|
|
29
|
+
import { type SyncUserProfile } from "./matrix.ts";
|
|
29
30
|
|
|
30
31
|
interface IOpts {
|
|
31
32
|
/**
|
|
@@ -36,6 +37,10 @@ interface IOpts {
|
|
|
36
37
|
* Default: 50.
|
|
37
38
|
*/
|
|
38
39
|
maxTimelineEntries?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to use the stable or unstable fields for user profiles.
|
|
42
|
+
*/
|
|
43
|
+
profileFieldsStable?: boolean;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
export interface IMinimalEvent {
|
|
@@ -183,6 +188,15 @@ export interface IDeviceLists {
|
|
|
183
188
|
left?: string[];
|
|
184
189
|
}
|
|
185
190
|
|
|
191
|
+
/**
|
|
192
|
+
* The "users" section of the sync update which contains extended profile updates.
|
|
193
|
+
*/
|
|
194
|
+
export interface UsersUpdate {
|
|
195
|
+
[userId: string]: {
|
|
196
|
+
profile_updates?: SyncUserProfile | null;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
186
200
|
export interface ISyncResponse {
|
|
187
201
|
"next_batch": string;
|
|
188
202
|
"rooms": IRooms;
|
|
@@ -191,7 +205,8 @@ export interface ISyncResponse {
|
|
|
191
205
|
"to_device"?: IToDevice;
|
|
192
206
|
"device_lists"?: IDeviceLists;
|
|
193
207
|
"device_one_time_keys_count"?: Record<string, number>;
|
|
194
|
-
|
|
208
|
+
"users"?: UsersUpdate;
|
|
209
|
+
"org.matrix.msc4429.users"?: UsersUpdate;
|
|
195
210
|
"device_unused_fallback_key_types"?: string[];
|
|
196
211
|
"org.matrix.msc2732.device_unused_fallback_key_types"?: string[];
|
|
197
212
|
}
|
package/src/sync.ts
CHANGED
|
@@ -24,12 +24,12 @@ limitations under the License.
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import type { SyncCryptoCallbacks } from "./common-crypto/CryptoBackend.ts";
|
|
27
|
-
import { User } from "./models/user.ts";
|
|
27
|
+
import { type SyncUserProfile, User } from "./models/user.ts";
|
|
28
28
|
import { NotificationCountType, Room, RoomEvent } from "./models/room.ts";
|
|
29
29
|
import { deepCopy, noUnsafeEventProps, unsafeProp } from "./utils.ts";
|
|
30
30
|
import { Filter } from "./filter.ts";
|
|
31
31
|
import { EventTimeline } from "./models/event-timeline.ts";
|
|
32
|
-
import { type Logger } from "./logger.ts";
|
|
32
|
+
import { logger, type Logger } from "./logger.ts";
|
|
33
33
|
import {
|
|
34
34
|
ClientEvent,
|
|
35
35
|
type IStoredClientOpts,
|
|
@@ -622,7 +622,11 @@ export class SyncApi {
|
|
|
622
622
|
return filter;
|
|
623
623
|
};
|
|
624
624
|
|
|
625
|
-
|
|
625
|
+
/**
|
|
626
|
+
* Sets up the sync filter options for lazy loading if enabled,
|
|
627
|
+
* (or force-disables lazy loading entirely if we're a guest).
|
|
628
|
+
*/
|
|
629
|
+
private prepareSyncFilterLazyLoading = (): void => {
|
|
626
630
|
this.syncOpts.logger.debug("Prepare lazy loading for sync...");
|
|
627
631
|
if (this.client.isGuest()) {
|
|
628
632
|
this.opts.lazyLoadMembers = false;
|
|
@@ -636,6 +640,22 @@ export class SyncApi {
|
|
|
636
640
|
}
|
|
637
641
|
};
|
|
638
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Preare sync filter options for the unstable MSC4429 user profile fields if enabled.
|
|
645
|
+
*/
|
|
646
|
+
private prepareSyncFilterUserProfiles = async (): Promise<void> => {
|
|
647
|
+
if (this.opts.unstableMSC4429SyncUserProfileFields?.length) {
|
|
648
|
+
this.syncOpts.logger.debug("Enabling EXPERIMENTAL user profiles on sync filter...");
|
|
649
|
+
if (!this.opts.filter) {
|
|
650
|
+
this.opts.filter = this.buildDefaultFilter();
|
|
651
|
+
}
|
|
652
|
+
this.opts.filter.setUnstableMSC4429SyncUserProfiles(
|
|
653
|
+
this.opts.unstableMSC4429SyncUserProfileFields,
|
|
654
|
+
await this.client.doesServerSupportUnstableFeature("org.matrix.msc4429.stable"),
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
|
|
639
659
|
private storeClientOptions = async (): Promise<void> => {
|
|
640
660
|
try {
|
|
641
661
|
this.syncOpts.logger.debug("Storing client options...");
|
|
@@ -723,7 +743,8 @@ export class SyncApi {
|
|
|
723
743
|
// take a while so if we set it going now, we can wait for it
|
|
724
744
|
// to finish while we process our saved sync data.
|
|
725
745
|
await this.getPushRules();
|
|
726
|
-
|
|
746
|
+
this.prepareSyncFilterLazyLoading();
|
|
747
|
+
await this.prepareSyncFilterUserProfiles();
|
|
727
748
|
await this.storeClientOptions();
|
|
728
749
|
|
|
729
750
|
const { filterId, filter } = await this.getFilter();
|
|
@@ -1138,6 +1159,29 @@ export class SyncApi {
|
|
|
1138
1159
|
});
|
|
1139
1160
|
}
|
|
1140
1161
|
|
|
1162
|
+
// handle user profile updates (MSC4429)
|
|
1163
|
+
const userUpdate = data["users"] ?? data["org.matrix.msc4429.users"];
|
|
1164
|
+
if (typeof userUpdate === "object" && userUpdate !== null) {
|
|
1165
|
+
const usersToRemove: string[] = [];
|
|
1166
|
+
const profilesToAmend: Map<string, SyncUserProfile> = new Map();
|
|
1167
|
+
for (const [userId, userData] of Object.entries(userUpdate)) {
|
|
1168
|
+
logger.info(`Storing user profile ${userId}`, userData);
|
|
1169
|
+
if (userData.profile_updates) {
|
|
1170
|
+
client.emit(ClientEvent.UserProfileUpdate, userId, userData.profile_updates);
|
|
1171
|
+
const existingProfile = await client.store.getUserProfile(userId);
|
|
1172
|
+
profilesToAmend.set(userId, { ...existingProfile, ...userData.profile_updates });
|
|
1173
|
+
} else if (userData.profile_updates === null) {
|
|
1174
|
+
usersToRemove.push(userId);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (usersToRemove.length) {
|
|
1178
|
+
await client.store.removeUserProfiles(usersToRemove);
|
|
1179
|
+
}
|
|
1180
|
+
if (profilesToAmend.size) {
|
|
1181
|
+
await client.store.storeUserProfiles(profilesToAmend);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1141
1185
|
// handle to-device events
|
|
1142
1186
|
if (data.to_device && Array.isArray(data.to_device.events) && data.to_device.events.length > 0) {
|
|
1143
1187
|
const toDeviceMessages: IToDeviceEvent[] = data.to_device.events.filter(noUnsafeEventProps);
|
package/src/webrtc/call.ts
CHANGED
|
@@ -21,7 +21,6 @@ limitations under the License.
|
|
|
21
21
|
* This is an internal module. See {@link createNewMatrixCall} for the public API.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { v4 as uuidv4 } from "uuid";
|
|
25
24
|
import { parse as parseSdp, write as writeSdp } from "sdp-transform";
|
|
26
25
|
|
|
27
26
|
import { logger } from "../logger.ts";
|
|
@@ -2490,7 +2489,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
|
|
2490
2489
|
sender_session_id: this.client.getSessionId(),
|
|
2491
2490
|
dest_session_id: this.opponentSessionId,
|
|
2492
2491
|
seq: toDeviceSeq,
|
|
2493
|
-
[ToDeviceMessageId]:
|
|
2492
|
+
[ToDeviceMessageId]: globalThis.crypto.randomUUID(),
|
|
2494
2493
|
};
|
|
2495
2494
|
|
|
2496
2495
|
this.emit(
|