Merge branch 'main' into organize-colors
This commit is contained in:
commit
977016fbb2
11 changed files with 141 additions and 79 deletions
7
src/@types/global.d.ts
vendored
7
src/@types/global.d.ts
vendored
|
@ -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<string, string>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,41 +15,55 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { useCallback } from "react";
|
||||
import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index";
|
||||
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
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 = 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,
|
||||
}),
|
||||
stateUpdated: null,
|
||||
requestEmailToken: null,
|
||||
});
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
const { user_id, access_token, device_id } =
|
||||
await interactiveAuth.attemptAuth();
|
||||
const session = { user_id, access_token, device_id };
|
||||
// XXX: This claims to return an IAuthData which contains none of these
|
||||
// things - the js-sdk types may be wrong?
|
||||
/* eslint-disable camelcase,@typescript-eslint/no-explicit-any */
|
||||
const { user_id, access_token, device_id } =
|
||||
(await interactiveAuth.attemptAuth()) as any;
|
||||
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];
|
||||
}, []);
|
||||
|
|
|
@ -15,8 +15,8 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { initClient, defaultHomeserver } from "../matrix-utils";
|
||||
import { Session } from "../ClientContext";
|
||||
|
@ -37,7 +37,7 @@ export const useInteractiveRegistration = (): [
|
|||
|
||||
const authClient = useRef<MatrixClient>();
|
||||
if (!authClient.current) {
|
||||
authClient.current = matrix.createClient(defaultHomeserver);
|
||||
authClient.current = createClient(defaultHomeserver);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -81,11 +81,14 @@ export const useInteractiveRegistration = (): [
|
|||
});
|
||||
}
|
||||
},
|
||||
requestEmailToken: null,
|
||||
});
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
// XXX: This claims to return an IAuthData which contains none of these
|
||||
// things - the js-sdk types may be wrong?
|
||||
/* eslint-disable camelcase,@typescript-eslint/no-explicit-any */
|
||||
const { user_id, access_token, device_id } =
|
||||
await interactiveAuth.attemptAuth();
|
||||
(await interactiveAuth.attemptAuth()) as any;
|
||||
|
||||
const client = await initClient({
|
||||
baseUrl: defaultHomeserver,
|
||||
|
|
|
@ -37,9 +37,8 @@ export const variantToClassName = {
|
|||
icon: [styles.iconButton],
|
||||
secondary: [styles.secondary],
|
||||
copy: [styles.copyButton],
|
||||
secondaryCopy: [styles.secondaryCopy],
|
||||
secondaryCopy: [styles.secondaryCopy, styles.copyButton],
|
||||
iconCopy: [styles.iconCopyButton],
|
||||
secondaryCopy: [styles.copyButton],
|
||||
secondaryHangup: [styles.secondaryHangup],
|
||||
dropdown: [styles.dropdownButton],
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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}`);
|
||||
});
|
||||
|
|
|
@ -14,6 +14,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
// We need to import this somewhere, once, so that the correct 'request'
|
||||
// function gets set. It needs to be not in the same file as we use
|
||||
// createClient, or the typescript transpiler gets confused about
|
||||
// dependency references.
|
||||
import "matrix-js-sdk/src/browser-index";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { createBrowserHistory } from "history";
|
||||
|
|
|
@ -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<void>((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<MatrixClient> {
|
||||
// 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,20 +66,23 @@ 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,
|
||||
// Use a relatively low timeout for API calls: this is a realtime application
|
||||
// so we don't want API calls taking ages, we'd rather they just fail.
|
||||
localTimeoutMs: 5000,
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -74,7 +92,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 +111,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 +119,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 +138,7 @@ export function roomNameFromRoomId(roomId) {
|
|||
.toLowerCase();
|
||||
}
|
||||
|
||||
export function isLocalRoomId(roomId) {
|
||||
export function isLocalRoomId(roomId: string): boolean {
|
||||
if (!roomId) {
|
||||
return false;
|
||||
}
|
||||
|
@ -127,12 +152,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<string> {
|
||||
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 +187,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 +213,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");
|
|
@ -50,13 +50,13 @@ export function VideoPreview({
|
|||
<div className={styles.preview} ref={previewRef}>
|
||||
<video ref={videoRef} muted playsInline disablePictureInPicture />
|
||||
{state === GroupCallState.LocalCallFeedUninitialized && (
|
||||
<Body fontWeight="semiBold" className={styles.webcamPermissions}>
|
||||
Webcam/microphone permissions needed to join the call.
|
||||
<Body fontWeight="semiBold" className={styles.cameraPermissions}>
|
||||
Camera/microphone permissions needed to join the call.
|
||||
</Body>
|
||||
)}
|
||||
{state === GroupCallState.InitializingLocalCallFeed && (
|
||||
<Body fontWeight="semiBold" className={styles.webcamPermissions}>
|
||||
Accept webcam/microphone permissions to join the call.
|
||||
<Body fontWeight="semiBold" className={styles.cameraPermissions}>
|
||||
Accept camera/microphone permissions to join the call.
|
||||
</Body>
|
||||
)}
|
||||
{state === GroupCallState.LocalCallFeedInitialized && (
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
background-color: var(--bgColor3);
|
||||
}
|
||||
|
||||
.webcamPermissions {
|
||||
.cameraPermissions {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
|
|
@ -103,7 +103,7 @@ export const SettingsModal = (props) => {
|
|||
}
|
||||
>
|
||||
<SelectInput
|
||||
label="Webcam"
|
||||
label="Camera"
|
||||
selectedKey={videoInput}
|
||||
onSelectionChange={setVideoInput}
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue