commit
1dfffce606
17 changed files with 337 additions and 140 deletions
|
@ -38,7 +38,8 @@
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"color-hash": "^2.0.1",
|
"color-hash": "^2.0.1",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#8ba2d257ae24bbed61cd7fe99af081324337161c",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#3334c01191bcd82b5243916284c9a08d08fd9795",
|
||||||
|
"matrix-widget-api": "^1.0.0",
|
||||||
"mermaid": "^8.13.8",
|
"mermaid": "^8.13.8",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"pako": "^2.0.4",
|
"pako": "^2.0.4",
|
||||||
|
|
|
@ -26,9 +26,14 @@ import React, {
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client";
|
import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { ErrorView } from "./FullScreenView";
|
import { ErrorView } from "./FullScreenView";
|
||||||
import { initClient, defaultHomeserver } from "./matrix-utils";
|
import {
|
||||||
|
initClient,
|
||||||
|
initMatroskaClient,
|
||||||
|
defaultHomeserver,
|
||||||
|
} from "./matrix-utils";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -91,40 +96,55 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const restore = async (): Promise<
|
const init = async (): Promise<
|
||||||
Pick<ClientProviderState, "client" | "isPasswordlessUser">
|
Pick<ClientProviderState, "client" | "isPasswordlessUser">
|
||||||
> => {
|
> => {
|
||||||
try {
|
const query = new URLSearchParams(window.location.search);
|
||||||
const session = loadSession();
|
const widgetId = query.get("widgetId");
|
||||||
|
const parentUrl = query.get("parentUrl");
|
||||||
|
|
||||||
if (session) {
|
if (widgetId && parentUrl) {
|
||||||
/* eslint-disable camelcase */
|
// We're inside a widget, so let's engage *Matroska mode*
|
||||||
const { user_id, device_id, access_token, passwordlessUser } =
|
logger.log("Using a Matroska client");
|
||||||
session;
|
|
||||||
|
|
||||||
const client = await initClient(
|
return {
|
||||||
{
|
client: await initMatroskaClient(widgetId, parentUrl),
|
||||||
baseUrl: defaultHomeserver,
|
isPasswordlessUser: false,
|
||||||
accessToken: access_token,
|
};
|
||||||
userId: user_id,
|
} else {
|
||||||
deviceId: device_id,
|
// We're running as a standalone application
|
||||||
},
|
try {
|
||||||
true
|
const session = loadSession();
|
||||||
);
|
|
||||||
/* eslint-enable camelcase */
|
|
||||||
|
|
||||||
return { client, isPasswordlessUser: passwordlessUser };
|
if (session) {
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
const { user_id, device_id, access_token, passwordlessUser } =
|
||||||
|
session;
|
||||||
|
|
||||||
|
logger.log("Using a standalone client");
|
||||||
|
const client = await initClient(
|
||||||
|
{
|
||||||
|
baseUrl: defaultHomeserver,
|
||||||
|
accessToken: access_token,
|
||||||
|
userId: user_id,
|
||||||
|
deviceId: device_id,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
|
return { client, isPasswordlessUser: passwordlessUser };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { client: undefined, isPasswordlessUser: false };
|
||||||
|
} catch (err) {
|
||||||
|
clearSession();
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { client: undefined, isPasswordlessUser: false };
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
clearSession();
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
restore()
|
init()
|
||||||
.then(({ client, isPasswordlessUser }) => {
|
.then(({ client, isPasswordlessUser }) => {
|
||||||
setState({
|
setState({
|
||||||
client,
|
client,
|
||||||
|
@ -135,7 +155,8 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
error: undefined,
|
error: undefined,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
|
logger.error(err);
|
||||||
setState({
|
setState({
|
||||||
client: undefined,
|
client: undefined,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
@ -37,7 +37,7 @@ import { AriaDialogProps } from "@react-types/dialog";
|
||||||
import { ReactComponent as CloseIcon } from "./icons/Close.svg";
|
import { ReactComponent as CloseIcon } from "./icons/Close.svg";
|
||||||
import styles from "./Modal.module.css";
|
import styles from "./Modal.module.css";
|
||||||
|
|
||||||
interface ModalProps extends OverlayProps, AriaDialogProps {
|
export interface ModalProps extends OverlayProps, AriaDialogProps {
|
||||||
title: string;
|
title: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
|
@ -5,14 +5,21 @@ import { MemoryStore } from "matrix-js-sdk/src/store/memory";
|
||||||
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
|
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
|
||||||
import { LocalStorageCryptoStore } from "matrix-js-sdk/src/crypto/store/localStorage-crypto-store";
|
import { LocalStorageCryptoStore } from "matrix-js-sdk/src/crypto/store/localStorage-crypto-store";
|
||||||
import { MemoryCryptoStore } from "matrix-js-sdk/src/crypto/store/memory-crypto-store";
|
import { MemoryCryptoStore } from "matrix-js-sdk/src/crypto/store/memory-crypto-store";
|
||||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
import {
|
||||||
|
createClient,
|
||||||
|
createRoomWidgetClient,
|
||||||
|
MatrixClient,
|
||||||
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { ICreateClientOpts } from "matrix-js-sdk/src/matrix";
|
import { ICreateClientOpts } from "matrix-js-sdk/src/matrix";
|
||||||
import { ClientEvent } from "matrix-js-sdk/src/client";
|
import { ClientEvent } from "matrix-js-sdk/src/client";
|
||||||
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials";
|
import { Visibility, Preset } from "matrix-js-sdk/src/@types/partials";
|
||||||
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
|
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
|
||||||
|
import { WidgetApi } from "matrix-widget-api";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import IndexedDBWorker from "./IndexedDBWorker?worker";
|
import IndexedDBWorker from "./IndexedDBWorker?worker";
|
||||||
|
import { getRoomParams } from "./room/useRoomParams";
|
||||||
|
|
||||||
export const defaultHomeserver =
|
export const defaultHomeserver =
|
||||||
(import.meta.env.VITE_DEFAULT_HOMESERVER as string) ??
|
(import.meta.env.VITE_DEFAULT_HOMESERVER as string) ??
|
||||||
|
@ -53,7 +60,74 @@ function waitForSync(client: MatrixClient) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises and returns a new Matrix Client
|
* Initialises and returns a new widget-API-based Matrix Client.
|
||||||
|
* @param widgetId The ID of the widget that the app is running inside.
|
||||||
|
* @param parentUrl The URL of the parent client.
|
||||||
|
* @returns The MatrixClient instance
|
||||||
|
*/
|
||||||
|
export async function initMatroskaClient(
|
||||||
|
widgetId: string,
|
||||||
|
parentUrl: string
|
||||||
|
): Promise<MatrixClient> {
|
||||||
|
// In this mode, we use a special client which routes all requests through
|
||||||
|
// the host application via the widget API
|
||||||
|
|
||||||
|
const { roomId, userId, deviceId } = getRoomParams();
|
||||||
|
if (!roomId) throw new Error("Room ID must be supplied");
|
||||||
|
if (!userId) throw new Error("User ID must be supplied");
|
||||||
|
if (!deviceId) throw new Error("Device ID must be supplied");
|
||||||
|
|
||||||
|
// These are all the event types the app uses
|
||||||
|
const sendState = [
|
||||||
|
{ eventType: EventType.GroupCallPrefix },
|
||||||
|
{ eventType: EventType.GroupCallMemberPrefix, stateKey: userId },
|
||||||
|
];
|
||||||
|
const receiveState = [
|
||||||
|
{ eventType: EventType.RoomMember },
|
||||||
|
{ eventType: EventType.GroupCallPrefix },
|
||||||
|
{ eventType: EventType.GroupCallMemberPrefix },
|
||||||
|
];
|
||||||
|
const sendRecvToDevice = [
|
||||||
|
EventType.CallInvite,
|
||||||
|
EventType.CallCandidates,
|
||||||
|
EventType.CallAnswer,
|
||||||
|
EventType.CallHangup,
|
||||||
|
EventType.CallReject,
|
||||||
|
EventType.CallSelectAnswer,
|
||||||
|
EventType.CallNegotiate,
|
||||||
|
EventType.CallSDPStreamMetadataChanged,
|
||||||
|
EventType.CallSDPStreamMetadataChangedPrefix,
|
||||||
|
EventType.CallReplaces,
|
||||||
|
"org.matrix.call_duplicate_session",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Since all data should be coming from the host application, there's no
|
||||||
|
// need to persist anything, and therefore we can use the default stores
|
||||||
|
// We don't even need to set up crypto
|
||||||
|
const client = createRoomWidgetClient(
|
||||||
|
new WidgetApi(widgetId, new URL(parentUrl).origin),
|
||||||
|
{
|
||||||
|
sendState,
|
||||||
|
receiveState,
|
||||||
|
sendToDevice: sendRecvToDevice,
|
||||||
|
receiveToDevice: sendRecvToDevice,
|
||||||
|
turnServers: true,
|
||||||
|
},
|
||||||
|
roomId,
|
||||||
|
{
|
||||||
|
baseUrl: "",
|
||||||
|
userId,
|
||||||
|
deviceId,
|
||||||
|
timelineSupport: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await client.startClient();
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises and returns a new standalone Matrix Client.
|
||||||
* If true is passed for the 'restore' parameter, a check will be made
|
* If true is passed for the 'restore' parameter, a check will be made
|
||||||
* to ensure that corresponding crypto data is stored and recovered.
|
* to ensure that corresponding crypto data is stored and recovered.
|
||||||
* If the check fails, CryptoStoreIntegrityError will be thrown.
|
* If the check fails, CryptoStoreIntegrityError will be thrown.
|
||||||
|
@ -127,16 +201,13 @@ export async function initClient(
|
||||||
storeOpts.cryptoStore = new MemoryCryptoStore();
|
storeOpts.cryptoStore = new MemoryCryptoStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: we read from the URL search params in RoomPage too:
|
// XXX: we read from the room params in RoomPage too:
|
||||||
// it would be much better to read them in one place and pass
|
// it would be much better to read them in one place and pass
|
||||||
// the values around, but we initialise the matrix client in
|
// the values around, but we initialise the matrix client in
|
||||||
// many different places so we'd have to pass it into all of
|
// many different places so we'd have to pass it into all of
|
||||||
// them.
|
// them.
|
||||||
const params = new URLSearchParams(window.location.search);
|
const { e2eEnabled } = getRoomParams();
|
||||||
// disable e2e only if enableE2e=false is given
|
if (!e2eEnabled) {
|
||||||
const enableE2e = params.get("enableE2e") !== "false";
|
|
||||||
|
|
||||||
if (!enableE2e) {
|
|
||||||
logger.info("Disabling E2E: group call signalling will NOT be encrypted.");
|
logger.info("Disabling E2E: group call signalling will NOT be encrypted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,10 +215,10 @@ export async function initClient(
|
||||||
...storeOpts,
|
...storeOpts,
|
||||||
...clientOptions,
|
...clientOptions,
|
||||||
useAuthorizationHeader: true,
|
useAuthorizationHeader: true,
|
||||||
// Use a relatively low timeout for API calls: this is a realtime application
|
// Use a relatively low timeout for API calls: this is a realtime app
|
||||||
// so we don't want API calls taking ages, we'd rather they just fail.
|
// so we don't want API calls taking ages, we'd rather they just fail.
|
||||||
localTimeoutMs: 5000,
|
localTimeoutMs: 5000,
|
||||||
useE2eForGroupCall: enableE2e,
|
useE2eForGroupCall: e2eEnabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -254,17 +325,17 @@ export async function createRoom(
|
||||||
return [fullAliasFromRoomName(name, client), result.room_id];
|
return [fullAliasFromRoomName(name, client), result.room_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRoomUrl(roomId: string): string {
|
export function getRoomUrl(roomIdOrAlias: string): string {
|
||||||
if (roomId.startsWith("#")) {
|
if (roomIdOrAlias.startsWith("#")) {
|
||||||
const [localPart, host] = roomId.replace("#", "").split(":");
|
const [localPart, host] = roomIdOrAlias.replace("#", "").split(":");
|
||||||
|
|
||||||
if (host !== defaultHomeserverHost) {
|
if (host !== defaultHomeserverHost) {
|
||||||
return `${window.location.protocol}//${window.location.host}/room/${roomId}`;
|
return `${window.location.protocol}//${window.location.host}/room/${roomIdOrAlias}`;
|
||||||
} else {
|
} else {
|
||||||
return `${window.location.protocol}//${window.location.host}/${localPart}`;
|
return `${window.location.protocol}//${window.location.host}/${localPart}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return `${window.location.protocol}//${window.location.host}/room/${roomId}`;
|
return `${window.location.protocol}//${window.location.host}/room/#?roomId=${roomIdOrAlias}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { usePageTitle } from "../usePageTitle";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
client: MatrixClient;
|
client: MatrixClient;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
viaServers: string[];
|
viaServers: string[];
|
||||||
children: (groupCall: GroupCall) => ReactNode;
|
children: (groupCall: GroupCall) => ReactNode;
|
||||||
createPtt: boolean;
|
createPtt: boolean;
|
||||||
|
@ -31,14 +31,14 @@ interface Props {
|
||||||
|
|
||||||
export function GroupCallLoader({
|
export function GroupCallLoader({
|
||||||
client,
|
client,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
viaServers,
|
viaServers,
|
||||||
children,
|
children,
|
||||||
createPtt,
|
createPtt,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const { loading, error, groupCall } = useLoadGroupCall(
|
const { loading, error, groupCall } = useLoadGroupCall(
|
||||||
client,
|
client,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
viaServers,
|
viaServers,
|
||||||
createPtt
|
createPtt
|
||||||
);
|
);
|
||||||
|
|
|
@ -37,14 +37,14 @@ interface Props {
|
||||||
client: MatrixClient;
|
client: MatrixClient;
|
||||||
isPasswordlessUser: boolean;
|
isPasswordlessUser: boolean;
|
||||||
isEmbedded: boolean;
|
isEmbedded: boolean;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
groupCall: GroupCall;
|
groupCall: GroupCall;
|
||||||
}
|
}
|
||||||
export function GroupCallView({
|
export function GroupCallView({
|
||||||
client,
|
client,
|
||||||
isPasswordlessUser,
|
isPasswordlessUser,
|
||||||
isEmbedded,
|
isEmbedded,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
groupCall,
|
groupCall,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const {
|
const {
|
||||||
|
@ -101,7 +101,7 @@ export function GroupCallView({
|
||||||
return (
|
return (
|
||||||
<PTTCallView
|
<PTTCallView
|
||||||
client={client}
|
client={client}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
roomName={groupCall.room.name}
|
roomName={groupCall.room.name}
|
||||||
avatarUrl={avatarUrl}
|
avatarUrl={avatarUrl}
|
||||||
groupCall={groupCall}
|
groupCall={groupCall}
|
||||||
|
@ -129,7 +129,7 @@ export function GroupCallView({
|
||||||
isScreensharing={isScreensharing}
|
isScreensharing={isScreensharing}
|
||||||
localScreenshareFeed={localScreenshareFeed}
|
localScreenshareFeed={localScreenshareFeed}
|
||||||
screenshareFeeds={screenshareFeeds}
|
screenshareFeeds={screenshareFeeds}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
unencryptedEventsFromUsers={unencryptedEventsFromUsers}
|
unencryptedEventsFromUsers={unencryptedEventsFromUsers}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -164,7 +164,7 @@ export function GroupCallView({
|
||||||
localVideoMuted={localVideoMuted}
|
localVideoMuted={localVideoMuted}
|
||||||
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
||||||
toggleMicrophoneMuted={toggleMicrophoneMuted}
|
toggleMicrophoneMuted={toggleMicrophoneMuted}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
isEmbedded={isEmbedded}
|
isEmbedded={isEmbedded}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -71,7 +71,7 @@ interface Props {
|
||||||
isScreensharing: boolean;
|
isScreensharing: boolean;
|
||||||
screenshareFeeds: CallFeed[];
|
screenshareFeeds: CallFeed[];
|
||||||
localScreenshareFeed: CallFeed;
|
localScreenshareFeed: CallFeed;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
unencryptedEventsFromUsers: Set<string>;
|
unencryptedEventsFromUsers: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ export function InCallView({
|
||||||
isScreensharing,
|
isScreensharing,
|
||||||
screenshareFeeds,
|
screenshareFeeds,
|
||||||
localScreenshareFeed,
|
localScreenshareFeed,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
unencryptedEventsFromUsers,
|
unencryptedEventsFromUsers,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
usePreventScroll();
|
usePreventScroll();
|
||||||
|
@ -260,7 +260,7 @@ export function InCallView({
|
||||||
{!fullscreenParticipant && (
|
{!fullscreenParticipant && (
|
||||||
<OverflowMenu
|
<OverflowMenu
|
||||||
inCall
|
inCall
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
groupCall={groupCall}
|
groupCall={groupCall}
|
||||||
showInvite={true}
|
showInvite={true}
|
||||||
feedbackModalState={feedbackModalState}
|
feedbackModalState={feedbackModalState}
|
||||||
|
@ -277,7 +277,7 @@ export function InCallView({
|
||||||
{rageshakeRequestModalState.isOpen && (
|
{rageshakeRequestModalState.isOpen && (
|
||||||
<RageshakeRequestModal
|
<RageshakeRequestModal
|
||||||
{...rageshakeRequestModalProps}
|
{...rageshakeRequestModalProps}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,31 +14,30 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { FC } from "react";
|
||||||
|
|
||||||
import { Modal, ModalContent } from "../Modal";
|
import { Modal, ModalContent, ModalProps } from "../Modal";
|
||||||
import { CopyButton } from "../button";
|
import { CopyButton } from "../button";
|
||||||
import { getRoomUrl } from "../matrix-utils";
|
import { getRoomUrl } from "../matrix-utils";
|
||||||
import styles from "./InviteModal.module.css";
|
import styles from "./InviteModal.module.css";
|
||||||
|
|
||||||
export function InviteModal({
|
interface Props extends Omit<ModalProps, "title" | "children"> {
|
||||||
roomId,
|
roomIdOrAlias: string;
|
||||||
...rest
|
|
||||||
}: {
|
|
||||||
roomId: string;
|
|
||||||
[x: string]: unknown;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title="Invite People"
|
|
||||||
isDismissable
|
|
||||||
className={styles.inviteModal}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<ModalContent>
|
|
||||||
<p>Copy and share this meeting link</p>
|
|
||||||
<CopyButton className={styles.copyButton} value={getRoomUrl(roomId)} />
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const InviteModal: FC<Props> = ({ roomIdOrAlias, ...rest }) => (
|
||||||
|
<Modal
|
||||||
|
title="Invite People"
|
||||||
|
isDismissable
|
||||||
|
className={styles.inviteModal}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<ModalContent>
|
||||||
|
<p>Copy and share this meeting link</p>
|
||||||
|
<CopyButton
|
||||||
|
className={styles.copyButton}
|
||||||
|
value={getRoomUrl(roomIdOrAlias)}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
|
@ -45,7 +45,7 @@ interface Props {
|
||||||
toggleLocalVideoMuted: () => void;
|
toggleLocalVideoMuted: () => void;
|
||||||
toggleMicrophoneMuted: () => void;
|
toggleMicrophoneMuted: () => void;
|
||||||
localVideoMuted: boolean;
|
localVideoMuted: boolean;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
isEmbedded: boolean;
|
isEmbedded: boolean;
|
||||||
}
|
}
|
||||||
export function LobbyView({
|
export function LobbyView({
|
||||||
|
@ -61,7 +61,7 @@ export function LobbyView({
|
||||||
localVideoMuted,
|
localVideoMuted,
|
||||||
toggleLocalVideoMuted,
|
toggleLocalVideoMuted,
|
||||||
toggleMicrophoneMuted,
|
toggleMicrophoneMuted,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
isEmbedded,
|
isEmbedded,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { stream } = useCallFeed(localCallFeed);
|
const { stream } = useCallFeed(localCallFeed);
|
||||||
|
@ -115,7 +115,7 @@ export function LobbyView({
|
||||||
<VideoPreview
|
<VideoPreview
|
||||||
state={state}
|
state={state}
|
||||||
client={client}
|
client={client}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
microphoneMuted={microphoneMuted}
|
microphoneMuted={microphoneMuted}
|
||||||
localVideoMuted={localVideoMuted}
|
localVideoMuted={localVideoMuted}
|
||||||
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
||||||
|
@ -136,7 +136,7 @@ export function LobbyView({
|
||||||
<Body>Or</Body>
|
<Body>Or</Body>
|
||||||
<CopyButton
|
<CopyButton
|
||||||
variant="secondaryCopy"
|
variant="secondaryCopy"
|
||||||
value={getRoomUrl(roomId)}
|
value={getRoomUrl(roomIdOrAlias)}
|
||||||
className={styles.copyButton}
|
className={styles.copyButton}
|
||||||
copiedMessage="Call link copied"
|
copiedMessage="Call link copied"
|
||||||
>
|
>
|
||||||
|
|
|
@ -32,7 +32,7 @@ import { InviteModal } from "./InviteModal";
|
||||||
import { TooltipTrigger } from "../Tooltip";
|
import { TooltipTrigger } from "../Tooltip";
|
||||||
import { FeedbackModal } from "./FeedbackModal";
|
import { FeedbackModal } from "./FeedbackModal";
|
||||||
interface Props {
|
interface Props {
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
inCall: boolean;
|
inCall: boolean;
|
||||||
groupCall: GroupCall;
|
groupCall: GroupCall;
|
||||||
showInvite: boolean;
|
showInvite: boolean;
|
||||||
|
@ -43,7 +43,7 @@ interface Props {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function OverflowMenu({
|
export function OverflowMenu({
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
inCall,
|
inCall,
|
||||||
groupCall,
|
groupCall,
|
||||||
showInvite,
|
showInvite,
|
||||||
|
@ -119,7 +119,7 @@ export function OverflowMenu({
|
||||||
</PopoverMenuTrigger>
|
</PopoverMenuTrigger>
|
||||||
{settingsModalState.isOpen && <SettingsModal {...settingsModalProps} />}
|
{settingsModalState.isOpen && <SettingsModal {...settingsModalProps} />}
|
||||||
{inviteModalState.isOpen && (
|
{inviteModalState.isOpen && (
|
||||||
<InviteModal roomId={roomId} {...inviteModalProps} />
|
<InviteModal roomIdOrAlias={roomIdOrAlias} {...inviteModalProps} />
|
||||||
)}
|
)}
|
||||||
{feedbackModalState.isOpen && (
|
{feedbackModalState.isOpen && (
|
||||||
<FeedbackModal
|
<FeedbackModal
|
||||||
|
|
|
@ -87,7 +87,7 @@ function getPromptText(
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
client: MatrixClient;
|
client: MatrixClient;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
roomName: string;
|
roomName: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
groupCall: GroupCall;
|
groupCall: GroupCall;
|
||||||
|
@ -99,7 +99,7 @@ interface Props {
|
||||||
|
|
||||||
export const PTTCallView: React.FC<Props> = ({
|
export const PTTCallView: React.FC<Props> = ({
|
||||||
client,
|
client,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
roomName,
|
roomName,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
groupCall,
|
groupCall,
|
||||||
|
@ -205,7 +205,7 @@ export const PTTCallView: React.FC<Props> = ({
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
<OverflowMenu
|
<OverflowMenu
|
||||||
inCall
|
inCall
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
groupCall={groupCall}
|
groupCall={groupCall}
|
||||||
showInvite={false}
|
showInvite={false}
|
||||||
feedbackModalState={feedbackModalState}
|
feedbackModalState={feedbackModalState}
|
||||||
|
@ -282,7 +282,7 @@ export const PTTCallView: React.FC<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{inviteModalState.isOpen && showControls && (
|
{inviteModalState.isOpen && showControls && (
|
||||||
<InviteModal roomId={roomId} {...inviteModalProps} />
|
<InviteModal roomIdOrAlias={roomIdOrAlias} {...inviteModalProps} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,24 +14,25 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { FC, useEffect } from "react";
|
||||||
|
|
||||||
import { Modal, ModalContent } from "../Modal";
|
import { Modal, ModalContent, ModalProps } from "../Modal";
|
||||||
import { Button } from "../button";
|
import { Button } from "../button";
|
||||||
import { FieldRow, ErrorMessage } from "../input/Input";
|
import { FieldRow, ErrorMessage } from "../input/Input";
|
||||||
import { useSubmitRageshake } from "../settings/submit-rageshake";
|
import { useSubmitRageshake } from "../settings/submit-rageshake";
|
||||||
import { Body } from "../typography/Typography";
|
import { Body } from "../typography/Typography";
|
||||||
|
|
||||||
export function RageshakeRequestModal({
|
interface Props extends Omit<ModalProps, "title" | "children"> {
|
||||||
rageshakeRequestId,
|
|
||||||
roomId,
|
|
||||||
...rest
|
|
||||||
}: {
|
|
||||||
rageshakeRequestId: string;
|
rageshakeRequestId: string;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
[x: string]: unknown;
|
}
|
||||||
}) {
|
|
||||||
|
export const RageshakeRequestModal: FC<Props> = ({
|
||||||
|
rageshakeRequestId,
|
||||||
|
roomIdOrAlias,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
|
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -53,7 +54,7 @@ export function RageshakeRequestModal({
|
||||||
submitRageshake({
|
submitRageshake({
|
||||||
sendLogs: true,
|
sendLogs: true,
|
||||||
rageshakeRequestId,
|
rageshakeRequestId,
|
||||||
roomId,
|
roomId: roomIdOrAlias, // Possibly not a room ID, but oh well
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
disabled={sending}
|
disabled={sending}
|
||||||
|
@ -69,4 +70,4 @@ export function RageshakeRequestModal({
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2021 New Vector Ltd
|
Copyright 2021-2022 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,33 +14,26 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { FC, useEffect, useState } from "react";
|
||||||
import { useLocation, useParams } from "react-router-dom";
|
|
||||||
|
|
||||||
import { useClient } from "../ClientContext";
|
import { useClient } from "../ClientContext";
|
||||||
import { ErrorView, LoadingView } from "../FullScreenView";
|
import { ErrorView, LoadingView } from "../FullScreenView";
|
||||||
import { RoomAuthView } from "./RoomAuthView";
|
import { RoomAuthView } from "./RoomAuthView";
|
||||||
import { GroupCallLoader } from "./GroupCallLoader";
|
import { GroupCallLoader } from "./GroupCallLoader";
|
||||||
import { GroupCallView } from "./GroupCallView";
|
import { GroupCallView } from "./GroupCallView";
|
||||||
|
import { useRoomParams } from "./useRoomParams";
|
||||||
import { MediaHandlerProvider } from "../settings/useMediaHandler";
|
import { MediaHandlerProvider } from "../settings/useMediaHandler";
|
||||||
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
||||||
|
|
||||||
export function RoomPage() {
|
export const RoomPage: FC = () => {
|
||||||
const { loading, isAuthenticated, error, client, isPasswordlessUser } =
|
const { loading, isAuthenticated, error, client, isPasswordlessUser } =
|
||||||
useClient();
|
useClient();
|
||||||
|
|
||||||
const { roomId: maybeRoomId } = useParams();
|
const { roomAlias, roomId, viaServers, isEmbedded, isPtt, displayName } =
|
||||||
const { hash, search }: { hash: string; search: string } = useLocation();
|
useRoomParams();
|
||||||
const [viaServers, isEmbedded, isPtt, displayName] = useMemo(() => {
|
const roomIdOrAlias = roomId ?? roomAlias;
|
||||||
const params = new URLSearchParams(search);
|
if (!roomIdOrAlias) throw new Error("No room specified");
|
||||||
return [
|
|
||||||
params.getAll("via"),
|
|
||||||
params.has("embed"),
|
|
||||||
params.get("ptt") === "true",
|
|
||||||
params.get("displayName"),
|
|
||||||
];
|
|
||||||
}, [search]);
|
|
||||||
const roomId = (maybeRoomId || hash || "").toLowerCase();
|
|
||||||
const { registerPasswordlessUser } = useRegisterPasswordlessUser();
|
const { registerPasswordlessUser } = useRegisterPasswordlessUser();
|
||||||
const [isRegistering, setIsRegistering] = useState(false);
|
const [isRegistering, setIsRegistering] = useState(false);
|
||||||
|
|
||||||
|
@ -76,14 +69,14 @@ export function RoomPage() {
|
||||||
<MediaHandlerProvider client={client}>
|
<MediaHandlerProvider client={client}>
|
||||||
<GroupCallLoader
|
<GroupCallLoader
|
||||||
client={client}
|
client={client}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
viaServers={viaServers}
|
viaServers={viaServers}
|
||||||
createPtt={isPtt}
|
createPtt={isPtt}
|
||||||
>
|
>
|
||||||
{(groupCall) => (
|
{(groupCall) => (
|
||||||
<GroupCallView
|
<GroupCallView
|
||||||
client={client}
|
client={client}
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
groupCall={groupCall}
|
groupCall={groupCall}
|
||||||
isPasswordlessUser={isPasswordlessUser}
|
isPasswordlessUser={isPasswordlessUser}
|
||||||
isEmbedded={isEmbedded}
|
isEmbedded={isEmbedded}
|
||||||
|
@ -92,4 +85,4 @@ export function RoomPage() {
|
||||||
</GroupCallLoader>
|
</GroupCallLoader>
|
||||||
</MediaHandlerProvider>
|
</MediaHandlerProvider>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -32,7 +32,7 @@ import { useModalTriggerState } from "../Modal";
|
||||||
interface Props {
|
interface Props {
|
||||||
client: MatrixClient;
|
client: MatrixClient;
|
||||||
state: GroupCallState;
|
state: GroupCallState;
|
||||||
roomId: string;
|
roomIdOrAlias: string;
|
||||||
microphoneMuted: boolean;
|
microphoneMuted: boolean;
|
||||||
localVideoMuted: boolean;
|
localVideoMuted: boolean;
|
||||||
toggleLocalVideoMuted: () => void;
|
toggleLocalVideoMuted: () => void;
|
||||||
|
@ -43,7 +43,7 @@ interface Props {
|
||||||
export function VideoPreview({
|
export function VideoPreview({
|
||||||
client,
|
client,
|
||||||
state,
|
state,
|
||||||
roomId,
|
roomIdOrAlias,
|
||||||
microphoneMuted,
|
microphoneMuted,
|
||||||
localVideoMuted,
|
localVideoMuted,
|
||||||
toggleLocalVideoMuted,
|
toggleLocalVideoMuted,
|
||||||
|
@ -93,7 +93,7 @@ export function VideoPreview({
|
||||||
onPress={toggleLocalVideoMuted}
|
onPress={toggleLocalVideoMuted}
|
||||||
/>
|
/>
|
||||||
<OverflowMenu
|
<OverflowMenu
|
||||||
roomId={roomId}
|
roomIdOrAlias={roomIdOrAlias}
|
||||||
feedbackModalState={feedbackModalState}
|
feedbackModalState={feedbackModalState}
|
||||||
feedbackModalProps={feedbackModalProps}
|
feedbackModalProps={feedbackModalProps}
|
||||||
inCall={false}
|
inCall={false}
|
||||||
|
|
93
src/room/useRoomParams.ts
Normal file
93
src/room/useRoomParams.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
|
export interface RoomParams {
|
||||||
|
roomAlias: string | null;
|
||||||
|
roomId: string | null;
|
||||||
|
viaServers: string[];
|
||||||
|
// Whether the app is running in embedded mode, and should keep the user
|
||||||
|
// confined to the current room
|
||||||
|
isEmbedded: boolean;
|
||||||
|
// Whether to start a walkie-talkie call instead of a video call
|
||||||
|
isPtt: boolean;
|
||||||
|
// Whether to use end-to-end encryption
|
||||||
|
e2eEnabled: boolean;
|
||||||
|
// The user's ID (only used in Matroska mode)
|
||||||
|
userId: string | null;
|
||||||
|
// The display name to use for auto-registration
|
||||||
|
displayName: string | null;
|
||||||
|
// The device's ID (only used in Matroska mode)
|
||||||
|
deviceId: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the room parameters for the current URL.
|
||||||
|
* @param {string} query The URL query string
|
||||||
|
* @param {string} fragment The URL fragment string
|
||||||
|
* @returns {RoomParams} The room parameters encoded in the URL
|
||||||
|
*/
|
||||||
|
export const getRoomParams = (
|
||||||
|
query: string = window.location.search,
|
||||||
|
fragment: string = window.location.hash
|
||||||
|
): RoomParams => {
|
||||||
|
const fragmentQueryStart = fragment.indexOf("?");
|
||||||
|
const fragmentParams = new URLSearchParams(
|
||||||
|
fragmentQueryStart === -1 ? "" : fragment.substring(fragmentQueryStart)
|
||||||
|
);
|
||||||
|
const queryParams = new URLSearchParams(query);
|
||||||
|
|
||||||
|
// Normally, room params should be encoded in the fragment so as to avoid
|
||||||
|
// leaking them to the server. However, we also check the normal query
|
||||||
|
// string for backwards compatibility with versions that only used that.
|
||||||
|
const hasParam = (name: string): boolean =>
|
||||||
|
fragmentParams.has(name) || queryParams.has(name);
|
||||||
|
const getParam = (name: string): string | null =>
|
||||||
|
fragmentParams.get(name) ?? queryParams.get(name);
|
||||||
|
const getAllParams = (name: string): string[] => [
|
||||||
|
...fragmentParams.getAll(name),
|
||||||
|
...queryParams.getAll(name),
|
||||||
|
];
|
||||||
|
|
||||||
|
// The part of the fragment before the ?
|
||||||
|
const fragmentRoute =
|
||||||
|
fragmentQueryStart === -1
|
||||||
|
? fragment
|
||||||
|
: fragment.substring(0, fragmentQueryStart);
|
||||||
|
|
||||||
|
return {
|
||||||
|
roomAlias: fragmentRoute.length > 1 ? fragmentRoute : null,
|
||||||
|
roomId: getParam("roomId"),
|
||||||
|
viaServers: getAllParams("via"),
|
||||||
|
isEmbedded: hasParam("embed"),
|
||||||
|
isPtt: hasParam("ptt"),
|
||||||
|
e2eEnabled: getParam("enableE2e") !== "false", // Defaults to true
|
||||||
|
userId: getParam("userId"),
|
||||||
|
displayName: getParam("displayName"),
|
||||||
|
deviceId: getParam("deviceId"),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to simplify use of getRoomParams.
|
||||||
|
* @returns {RoomParams} The room parameters for the current URL
|
||||||
|
*/
|
||||||
|
export const useRoomParams = (): RoomParams => {
|
||||||
|
const { hash, search } = useLocation();
|
||||||
|
return useMemo(() => getRoomParams(search, hash), [search, hash]);
|
||||||
|
};
|
|
@ -41,6 +41,12 @@ export default defineConfig(({ mode }) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
// matrix-widget-api has its transpiled lib/index.js as its entry point,
|
||||||
|
// which Vite for some reason refuses to work with, so we point it to
|
||||||
|
// src/index.ts instead
|
||||||
|
"matrix-widget-api": "matrix-widget-api/src/index.ts",
|
||||||
|
},
|
||||||
dedupe: [
|
dedupe: [
|
||||||
"react",
|
"react",
|
||||||
"react-dom",
|
"react-dom",
|
||||||
|
|
46
yarn.lock
46
yarn.lock
|
@ -2754,6 +2754,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
|
||||||
integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
|
integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
|
||||||
|
|
||||||
|
"@types/events@^3.0.0":
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||||
|
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||||
|
|
||||||
"@types/glob@*", "@types/glob@^7.1.1", "@types/glob@^7.1.3":
|
"@types/glob@*", "@types/glob@^7.1.1", "@types/glob@^7.1.3":
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||||
|
@ -3863,12 +3868,10 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
base-x@^3.0.2:
|
base-x@^4.0.0:
|
||||||
version "3.0.9"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
|
resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a"
|
||||||
integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
|
integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==
|
||||||
dependencies:
|
|
||||||
safe-buffer "^5.0.1"
|
|
||||||
|
|
||||||
base16@^1.0.0:
|
base16@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
|
@ -4105,12 +4108,12 @@ browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^
|
||||||
node-releases "^2.0.5"
|
node-releases "^2.0.5"
|
||||||
update-browserslist-db "^1.0.4"
|
update-browserslist-db "^1.0.4"
|
||||||
|
|
||||||
bs58@^4.0.1:
|
bs58@^5.0.0:
|
||||||
version "4.0.1"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279"
|
||||||
integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==
|
integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
base-x "^3.0.2"
|
base-x "^4.0.0"
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer-from@^1.0.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
|
@ -8387,24 +8390,33 @@ matrix-events-sdk@^0.0.1-beta.7:
|
||||||
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934"
|
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934"
|
||||||
integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA==
|
integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA==
|
||||||
|
|
||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#8ba2d257ae24bbed61cd7fe99af081324337161c":
|
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#3334c01191bcd82b5243916284c9a08d08fd9795":
|
||||||
version "19.0.0"
|
version "19.2.0"
|
||||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/8ba2d257ae24bbed61cd7fe99af081324337161c"
|
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/3334c01191bcd82b5243916284c9a08d08fd9795"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
"@types/sdp-transform" "^2.4.5"
|
"@types/sdp-transform" "^2.4.5"
|
||||||
another-json "^0.2.0"
|
another-json "^0.2.0"
|
||||||
browser-request "^0.3.3"
|
browser-request "^0.3.3"
|
||||||
bs58 "^4.0.1"
|
bs58 "^5.0.0"
|
||||||
content-type "^1.0.4"
|
content-type "^1.0.4"
|
||||||
loglevel "^1.7.1"
|
loglevel "^1.7.1"
|
||||||
matrix-events-sdk "^0.0.1-beta.7"
|
matrix-events-sdk "^0.0.1-beta.7"
|
||||||
p-retry "^4.5.0"
|
matrix-widget-api "^1.0.0"
|
||||||
|
p-retry "4"
|
||||||
qs "^6.9.6"
|
qs "^6.9.6"
|
||||||
request "^2.88.2"
|
request "^2.88.2"
|
||||||
sdp-transform "^2.14.1"
|
sdp-transform "^2.14.1"
|
||||||
unhomoglyph "^1.0.6"
|
unhomoglyph "^1.0.6"
|
||||||
|
|
||||||
|
matrix-widget-api@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.0.0.tgz#0cde6839cca66ad817ab12aca3490ccc8bac97d1"
|
||||||
|
integrity sha512-cy8p/8EteRPTFIAw7Q9EgPUJc2jD19ZahMR8bMKf2NkILDcjuPMC0UWnsJyB3fSnlGw+VbGepttRpULM31zX8Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/events" "^3.0.0"
|
||||||
|
events "^3.2.0"
|
||||||
|
|
||||||
md5.js@^1.3.4:
|
md5.js@^1.3.4:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||||
|
@ -9189,7 +9201,7 @@ p-map@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
aggregate-error "^3.0.0"
|
aggregate-error "^3.0.0"
|
||||||
|
|
||||||
p-retry@^4.5.0:
|
p-retry@4:
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
|
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
|
||||||
integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
|
integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
|
||||||
|
|
Loading…
Reference in a new issue