From a05501a90937a4f73099bf3f8f761d31444ffecc Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 May 2022 10:09:13 +0100 Subject: [PATCH 1/6] Convert matrix-utils to typescript --- src/@types/global.d.ts | 7 ++++ src/{matrix-utils.js => matrix-utils.ts} | 46 +++++++++++++----------- tsconfig.json | 2 +- 3 files changed, 33 insertions(+), 22 deletions(-) rename src/{matrix-utils.js => matrix-utils.ts} (75%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 7b9c1d9..35b93a7 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -15,3 +15,10 @@ limitations under the License. */ import "matrix-js-sdk/src/@types/global"; + +declare global { + interface Window { + // TODO: https://gitlab.matrix.org/matrix-org/olm/-/issues/10 + OLM_OPTIONS: Record; + } +} diff --git a/src/matrix-utils.js b/src/matrix-utils.ts similarity index 75% rename from src/matrix-utils.js rename to src/matrix-utils.ts index dba6f16..4f01e70 100644 --- a/src/matrix-utils.js +++ b/src/matrix-utils.ts @@ -1,20 +1,24 @@ -import matrix from "matrix-js-sdk/src/browser-index"; -import { - GroupCallIntent, - GroupCallType, -} from "matrix-js-sdk/src/browser-index"; -import IndexedDBWorker from "./IndexedDBWorker?worker"; import Olm from "@matrix-org/olm"; import olmWasmPath from "@matrix-org/olm/olm.wasm?url"; +import { IndexedDBStore } from "matrix-js-sdk/src/store/indexeddb"; +import { WebStorageSessionStore } from "matrix-js-sdk/src/store/session/webstorage"; +import { MemoryStore } from "matrix-js-sdk/src/store/memory"; +import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; +import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix"; +import { ICreateClientOpts } from "matrix-js-sdk/src/matrix"; +import { Visibility, Preset, GroupCallIntent } from "matrix-js-sdk"; +import { GroupCallType } from "matrix-js-sdk"; + +import IndexedDBWorker from "./IndexedDBWorker?worker"; export const defaultHomeserver = - import.meta.env.VITE_DEFAULT_HOMESERVER || + (import.meta.env.VITE_DEFAULT_HOMESERVER as string) ?? `${window.location.protocol}//${window.location.host}`; export const defaultHomeserverHost = new URL(defaultHomeserver).host; function waitForSync(client) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const onSync = (state, _old, data) => { if (state === "PREPARED") { resolve(); @@ -28,7 +32,7 @@ function waitForSync(client) { }); } -export async function initClient(clientOptions) { +export async function initClient(clientOptions: ICreateClientOpts) { // TODO: https://gitlab.matrix.org/matrix-org/olm/-/issues/10 window.OLM_OPTIONS = {}; await Olm.init({ locateFile: () => olmWasmPath }); @@ -39,10 +43,10 @@ export async function initClient(clientOptions) { indexedDB = window.indexedDB; } catch (e) {} - const storeOpts = {}; + const storeOpts = {} as ICreateClientOpts; if (indexedDB && localStorage && !import.meta.env.DEV) { - storeOpts.store = new matrix.IndexedDBStore({ + storeOpts.store = new IndexedDBStore({ indexedDB: window.indexedDB, localStorage: window.localStorage, dbName: "element-call-sync", @@ -51,17 +55,17 @@ export async function initClient(clientOptions) { } if (localStorage) { - storeOpts.sessionStore = new matrix.WebStorageSessionStore(localStorage); + storeOpts.sessionStore = new WebStorageSessionStore(localStorage); } if (indexedDB) { - storeOpts.cryptoStore = new matrix.IndexedDBCryptoStore( + storeOpts.cryptoStore = new IndexedDBCryptoStore( indexedDB, "matrix-js-sdk:crypto" ); } - const client = matrix.createClient({ + const client = createClient({ ...storeOpts, ...clientOptions, useAuthorizationHeader: true, @@ -74,7 +78,7 @@ export async function initClient(clientOptions) { "Error starting matrix client store. Falling back to memory store.", error ); - client.store = new matrix.MemoryStore({ localStorage }); + client.store = new MemoryStore({ localStorage }); await client.store.startup(); } @@ -127,10 +131,10 @@ export function isLocalRoomId(roomId) { return parts[1] === defaultHomeserverHost; } -export async function createRoom(client, name, isPtt = false) { - const { room_id, room_alias } = await client.createRoom({ - visibility: "private", - preset: "public_chat", +export async function createRoom(client: MatrixClient, name, isPtt = false) { + const createRoomResult = await client.createRoom({ + visibility: Visibility.Private, + preset: Preset.PublicChat, name, room_alias_name: roomAliasFromRoomName(name), power_level_content_override: { @@ -161,13 +165,13 @@ export async function createRoom(client, name, isPtt = false) { console.log({ isPtt }); await client.createGroupCall( - room_id, + createRoomResult.room_id, isPtt ? GroupCallType.Voice : GroupCallType.Video, isPtt, GroupCallIntent.Prompt ); - return room_alias || room_id; + return createRoomResult.room_id; } export function getRoomUrl(roomId) { diff --git a/tsconfig.json b/tsconfig.json index 0de9b90..437a94f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es2016", "esModuleInterop": true, - "module": "commonjs", + "module": "es2020", "moduleResolution": "node", "noEmit": true, "noImplicitAny": false, From 7bd95621f1e5f898198792ad59f739dfbd4a2f68 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 May 2022 11:28:16 +0100 Subject: [PATCH 2/6] More types --- src/matrix-utils.ts | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 4f01e70..0faa1c6 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -6,8 +6,10 @@ import { MemoryStore } from "matrix-js-sdk/src/store/memory"; import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix"; import { ICreateClientOpts } from "matrix-js-sdk/src/matrix"; +import { ClientEvent } from "matrix-js-sdk/src/client"; import { Visibility, Preset, GroupCallIntent } from "matrix-js-sdk"; import { GroupCallType } from "matrix-js-sdk"; +import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; import IndexedDBWorker from "./IndexedDBWorker?worker"; @@ -17,22 +19,28 @@ export const defaultHomeserver = export const defaultHomeserverHost = new URL(defaultHomeserver).host; -function waitForSync(client) { +function waitForSync(client: MatrixClient) { return new Promise((resolve, reject) => { - const onSync = (state, _old, data) => { + const onSync = ( + state: SyncState, + _old: SyncState, + data: ISyncStateData + ) => { if (state === "PREPARED") { resolve(); - client.removeListener("sync", onSync); + client.removeListener(ClientEvent.Sync, onSync); } else if (state === "ERROR") { reject(data?.error); - client.removeListener("sync", onSync); + client.removeListener(ClientEvent.Sync, onSync); } }; - client.on("sync", onSync); + client.on(ClientEvent.Sync, onSync); }); } -export async function initClient(clientOptions: ICreateClientOpts) { +export async function initClient( + clientOptions: ICreateClientOpts +): Promise { // TODO: https://gitlab.matrix.org/matrix-org/olm/-/issues/10 window.OLM_OPTIONS = {}; await Olm.init({ locateFile: () => olmWasmPath }); @@ -97,7 +105,7 @@ export async function initClient(clientOptions: ICreateClientOpts) { return client; } -export function roomAliasFromRoomName(roomName) { +export function roomAliasFromRoomName(roomName: string): string { return roomName .trim() .replace(/\s/g, "-") @@ -105,7 +113,7 @@ export function roomAliasFromRoomName(roomName) { .toLowerCase(); } -export function roomNameFromRoomId(roomId) { +export function roomNameFromRoomId(roomId: string): string { return roomId .match(/([^:]+):.*$/)[1] .substring(1) @@ -117,7 +125,7 @@ export function roomNameFromRoomId(roomId) { .toLowerCase(); } -export function isLocalRoomId(roomId) { +export function isLocalRoomId(roomId: string): boolean { if (!roomId) { return false; } @@ -131,7 +139,11 @@ export function isLocalRoomId(roomId) { return parts[1] === defaultHomeserverHost; } -export async function createRoom(client: MatrixClient, name, isPtt = false) { +export async function createRoom( + client: MatrixClient, + name: string, + isPtt = false +): Promise { const createRoomResult = await client.createRoom({ visibility: Visibility.Private, preset: Preset.PublicChat, @@ -174,7 +186,7 @@ export async function createRoom(client: MatrixClient, name, isPtt = false) { return createRoomResult.room_id; } -export function getRoomUrl(roomId) { +export function getRoomUrl(roomId: string): string { if (roomId.startsWith("#")) { const [localPart, host] = roomId.replace("#", "").split(":"); @@ -188,7 +200,11 @@ export function getRoomUrl(roomId) { } } -export function getAvatarUrl(client, mxcUrl, avatarSize = 96) { +export function getAvatarUrl( + client: MatrixClient, + mxcUrl: string, + avatarSize = 96 +): string { const width = Math.floor(avatarSize * window.devicePixelRatio); const height = Math.floor(avatarSize * window.devicePixelRatio); return mxcUrl && client.mxcUrlToHttp(mxcUrl, width, height, "crop"); From 2cdbeb6f120bbb9ddd5413052b6c722b667d74ec Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 May 2022 11:41:59 +0100 Subject: [PATCH 3/6] Fix imports --- src/matrix-utils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 0faa1c6..e3617c5 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -7,8 +7,11 @@ import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-c import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix"; import { ICreateClientOpts } from "matrix-js-sdk/src/matrix"; import { ClientEvent } from "matrix-js-sdk/src/client"; -import { Visibility, Preset, GroupCallIntent } from "matrix-js-sdk"; -import { GroupCallType } from "matrix-js-sdk"; +import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials"; +import { + GroupCallIntent, + GroupCallType, +} from "matrix-js-sdk/src/webrtc/groupCall"; import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; import IndexedDBWorker from "./IndexedDBWorker?worker"; From 9edc1acc9000635739070abccc32205ae1cb99f7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 1 Jun 2022 09:07:00 +0100 Subject: [PATCH 4/6] Add type to indexeddb variable --- src/matrix-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index e3617c5..7f98f9f 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -48,7 +48,7 @@ export async function initClient( window.OLM_OPTIONS = {}; await Olm.init({ locateFile: () => olmWasmPath }); - let indexedDB; + let indexedDB: IDBFactory; try { indexedDB = window.indexedDB; From 2cf40ff0b813162e741414c70646f3bfacaec896 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 1 Jun 2022 09:29:47 +0100 Subject: [PATCH 5/6] Fix room creation The room alias is not part of the spec. Synapse returns it anyway, but it's not part of the js-sdk types. We don't really need the server to tell us what the alias is, so just generate it locally instead. --- src/home/RegisteredView.jsx | 4 ++-- src/home/UnauthenticatedView.jsx | 4 ++-- src/matrix-utils.ts | 15 +++++++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/home/RegisteredView.jsx b/src/home/RegisteredView.jsx index 0fc1b3e..aaa8a33 100644 --- a/src/home/RegisteredView.jsx +++ b/src/home/RegisteredView.jsx @@ -15,7 +15,7 @@ limitations under the License. */ import React, { useState, useCallback } from "react"; -import { createRoom, roomAliasFromRoomName } from "../matrix-utils"; +import { createRoom, roomAliasLocalpartFromRoomName } from "../matrix-utils"; import { useGroupCallRooms } from "./useGroupCallRooms"; import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; import commonStyles from "./common.module.css"; @@ -56,7 +56,7 @@ export function RegisteredView({ client }) { submit().catch((error) => { if (error.errcode === "M_ROOM_IN_USE") { - setExistingRoomId(roomAliasFromRoomName(roomName)); + setExistingRoomId(roomAliasLocalpartFromRoomName(roomName)); setLoading(false); setError(undefined); modalState.open(); diff --git a/src/home/UnauthenticatedView.jsx b/src/home/UnauthenticatedView.jsx index 51e8eed..aea8ae8 100644 --- a/src/home/UnauthenticatedView.jsx +++ b/src/home/UnauthenticatedView.jsx @@ -22,7 +22,7 @@ import { useHistory } from "react-router-dom"; import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { Button } from "../button"; import { randomString } from "matrix-js-sdk/src/randomstring"; -import { createRoom, roomAliasFromRoomName } from "../matrix-utils"; +import { createRoom, roomAliasLocalpartFromRoomName } from "../matrix-utils"; import { useInteractiveRegistration } from "../auth/useInteractiveRegistration"; import { useModalTriggerState } from "../Modal"; import { JoinExistingCallModal } from "./JoinExistingCallModal"; @@ -75,7 +75,7 @@ export function UnauthenticatedView() { if (error.errcode === "M_ROOM_IN_USE") { setOnFinished(() => () => { setClient(client, session); - const aliasLocalpart = roomAliasFromRoomName(roomName); + const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName); const [, serverName] = client.getUserId().split(":"); history.push(`/room/#${aliasLocalpart}:${serverName}`); }); diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 7f98f9f..2f7897b 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -108,7 +108,7 @@ export async function initClient( return client; } -export function roomAliasFromRoomName(roomName: string): string { +export function roomAliasLocalpartFromRoomName(roomName: string): string { return roomName .trim() .replace(/\s/g, "-") @@ -116,6 +116,13 @@ export function roomAliasFromRoomName(roomName: string): string { .toLowerCase(); } +export function fullAliasFromRoomName( + roomName: string, + client: MatrixClient +): string { + return `#${roomAliasLocalpartFromRoomName(roomName)}:${client.getDomain()}`; +} + export function roomNameFromRoomId(roomId: string): string { return roomId .match(/([^:]+):.*$/)[1] @@ -151,7 +158,7 @@ export async function createRoom( visibility: Visibility.Private, preset: Preset.PublicChat, name, - room_alias_name: roomAliasFromRoomName(name), + room_alias_name: roomAliasLocalpartFromRoomName(name), power_level_content_override: { invite: 100, kick: 100, @@ -177,7 +184,7 @@ export async function createRoom( }, }); - console.log({ isPtt }); + console.log(`Creating ${isPtt ? "PTT" : "video"} group call room`); await client.createGroupCall( createRoomResult.room_id, @@ -186,7 +193,7 @@ export async function createRoom( GroupCallIntent.Prompt ); - return createRoomResult.room_id; + return fullAliasFromRoomName(name, client); } export function getRoomUrl(roomId: string): string { From 7ee2f630db0e97f4f61c79a2366ae2f75c958fb8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 1 Jun 2022 09:59:59 +0100 Subject: [PATCH 6/6] Add more typers to useInteractiveLogin otherwise apparently Typescript can't trace the MatrixClient type through. --- src/auth/useInteractiveLogin.ts | 68 +++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/auth/useInteractiveLogin.ts b/src/auth/useInteractiveLogin.ts index b4e3ad2..2cfe78c 100644 --- a/src/auth/useInteractiveLogin.ts +++ b/src/auth/useInteractiveLogin.ts @@ -16,40 +16,50 @@ limitations under the License. import { useCallback } from "react"; import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index"; +import { MatrixClient } from "matrix-js-sdk"; import { initClient, defaultHomeserver } from "../matrix-utils"; +import { Session } from "../ClientContext"; export const useInteractiveLogin = () => - useCallback( - async (homeserver: string, username: string, password: string) => { - const authClient = matrix.createClient(homeserver); + useCallback< + ( + homeserver: string, + username: string, + password: string + ) => Promise<[MatrixClient, Session]> + >(async (homeserver: string, username: string, password: string) => { + const authClient = matrix.createClient(homeserver); - const interactiveAuth = new InteractiveAuth({ - matrixClient: authClient, - doRequest: () => - authClient.login("m.login.password", { - identifier: { - type: "m.id.user", - user: username, - }, - password, - }), - }); + const interactiveAuth = new InteractiveAuth({ + matrixClient: authClient, + doRequest: () => + authClient.login("m.login.password", { + identifier: { + type: "m.id.user", + user: username, + }, + password, + }), + }); - /* eslint-disable camelcase */ - const { user_id, access_token, device_id } = - await interactiveAuth.attemptAuth(); - const session = { user_id, access_token, device_id }; + /* eslint-disable camelcase */ + const { user_id, access_token, device_id } = + await interactiveAuth.attemptAuth(); + const session = { + user_id, + access_token, + device_id, + passwordlessUser: false, + }; - const client = await initClient({ - baseUrl: defaultHomeserver, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }); - /* eslint-enable camelcase */ + const client = await initClient({ + baseUrl: defaultHomeserver, + accessToken: access_token, + userId: user_id, + deviceId: device_id, + }); + /* eslint-enable camelcase */ - return [client, session]; - }, - [] - ); + return [client, session]; + }, []);