Merge pull request #358 from vector-im/dbkr/matrix-utils-ts

Convert matrix-utils to typescript
This commit is contained in:
David Baker 2022-06-01 16:02:20 +01:00 committed by GitHub
commit 65804cd962
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 66 deletions

View file

@ -15,3 +15,10 @@ limitations under the License.
*/ */
import "matrix-js-sdk/src/@types/global"; import "matrix-js-sdk/src/@types/global";
declare global {
interface Window {
// TODO: https://gitlab.matrix.org/matrix-org/olm/-/issues/10
OLM_OPTIONS: Record<string, string>;
}
}

View file

@ -16,40 +16,50 @@ limitations under the License.
import { useCallback } from "react"; import { useCallback } from "react";
import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index"; import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index";
import { MatrixClient } from "matrix-js-sdk";
import { initClient, defaultHomeserver } from "../matrix-utils"; import { initClient, defaultHomeserver } from "../matrix-utils";
import { Session } from "../ClientContext";
export const useInteractiveLogin = () => export const useInteractiveLogin = () =>
useCallback( useCallback<
async (homeserver: string, username: string, password: string) => { (
const authClient = matrix.createClient(homeserver); 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({ const interactiveAuth = new InteractiveAuth({
matrixClient: authClient, matrixClient: authClient,
doRequest: () => doRequest: () =>
authClient.login("m.login.password", { authClient.login("m.login.password", {
identifier: { identifier: {
type: "m.id.user", type: "m.id.user",
user: username, user: username,
}, },
password, password,
}), }),
}); });
/* eslint-disable camelcase */ /* eslint-disable camelcase */
const { user_id, access_token, device_id } = const { user_id, access_token, device_id } =
await interactiveAuth.attemptAuth(); await interactiveAuth.attemptAuth();
const session = { user_id, access_token, device_id }; const session = {
user_id,
access_token,
device_id,
passwordlessUser: false,
};
const client = await initClient({ const client = await initClient({
baseUrl: defaultHomeserver, baseUrl: defaultHomeserver,
accessToken: access_token, accessToken: access_token,
userId: user_id, userId: user_id,
deviceId: device_id, deviceId: device_id,
}); });
/* eslint-enable camelcase */ /* eslint-enable camelcase */
return [client, session]; return [client, session];
}, }, []);
[]
);

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React, { useState, useCallback } from "react"; import React, { useState, useCallback } from "react";
import { createRoom, roomAliasFromRoomName } from "../matrix-utils"; import { createRoom, roomAliasLocalpartFromRoomName } from "../matrix-utils";
import { useGroupCallRooms } from "./useGroupCallRooms"; import { useGroupCallRooms } from "./useGroupCallRooms";
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
import commonStyles from "./common.module.css"; import commonStyles from "./common.module.css";
@ -56,7 +56,7 @@ export function RegisteredView({ client }) {
submit().catch((error) => { submit().catch((error) => {
if (error.errcode === "M_ROOM_IN_USE") { if (error.errcode === "M_ROOM_IN_USE") {
setExistingRoomId(roomAliasFromRoomName(roomName)); setExistingRoomId(roomAliasLocalpartFromRoomName(roomName));
setLoading(false); setLoading(false);
setError(undefined); setError(undefined);
modalState.open(); modalState.open();

View file

@ -22,7 +22,7 @@ import { useHistory } from "react-router-dom";
import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { FieldRow, InputField, ErrorMessage } from "../input/Input";
import { Button } from "../button"; import { Button } from "../button";
import { randomString } from "matrix-js-sdk/src/randomstring"; 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 { useInteractiveRegistration } from "../auth/useInteractiveRegistration";
import { useModalTriggerState } from "../Modal"; import { useModalTriggerState } from "../Modal";
import { JoinExistingCallModal } from "./JoinExistingCallModal"; import { JoinExistingCallModal } from "./JoinExistingCallModal";
@ -75,7 +75,7 @@ export function UnauthenticatedView() {
if (error.errcode === "M_ROOM_IN_USE") { if (error.errcode === "M_ROOM_IN_USE") {
setOnFinished(() => () => { setOnFinished(() => () => {
setClient(client, session); setClient(client, session);
const aliasLocalpart = roomAliasFromRoomName(roomName); const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName);
const [, serverName] = client.getUserId().split(":"); const [, serverName] = client.getUserId().split(":");
history.push(`/room/#${aliasLocalpart}:${serverName}`); history.push(`/room/#${aliasLocalpart}:${serverName}`);
}); });

View file

@ -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 { import {
GroupCallIntent, GroupCallIntent,
GroupCallType, 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 IndexedDBWorker from "./IndexedDBWorker?worker";
import Olm from "@matrix-org/olm";
import olmWasmPath from "@matrix-org/olm/olm.wasm?url";
export const defaultHomeserver = export const defaultHomeserver =
import.meta.env.VITE_DEFAULT_HOMESERVER || (import.meta.env.VITE_DEFAULT_HOMESERVER as string) ??
`${window.location.protocol}//${window.location.host}`; `${window.location.protocol}//${window.location.host}`;
export const defaultHomeserverHost = new URL(defaultHomeserver).host; export const defaultHomeserverHost = new URL(defaultHomeserver).host;
function waitForSync(client) { function waitForSync(client: MatrixClient) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const onSync = (state, _old, data) => { const onSync = (
state: SyncState,
_old: SyncState,
data: ISyncStateData
) => {
if (state === "PREPARED") { if (state === "PREPARED") {
resolve(); resolve();
client.removeListener("sync", onSync); client.removeListener(ClientEvent.Sync, onSync);
} else if (state === "ERROR") { } else if (state === "ERROR") {
reject(data?.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<MatrixClient> {
// TODO: https://gitlab.matrix.org/matrix-org/olm/-/issues/10 // TODO: https://gitlab.matrix.org/matrix-org/olm/-/issues/10
window.OLM_OPTIONS = {}; window.OLM_OPTIONS = {};
await Olm.init({ locateFile: () => olmWasmPath }); await Olm.init({ locateFile: () => olmWasmPath });
let indexedDB; let indexedDB: IDBFactory;
try { try {
indexedDB = window.indexedDB; indexedDB = window.indexedDB;
} catch (e) {} } catch (e) {}
const storeOpts = {}; const storeOpts = {} as ICreateClientOpts;
if (indexedDB && localStorage && !import.meta.env.DEV) { if (indexedDB && localStorage && !import.meta.env.DEV) {
storeOpts.store = new matrix.IndexedDBStore({ storeOpts.store = new IndexedDBStore({
indexedDB: window.indexedDB, indexedDB: window.indexedDB,
localStorage: window.localStorage, localStorage: window.localStorage,
dbName: "element-call-sync", dbName: "element-call-sync",
@ -51,17 +66,17 @@ export async function initClient(clientOptions) {
} }
if (localStorage) { if (localStorage) {
storeOpts.sessionStore = new matrix.WebStorageSessionStore(localStorage); storeOpts.sessionStore = new WebStorageSessionStore(localStorage);
} }
if (indexedDB) { if (indexedDB) {
storeOpts.cryptoStore = new matrix.IndexedDBCryptoStore( storeOpts.cryptoStore = new IndexedDBCryptoStore(
indexedDB, indexedDB,
"matrix-js-sdk:crypto" "matrix-js-sdk:crypto"
); );
} }
const client = matrix.createClient({ const client = createClient({
...storeOpts, ...storeOpts,
...clientOptions, ...clientOptions,
useAuthorizationHeader: true, useAuthorizationHeader: true,
@ -74,7 +89,7 @@ export async function initClient(clientOptions) {
"Error starting matrix client store. Falling back to memory store.", "Error starting matrix client store. Falling back to memory store.",
error error
); );
client.store = new matrix.MemoryStore({ localStorage }); client.store = new MemoryStore({ localStorage });
await client.store.startup(); await client.store.startup();
} }
@ -93,7 +108,7 @@ export async function initClient(clientOptions) {
return client; return client;
} }
export function roomAliasFromRoomName(roomName) { export function roomAliasLocalpartFromRoomName(roomName: string): string {
return roomName return roomName
.trim() .trim()
.replace(/\s/g, "-") .replace(/\s/g, "-")
@ -101,7 +116,14 @@ export function roomAliasFromRoomName(roomName) {
.toLowerCase(); .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 return roomId
.match(/([^:]+):.*$/)[1] .match(/([^:]+):.*$/)[1]
.substring(1) .substring(1)
@ -113,7 +135,7 @@ export function roomNameFromRoomId(roomId) {
.toLowerCase(); .toLowerCase();
} }
export function isLocalRoomId(roomId) { export function isLocalRoomId(roomId: string): boolean {
if (!roomId) { if (!roomId) {
return false; return false;
} }
@ -127,12 +149,16 @@ export function isLocalRoomId(roomId) {
return parts[1] === defaultHomeserverHost; return parts[1] === defaultHomeserverHost;
} }
export async function createRoom(client, name, isPtt = false) { export async function createRoom(
const { room_id, room_alias } = await client.createRoom({ client: MatrixClient,
visibility: "private", name: string,
preset: "public_chat", isPtt = false
): Promise<string> {
const createRoomResult = await client.createRoom({
visibility: Visibility.Private,
preset: Preset.PublicChat,
name, name,
room_alias_name: roomAliasFromRoomName(name), room_alias_name: roomAliasLocalpartFromRoomName(name),
power_level_content_override: { power_level_content_override: {
invite: 100, invite: 100,
kick: 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( await client.createGroupCall(
room_id, createRoomResult.room_id,
isPtt ? GroupCallType.Voice : GroupCallType.Video, isPtt ? GroupCallType.Voice : GroupCallType.Video,
isPtt, isPtt,
GroupCallIntent.Prompt GroupCallIntent.Prompt
); );
return room_alias || room_id; return fullAliasFromRoomName(name, client);
} }
export function getRoomUrl(roomId) { export function getRoomUrl(roomId: string): string {
if (roomId.startsWith("#")) { if (roomId.startsWith("#")) {
const [localPart, host] = roomId.replace("#", "").split(":"); 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 width = Math.floor(avatarSize * window.devicePixelRatio);
const height = Math.floor(avatarSize * window.devicePixelRatio); const height = Math.floor(avatarSize * window.devicePixelRatio);
return mxcUrl && client.mxcUrlToHttp(mxcUrl, width, height, "crop"); return mxcUrl && client.mxcUrlToHttp(mxcUrl, width, height, "crop");

View file

@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "es2016", "target": "es2016",
"esModuleInterop": true, "esModuleInterop": true,
"module": "commonjs", "module": "es2020",
"moduleResolution": "node", "moduleResolution": "node",
"noEmit": true, "noEmit": true,
"noImplicitAny": false, "noImplicitAny": false,