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/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]; + }, []); 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 8c2653c..6af34c5 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.js b/src/matrix-utils.ts similarity index 58% rename from src/matrix-utils.js rename to src/matrix-utils.ts index dba6f16..2f7897b 100644 --- a/src/matrix-utils.js +++ b/src/matrix-utils.ts @@ -1,48 +1,63 @@ -import matrix from "matrix-js-sdk/src/browser-index"; +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 { ClientEvent } from "matrix-js-sdk/src/client"; +import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials"; import { GroupCallIntent, GroupCallType, -} from "matrix-js-sdk/src/browser-index"; +} from "matrix-js-sdk/src/webrtc/groupCall"; +import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; + import IndexedDBWorker from "./IndexedDBWorker?worker"; -import Olm from "@matrix-org/olm"; -import olmWasmPath from "@matrix-org/olm/olm.wasm?url"; 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) => { - const onSync = (state, _old, data) => { +function waitForSync(client: MatrixClient) { + return new Promise((resolve, reject) => { + 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) { +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 }); - let indexedDB; + let indexedDB: IDBFactory; try { 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 +66,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 +89,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(); } @@ -93,7 +108,7 @@ export async function initClient(clientOptions) { return client; } -export function roomAliasFromRoomName(roomName) { +export function roomAliasLocalpartFromRoomName(roomName: string): string { return roomName .trim() .replace(/\s/g, "-") @@ -101,7 +116,14 @@ export function roomAliasFromRoomName(roomName) { .toLowerCase(); } -export function roomNameFromRoomId(roomId) { +export function fullAliasFromRoomName( + roomName: string, + client: MatrixClient +): string { + return `#${roomAliasLocalpartFromRoomName(roomName)}:${client.getDomain()}`; +} + +export function roomNameFromRoomId(roomId: string): string { return roomId .match(/([^:]+):.*$/)[1] .substring(1) @@ -113,7 +135,7 @@ export function roomNameFromRoomId(roomId) { .toLowerCase(); } -export function isLocalRoomId(roomId) { +export function isLocalRoomId(roomId: string): boolean { if (!roomId) { return false; } @@ -127,12 +149,16 @@ 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: string, + isPtt = false +): Promise { + const createRoomResult = await client.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, @@ -158,19 +184,19 @@ export async function createRoom(client, name, isPtt = false) { }, }); - console.log({ isPtt }); + console.log(`Creating ${isPtt ? "PTT" : "video"} group call room`); await client.createGroupCall( - room_id, + createRoomResult.room_id, isPtt ? GroupCallType.Voice : GroupCallType.Video, isPtt, GroupCallIntent.Prompt ); - return room_alias || room_id; + return fullAliasFromRoomName(name, client); } -export function getRoomUrl(roomId) { +export function getRoomUrl(roomId: string): string { if (roomId.startsWith("#")) { const [localPart, host] = roomId.replace("#", "").split(":"); @@ -184,7 +210,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"); 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,