Merge branch 'livekit' into toger5/track-processor-blur
This commit is contained in:
6
src/@types/global.d.ts
vendored
6
src/@types/global.d.ts
vendored
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import "matrix-js-sdk/src/@types/global";
|
||||
import { type setLogLevel as setLKLogLevel } from "livekit-client";
|
||||
|
||||
import type { DurationFormat as PolyfillDurationFormat } from "@formatjs/intl-durationformat";
|
||||
import { type Controls } from "../controls";
|
||||
|
||||
@@ -18,6 +19,7 @@ declare global {
|
||||
|
||||
interface Window {
|
||||
controls: Controls;
|
||||
setLKLogLevel: typeof setLKLogLevel;
|
||||
}
|
||||
|
||||
interface HTMLElement {
|
||||
|
||||
2
src/@types/i18next.d.ts
vendored
2
src/@types/i18next.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
12
src/@types/matrix-js-sdk.d.ts
vendored
12
src/@types/matrix-js-sdk.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,16 @@ import {
|
||||
} from "../reactions";
|
||||
|
||||
// Extend Matrix JS SDK types via Typescript declaration merging to support unspecced event fields and types
|
||||
declare module "matrix-js-sdk/src/types" {
|
||||
declare module "matrix-js-sdk/lib/types" {
|
||||
export interface TimelineEvents {
|
||||
[ElementCallReactionEventType]: ECallReactionEventContent;
|
||||
}
|
||||
|
||||
export interface AccountDataEvents {
|
||||
// Analytics account data event
|
||||
"im.vector.analytics": {
|
||||
id: string;
|
||||
pseudonymousAnalyticsOptIn?: boolean;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
2
src/@types/modules.d.ts
vendored
2
src/@types/modules.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
63
src/App.tsx
63
src/App.tsx
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Copyright 2021-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC, Suspense, useEffect, useState } from "react";
|
||||
import { type FC, type JSX, Suspense, useEffect, useState } from "react";
|
||||
import { BrowserRouter, Route, useLocation, Routes } from "react-router-dom";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { HomePage } from "./home/HomePage";
|
||||
import { LoginPage } from "./auth/LoginPage";
|
||||
import { RegisterPage } from "./auth/RegisterPage";
|
||||
import { RoomPage } from "./room/RoomPage";
|
||||
import { ClientProvider } from "./ClientContext";
|
||||
import { CrashView, LoadingView } from "./FullScreenView";
|
||||
import { ErrorPage, LoadingPage } from "./FullScreenView";
|
||||
import { DisconnectedBanner } from "./DisconnectedBanner";
|
||||
import { Initializer } from "./initializer";
|
||||
import { MediaDevicesProvider } from "./livekit/MediaDevicesContext";
|
||||
@@ -24,7 +24,7 @@ import { widget } from "./widget";
|
||||
import { useTheme } from "./useTheme";
|
||||
import { ProcessorProvider } from "./livekit/TrackProcessorContext";
|
||||
|
||||
const SentryRoute = Sentry.withSentryReactRouterV6Routing(Route);
|
||||
const SentryRoute = Sentry.withSentryReactRouterV7Routing(Route);
|
||||
|
||||
interface SimpleProviderProps {
|
||||
children: JSX.Element;
|
||||
@@ -62,8 +62,6 @@ export const App: FC = () => {
|
||||
.catch(logger.error);
|
||||
});
|
||||
|
||||
const errorPage = <CrashView />;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -73,23 +71,31 @@ export const App: FC = () => {
|
||||
<TooltipProvider>
|
||||
{loaded ? (
|
||||
<Suspense fallback={null}>
|
||||
<Providers>
|
||||
<Sentry.ErrorBoundary fallback={errorPage}>
|
||||
<DisconnectedBanner />
|
||||
<Routes>
|
||||
<SentryRoute path="/" element={<HomePage />} />
|
||||
<SentryRoute path="/login" element={<LoginPage />} />
|
||||
<SentryRoute
|
||||
path="/register"
|
||||
element={<RegisterPage />}
|
||||
/>
|
||||
<SentryRoute path="*" element={<RoomPage />} />
|
||||
</Routes>
|
||||
</Sentry.ErrorBoundary>
|
||||
</Providers>
|
||||
<ClientProvider>
|
||||
<MediaDevicesProvider>
|
||||
<ProcessorProvider>
|
||||
<Sentry.ErrorBoundary
|
||||
fallback={(error) => (
|
||||
<ErrorPage error={error} widget={widget} />
|
||||
)}
|
||||
>
|
||||
<DisconnectedBanner />
|
||||
<Routes>
|
||||
<SentryRoute path="/" element={<HomePage />} />
|
||||
<SentryRoute path="/login" element={<LoginPage />} />
|
||||
<SentryRoute
|
||||
path="/register"
|
||||
element={<RegisterPage />}
|
||||
/>
|
||||
<SentryRoute path="*" element={<RoomPage />} />
|
||||
</Routes>
|
||||
</Sentry.ErrorBoundary>
|
||||
</ProcessorProvider>
|
||||
</MediaDevicesProvider>
|
||||
</ClientProvider>
|
||||
</Suspense>
|
||||
) : (
|
||||
<LoadingView />
|
||||
<LoadingPage />
|
||||
)}
|
||||
</TooltipProvider>
|
||||
</ThemeProvider>
|
||||
@@ -97,16 +103,3 @@ export const App: FC = () => {
|
||||
</BrowserRouter>
|
||||
);
|
||||
};
|
||||
|
||||
const Providers: FC<{
|
||||
children: JSX.Element;
|
||||
}> = ({ children }) => {
|
||||
// We use this to stack all used providers to not make the App component to verbose
|
||||
return (
|
||||
<ClientProvider>
|
||||
<MediaDevicesProvider>
|
||||
<ProcessorProvider>{children}</ProcessorProvider>
|
||||
</MediaDevicesProvider>
|
||||
</ClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { afterEach, expect, test, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
import { type FC, type PropsWithChildren } from "react";
|
||||
|
||||
import { ClientContextProvider } from "./ClientContext";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
useEffect,
|
||||
} from "react";
|
||||
import { Avatar as CompoundAvatar } from "@vector-im/compound-web";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
|
||||
import { useClientState } from "./ClientContext";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2021-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -14,22 +14,22 @@ import {
|
||||
useContext,
|
||||
useRef,
|
||||
useMemo,
|
||||
type JSX,
|
||||
} from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type ISyncStateData, type SyncState } from "matrix-js-sdk/src/sync";
|
||||
import { ClientEvent, type MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { type ISyncStateData, type SyncState } from "matrix-js-sdk/lib/sync";
|
||||
import { ClientEvent, type MatrixClient } from "matrix-js-sdk";
|
||||
|
||||
import type { WidgetApi } from "matrix-widget-api";
|
||||
import { ErrorView } from "./FullScreenView";
|
||||
import { ErrorPage } from "./FullScreenView";
|
||||
import { widget } from "./widget";
|
||||
import {
|
||||
PosthogAnalytics,
|
||||
RegistrationType,
|
||||
} from "./analytics/PosthogAnalytics";
|
||||
import { translatedError } from "./TranslatedError";
|
||||
import { useEventTarget } from "./useEvents";
|
||||
import { OpenElsewhereError } from "./RichError";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -50,7 +50,7 @@ export type ValidClientState = {
|
||||
reactions: boolean;
|
||||
thumbnails: boolean;
|
||||
};
|
||||
setClient: (params?: SetClientParams) => void;
|
||||
setClient: (client: MatrixClient, session: Session) => void;
|
||||
};
|
||||
|
||||
export type AuthenticatedClient = {
|
||||
@@ -65,11 +65,6 @@ export type ErrorState = {
|
||||
error: Error;
|
||||
};
|
||||
|
||||
export type SetClientParams = {
|
||||
client: MatrixClient;
|
||||
session: Session;
|
||||
};
|
||||
|
||||
const ClientContext = createContext<ClientState | undefined>(undefined);
|
||||
|
||||
export const ClientContextProvider = ClientContext.Provider;
|
||||
@@ -79,7 +74,7 @@ export const useClientState = (): ClientState | undefined =>
|
||||
|
||||
export function useClient(): {
|
||||
client?: MatrixClient;
|
||||
setClient?: (params?: SetClientParams) => void;
|
||||
setClient?: (client: MatrixClient, session: Session) => void;
|
||||
} {
|
||||
let client;
|
||||
let setClient;
|
||||
@@ -96,7 +91,7 @@ export function useClient(): {
|
||||
// Plain representation of the `ClientContext` as a helper for old components that expected an object with multiple fields.
|
||||
export function useClientLegacy(): {
|
||||
client?: MatrixClient;
|
||||
setClient?: (params?: SetClientParams) => void;
|
||||
setClient?: (client: MatrixClient, session: Session) => void;
|
||||
passwordlessUser: boolean;
|
||||
loading: boolean;
|
||||
authenticated: boolean;
|
||||
@@ -160,7 +155,11 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
initializing.current = true;
|
||||
|
||||
loadClient()
|
||||
.then(setInitClientState)
|
||||
.then((initResult) => {
|
||||
setInitClientState(initResult);
|
||||
if (PosthogAnalytics.instance.isEnabled())
|
||||
PosthogAnalytics.instance.startListeningToSettingsChanges();
|
||||
})
|
||||
.catch((err) => logger.error(err))
|
||||
.finally(() => (initializing.current = false));
|
||||
}, []);
|
||||
@@ -196,24 +195,20 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
);
|
||||
|
||||
const setClient = useCallback(
|
||||
(clientParams?: SetClientParams) => {
|
||||
(client: MatrixClient, session: Session) => {
|
||||
const oldClient = initClientState?.client;
|
||||
const newClient = clientParams?.client;
|
||||
if (oldClient && oldClient !== newClient) {
|
||||
if (oldClient && oldClient !== client) {
|
||||
oldClient.stopClient();
|
||||
}
|
||||
|
||||
if (clientParams) {
|
||||
saveSession(clientParams.session);
|
||||
setInitClientState({
|
||||
widgetApi: null,
|
||||
client: clientParams.client,
|
||||
passwordlessUser: clientParams.session.passwordlessUser,
|
||||
});
|
||||
} else {
|
||||
clearSession();
|
||||
setInitClientState(null);
|
||||
}
|
||||
saveSession(session);
|
||||
setInitClientState({
|
||||
widgetApi: null,
|
||||
client,
|
||||
passwordlessUser: session.passwordlessUser,
|
||||
});
|
||||
if (PosthogAnalytics.instance.isEnabled())
|
||||
PosthogAnalytics.instance.startListeningToSettingsChanges();
|
||||
},
|
||||
[initClientState?.client],
|
||||
);
|
||||
@@ -228,12 +223,11 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
await client.clearStores();
|
||||
clearSession();
|
||||
setInitClientState(null);
|
||||
navigate("/");
|
||||
await navigate("/");
|
||||
PosthogAnalytics.instance.logout();
|
||||
PosthogAnalytics.instance.setRegistrationType(RegistrationType.Guest);
|
||||
}, [navigate, initClientState?.client]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
// To protect against multiple sessions writing to the same storage
|
||||
// simultaneously, we send a broadcast message that shuts down all other
|
||||
// running instances of the app. This isn't necessary if the app is running in
|
||||
@@ -250,8 +244,8 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
"message",
|
||||
useCallback(() => {
|
||||
initClientState?.client.stopClient();
|
||||
setAlreadyOpenedErr(translatedError("application_opened_another_tab", t));
|
||||
}, [initClientState?.client, setAlreadyOpenedErr, t]),
|
||||
setAlreadyOpenedErr(new OpenElsewhereError());
|
||||
}, [initClientState?.client, setAlreadyOpenedErr]),
|
||||
);
|
||||
|
||||
const [isDisconnected, setIsDisconnected] = useState(false);
|
||||
@@ -353,7 +347,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
}, [initClientState, onSync]);
|
||||
|
||||
if (alreadyOpenedErr) {
|
||||
return <ErrorView error={alreadyOpenedErr} />;
|
||||
return <ErrorPage widget={widget} error={alreadyOpenedErr} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
21
src/ErrorView.module.css
Normal file
21
src/ErrorView.module.css
Normal file
@@ -0,0 +1,21 @@
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--cpd-space-2x);
|
||||
max-inline-size: 480px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-block-end: var(--cpd-space-4x);
|
||||
}
|
||||
|
||||
.error > h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error > p {
|
||||
font: var(--cpd-font-body-lg-regular);
|
||||
color: var(--cpd-color-text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
118
src/ErrorView.tsx
Normal file
118
src/ErrorView.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { BigIcon, Button, Heading } from "@vector-im/compound-web";
|
||||
import {
|
||||
useCallback,
|
||||
type ComponentType,
|
||||
type FC,
|
||||
type ReactNode,
|
||||
type SVGAttributes,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { RageshakeButton } from "./settings/RageshakeButton";
|
||||
import styles from "./ErrorView.module.css";
|
||||
import { useUrlParams } from "./UrlParams";
|
||||
import { LinkButton } from "./button";
|
||||
import { ElementWidgetActions, type WidgetHelpers } from "./widget.ts";
|
||||
|
||||
interface Props {
|
||||
Icon: ComponentType<SVGAttributes<SVGElement>>;
|
||||
title: string;
|
||||
/**
|
||||
* Show an option to submit a rageshake.
|
||||
* @default false
|
||||
*/
|
||||
rageshake?: boolean;
|
||||
/**
|
||||
* Whether the error is considered fatal, i.e. non-recoverable. Causes the app
|
||||
* to fully reload when clicking 'return to home'.
|
||||
* @default false
|
||||
*/
|
||||
fatal?: boolean;
|
||||
children: ReactNode;
|
||||
widget: WidgetHelpers | null;
|
||||
}
|
||||
|
||||
export const ErrorView: FC<Props> = ({
|
||||
Icon,
|
||||
title,
|
||||
rageshake,
|
||||
fatal,
|
||||
children,
|
||||
widget,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { confineToRoom } = useUrlParams();
|
||||
|
||||
const onReload = useCallback(() => {
|
||||
window.location.href = "/";
|
||||
}, []);
|
||||
|
||||
const CloseWidgetButton: FC<{ widget: WidgetHelpers }> = ({
|
||||
widget,
|
||||
}): ReactElement => {
|
||||
// in widget mode we don't want to show the return home button but a close button
|
||||
const closeWidget = (): void => {
|
||||
widget.api.transport
|
||||
.send(ElementWidgetActions.Close, {})
|
||||
.catch((e) => {
|
||||
// What to do here?
|
||||
logger.error("Failed to send close action", e);
|
||||
})
|
||||
.finally(() => {
|
||||
widget.api.transport.stop();
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Button kind="primary" onClick={closeWidget}>
|
||||
{t("action.close")}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
// Whether the error is considered fatal or pathname is `/` then reload the all app.
|
||||
// If not then navigate to home page.
|
||||
const ReturnToHomeButton = (): ReactElement => {
|
||||
if (fatal || location.pathname === "/") {
|
||||
return (
|
||||
<Button kind="tertiary" className={styles.homeLink} onClick={onReload}>
|
||||
{t("return_home_button")}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<LinkButton kind="tertiary" className={styles.homeLink} to="/">
|
||||
{t("return_home_button")}
|
||||
</LinkButton>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.error}>
|
||||
<BigIcon className={styles.icon}>
|
||||
<Icon />
|
||||
</BigIcon>
|
||||
<Heading as="h1" weight="semibold" size="md">
|
||||
{title}
|
||||
</Heading>
|
||||
{children}
|
||||
{rageshake && (
|
||||
<RageshakeButton description={`***Error View***: ${title}`} />
|
||||
)}
|
||||
{widget ? (
|
||||
<CloseWidgetButton widget={widget} />
|
||||
) : (
|
||||
!confineToRoom && <ReturnToHomeButton />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC, type ReactNode, useCallback, useEffect } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { type FC, type ReactElement, type ReactNode, useEffect } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { ErrorIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { Header, HeaderLogo, LeftNav, RightNav } from "./Header";
|
||||
import { LinkButton } from "./button";
|
||||
import styles from "./FullScreenView.module.css";
|
||||
import { TranslatedError } from "./TranslatedError";
|
||||
import { Config } from "./config/Config";
|
||||
import { RageshakeButton } from "./settings/RageshakeButton";
|
||||
import { useUrlParams } from "./UrlParams";
|
||||
import { RichError } from "./RichError";
|
||||
import { ErrorView } from "./ErrorView";
|
||||
import { type WidgetHelpers } from "./widget.ts";
|
||||
|
||||
interface FullScreenViewProps {
|
||||
className?: string;
|
||||
@@ -33,10 +31,14 @@ export const FullScreenView: FC<FullScreenViewProps> = ({
|
||||
const { hideHeader } = useUrlParams();
|
||||
return (
|
||||
<div className={classNames(styles.page, className)}>
|
||||
<Header>
|
||||
<LeftNav>{!hideHeader && <HeaderLogo />}</LeftNav>
|
||||
<RightNav />
|
||||
</Header>
|
||||
{!hideHeader && (
|
||||
<Header>
|
||||
<LeftNav>
|
||||
<HeaderLogo />
|
||||
</LeftNav>
|
||||
<RightNav />
|
||||
</Header>
|
||||
)}
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>{children}</div>
|
||||
</div>
|
||||
@@ -44,74 +46,40 @@ export const FullScreenView: FC<FullScreenViewProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface ErrorViewProps {
|
||||
error: Error;
|
||||
interface ErrorPageProps {
|
||||
error: Error | unknown;
|
||||
widget: WidgetHelpers | null;
|
||||
}
|
||||
|
||||
export const ErrorView: FC<ErrorViewProps> = ({ error }) => {
|
||||
const location = useLocation();
|
||||
const { confineToRoom } = useUrlParams();
|
||||
// Due to this component being used as the crash fallback for Sentry, which has
|
||||
// weird type requirements, we can't just give this a type of FC<ErrorPageProps>
|
||||
export const ErrorPage = ({ error, widget }: ErrorPageProps): ReactElement => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
logger.error(error);
|
||||
Sentry.captureException(error);
|
||||
}, [error]);
|
||||
|
||||
const onReload = useCallback(() => {
|
||||
window.location.href = "/";
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FullScreenView>
|
||||
<h1>{t("common.error")}</h1>
|
||||
<p>
|
||||
{error instanceof TranslatedError
|
||||
? error.translatedMessage
|
||||
: error.message}
|
||||
</p>
|
||||
<RageshakeButton description={`***Error View***: ${error.message}`} />
|
||||
{!confineToRoom &&
|
||||
(location.pathname === "/" ? (
|
||||
<Button className={styles.homeLink} onClick={onReload}>
|
||||
{t("return_home_button")}
|
||||
</Button>
|
||||
) : (
|
||||
<LinkButton className={styles.homeLink} to="/">
|
||||
{t("return_home_button")}
|
||||
</LinkButton>
|
||||
))}
|
||||
</FullScreenView>
|
||||
);
|
||||
};
|
||||
|
||||
export const CrashView: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onReload = useCallback(() => {
|
||||
window.location.href = "/";
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FullScreenView>
|
||||
<Trans i18nKey="full_screen_view_h1">
|
||||
<h1>Oops, something's gone wrong.</h1>
|
||||
</Trans>
|
||||
{Config.get().rageshake?.submit_url && (
|
||||
<Trans i18nKey="full_screen_view_description">
|
||||
<p>Submitting debug logs will help us track down the problem.</p>
|
||||
</Trans>
|
||||
{error instanceof RichError ? (
|
||||
error.richMessage
|
||||
) : (
|
||||
<ErrorView
|
||||
widget={widget}
|
||||
Icon={ErrorIcon}
|
||||
title={t("error.generic")}
|
||||
rageshake
|
||||
fatal
|
||||
>
|
||||
<p>{t("error.generic_description")}</p>
|
||||
</ErrorView>
|
||||
)}
|
||||
|
||||
<RageshakeButton description="***Soft Crash***" />
|
||||
<Button className={styles.wideButton} onClick={onReload}>
|
||||
{t("return_home_button")}
|
||||
</Button>
|
||||
</FullScreenView>
|
||||
);
|
||||
};
|
||||
|
||||
export const LoadingView: FC = () => {
|
||||
export const LoadingPage: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -161,7 +161,12 @@ export const RoomHeaderInfo: FC<RoomHeaderInfoProps> = ({
|
||||
height={20}
|
||||
aria-label={t("header_participants_label")}
|
||||
/>
|
||||
<Text as="span" size="sm" weight="medium">
|
||||
<Text
|
||||
as="span"
|
||||
size="sm"
|
||||
weight="medium"
|
||||
data-testid="roomHeader_participants_count"
|
||||
>
|
||||
{t("participant_count", { count: participantCount ?? 0 })}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { IndexedDBStoreWorker } from "matrix-js-sdk/src/indexeddb-worker";
|
||||
import { IndexedDBStoreWorker } from "matrix-js-sdk/lib/indexeddb-worker";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const remoteWorker = new IndexedDBStoreWorker((self as any).postMessage);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,7 @@ test("the modal can be closed by clicking the close button", async () => {
|
||||
}
|
||||
const user = userEvent.setup();
|
||||
const { queryByRole, getByRole } = render(<ModalFn />);
|
||||
await user.click(getByRole("button", { name: "action.close" }));
|
||||
await user.click(getByRole("button", { name: "Close" }));
|
||||
expect(queryByRole("dialog")).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
53
src/RichError.tsx
Normal file
53
src/RichError.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PopOutIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import type { FC, ReactNode } from "react";
|
||||
import { ErrorView } from "./ErrorView";
|
||||
import { widget } from "./widget.ts";
|
||||
|
||||
/**
|
||||
* An error consisting of a terse message to be logged to the console and a
|
||||
* richer message to be shown to the user, as a full-screen page.
|
||||
*/
|
||||
export class RichError extends Error {
|
||||
public constructor(
|
||||
message: string,
|
||||
/**
|
||||
* The pretty, more helpful message to be shown on the error screen.
|
||||
*/
|
||||
public readonly richMessage: ReactNode,
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
const OpenElsewhere: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ErrorView
|
||||
widget={widget}
|
||||
Icon={PopOutIcon}
|
||||
title={t("error.open_elsewhere")}
|
||||
>
|
||||
<p>
|
||||
{t("error.open_elsewhere_description", {
|
||||
brand: import.meta.env.VITE_PRODUCT_NAME || "Element Call",
|
||||
})}
|
||||
</p>
|
||||
</ErrorView>
|
||||
);
|
||||
};
|
||||
|
||||
export class OpenElsewhereError extends RichError {
|
||||
public constructor() {
|
||||
super("App opened in another tab", <OpenElsewhere />);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,9 @@ import type { DefaultNamespace, ParseKeys, TFunction, TOptions } from "i18next";
|
||||
|
||||
/**
|
||||
* An error with messages in both English and the user's preferred language.
|
||||
* Use this for errors that need to be displayed inline within another
|
||||
* component. For errors that could be given their own screen, prefer
|
||||
* {@link RichError}.
|
||||
*/
|
||||
// Abstract to force consumers to use the function below rather than calling the
|
||||
// constructor directly
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -110,8 +110,8 @@ describe("UrlParams", () => {
|
||||
});
|
||||
|
||||
describe("returnToLobby", () => {
|
||||
it("is true in SPA mode", () => {
|
||||
expect(getUrlParams("?returnToLobby=false").returnToLobby).toBe(true);
|
||||
it("is false in SPA mode", () => {
|
||||
expect(getUrlParams("?returnToLobby=true").returnToLobby).toBe(false);
|
||||
});
|
||||
|
||||
it("defaults to false in widget mode", () => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { Config } from "./config/Config";
|
||||
import { type EncryptionSystem } from "./e2ee/sharedKeyManagement";
|
||||
@@ -105,7 +105,15 @@ export interface UrlParams {
|
||||
/**
|
||||
* The Posthog analytics ID. It is only available if the user has given consent for sharing telemetry in element web.
|
||||
*/
|
||||
analyticsID: string | null;
|
||||
posthogUserId: string | null;
|
||||
/**
|
||||
* The Posthog API host. This is only used in the embedded package of Element Call.
|
||||
*/
|
||||
posthogApiHost: string | null;
|
||||
/**
|
||||
* The Posthog API key. This is only used in the embedded package of Element Call.
|
||||
*/
|
||||
posthogApiKey: string | null;
|
||||
/**
|
||||
* Whether the app is allowed to use fallback STUN servers for ICE in case the
|
||||
* user's homeserver doesn't provide any.
|
||||
@@ -155,6 +163,20 @@ export interface UrlParams {
|
||||
* If it was a Join Call button, it would be `join_existing`.
|
||||
*/
|
||||
intent: string | null;
|
||||
|
||||
/**
|
||||
* The rageshake submit URL. This is only used in the embedded package of Element Call.
|
||||
*/
|
||||
rageshakeSubmitUrl: string | null;
|
||||
|
||||
/**
|
||||
* The Sentry DSN. This is only used in the embedded package of Element Call.
|
||||
*/
|
||||
sentryDsn: string | null;
|
||||
/**
|
||||
* The Sentry environment. This is only used in the embedded package of Element Call.
|
||||
*/
|
||||
sentryEnvironment: string | null;
|
||||
}
|
||||
|
||||
// This is here as a stopgap, but what would be far nicer is a function that
|
||||
@@ -257,18 +279,26 @@ export const getUrlParams = (
|
||||
lang: parser.getParam("lang"),
|
||||
fonts: parser.getAllParams("font"),
|
||||
fontScale: Number.isNaN(fontScale) ? null : fontScale,
|
||||
analyticsID: parser.getParam("analyticsID"),
|
||||
allowIceFallback: parser.getFlagParam("allowIceFallback"),
|
||||
perParticipantE2EE: parser.getFlagParam("perParticipantE2EE"),
|
||||
skipLobby: parser.getFlagParam(
|
||||
"skipLobby",
|
||||
isWidget && intent === UserIntent.StartNewCall,
|
||||
),
|
||||
returnToLobby: isWidget ? parser.getFlagParam("returnToLobby") : true,
|
||||
// In SPA mode the user should always exit to the home screen when hanging
|
||||
// up, rather than being sent back to the lobby
|
||||
returnToLobby: isWidget ? parser.getFlagParam("returnToLobby") : false,
|
||||
theme: parser.getParam("theme"),
|
||||
viaServers: !isWidget ? parser.getParam("viaServers") : null,
|
||||
homeserver: !isWidget ? parser.getParam("homeserver") : null,
|
||||
intent,
|
||||
posthogApiHost: parser.getParam("posthogApiHost"),
|
||||
posthogApiKey: parser.getParam("posthogApiKey"),
|
||||
posthogUserId:
|
||||
parser.getParam("posthogUserId") ?? parser.getParam("analyticsID"),
|
||||
rageshakeSubmitUrl: parser.getParam("rageshakeSubmitUrl"),
|
||||
sentryDsn: parser.getParam("sentryDsn"),
|
||||
sentryEnvironment: parser.getParam("sentryEnvironment"),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC, useCallback, useState } from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { useClientLegacy } from "./ClientContext";
|
||||
import { useProfile } from "./profile/useProfile";
|
||||
@@ -45,7 +46,9 @@ export const UserMenuContainer: FC<Props> = ({ preventNavigation = false }) => {
|
||||
logout?.();
|
||||
break;
|
||||
case "login":
|
||||
navigate("/login", { state: { from: location } });
|
||||
navigate("/login", { state: { from: location } })?.catch((error) =>
|
||||
logger.error("Failed to navigate to login", error),
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ exports[`QrCode > renders 1`] = `
|
||||
class="qrCode bar"
|
||||
>
|
||||
<img
|
||||
alt="qr_code"
|
||||
alt="QR Code"
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAAAklEQVR4AewaftIAAALBSURBVO3BQW7kQAwEwSxC//9yro88NSBI4/UQjIg/WGMUa5RijVKsUYo1SrFGKdYoxRqlWKMUa5RijVKsUYo1SrFGKdYoxRrl4qEk/CaVkyR0Kl0STlS6JPwmlSeKNUqxRinWKBcvU3lTEk6S8ITKHSpvSsKbijVKsUYp1igXH5aEO1Q+SaVLQqdyRxLuUPmkYo1SrFGKNcrFl1PpknCShE5lkmKNUqxRijXKxZdLQqdyotIloVP5ZsUapVijFGuUiw9T+UuS8CaVv6RYoxRrlGKNcvGyJPwlSehUuiTckYS/rFijFGuUYo0Sf/DFkvAmlW9WrFGKNUqxRrl4KAknKl0SOpWTJJyodEk4UbkjCXeodEk4UXlTsUYp1ijFGuXiIZUuCXck4USlS0KXhE7lk1TelIRO5YlijVKsUYo1ysXLVLok3KHSJaFT6ZLQJaFTOUnCicodSehUTpLwpmKNUqxRijXKxUNJ6FSeSEKn0iXhROUkCZ3Kb0pCp/KmYo1SrFGKNcrFh6mcJKFT6ZJwotIloVPpVLokdCpdEjqVLgmdyh1J6FSeKNYoxRqlWKPEH3yxJHQqJ0noVO5IwolKl4ROpUtCp/JEsUYp1ijFGuXioST8JpU7ktCpnCShUzlROVHpktCpvKlYoxRrlGKNcvEylTcl4USlS8JJEt6UhBOVTqVLQqfyRLFGKdYoxRrl4sOScIfKEyonSehUTpJwh0qXhN9UrFGKNUqxRrn4ckn4JJU7kvA/FWuUYo1SrFEuvpxKl4QTlS4JncodSehU7kjCm4o1SrFGKdYoFx+m8klJOFE5UemScKJyRxI6lU7lTcUapVijFGuUi5cl4X9SOUnCicoTSThJQqfypmKNUqxRijVK/MEao1ijFGuUYo1SrFGKNUqxRinWKMUapVijFGuUYo1SrFGKNUqxRinWKP8AKoQP/lIBoMIAAAAASUVORK5CYII="
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
91
src/analytics/PosthogAnalytics.test.ts
Normal file
91
src/analytics/PosthogAnalytics.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import {
|
||||
expect,
|
||||
describe,
|
||||
it,
|
||||
vi,
|
||||
beforeEach,
|
||||
beforeAll,
|
||||
afterAll,
|
||||
} from "vitest";
|
||||
|
||||
import { PosthogAnalytics } from "./PosthogAnalytics";
|
||||
import { mockConfig } from "../utils/test";
|
||||
|
||||
describe("PosthogAnalytics", () => {
|
||||
describe("embedded package", () => {
|
||||
beforeAll(() => {
|
||||
vi.stubEnv("VITE_PACKAGE", "embedded");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig({});
|
||||
window.location.hash = "#";
|
||||
PosthogAnalytics.resetInstance();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it("does not create instance without config value or URL params", () => {
|
||||
expect(PosthogAnalytics.instance.isEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores config value and does not create instance", () => {
|
||||
mockConfig({
|
||||
posthog: {
|
||||
api_host: "https://api.example.com.localhost",
|
||||
api_key: "api_key",
|
||||
},
|
||||
});
|
||||
expect(PosthogAnalytics.instance.isEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it("uses URL params if both set", () => {
|
||||
window.location.hash = `#?posthogApiHost=${encodeURIComponent("https://url.example.com.localhost")}&posthogApiKey=api_key`;
|
||||
expect(PosthogAnalytics.instance.isEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("full package", () => {
|
||||
beforeAll(() => {
|
||||
vi.stubEnv("VITE_PACKAGE", "full");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig({});
|
||||
window.location.hash = "#";
|
||||
PosthogAnalytics.resetInstance();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it("does not create instance without config value", () => {
|
||||
expect(PosthogAnalytics.instance.isEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores URL params and does not create instance", () => {
|
||||
window.location.hash = `#?posthogApiHost=${encodeURIComponent("https://url.example.com.localhost")}&posthogApiKey=api_key`;
|
||||
expect(PosthogAnalytics.instance.isEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it("creates instance with config value", () => {
|
||||
mockConfig({
|
||||
posthog: {
|
||||
api_host: "https://api.example.com.localhost",
|
||||
api_key: "api_key",
|
||||
},
|
||||
});
|
||||
expect(PosthogAnalytics.instance.isEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -10,9 +10,9 @@ import posthog, {
|
||||
type PostHog,
|
||||
type Properties,
|
||||
} from "posthog-js";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { Buffer } from "buffer";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
import { type Subscription } from "rxjs";
|
||||
|
||||
import { widget } from "../widget";
|
||||
import {
|
||||
@@ -71,11 +71,6 @@ interface PlatformProperties {
|
||||
cryptoVersion?: string;
|
||||
}
|
||||
|
||||
interface PosthogSettings {
|
||||
project_api_key?: string;
|
||||
api_host?: string;
|
||||
}
|
||||
|
||||
export class PosthogAnalytics {
|
||||
/* Wrapper for Posthog analytics.
|
||||
* 3 modes of anonymity are supported, governed by this.anonymity
|
||||
@@ -91,7 +86,7 @@ export class PosthogAnalytics {
|
||||
* 1. Declare a type for the event, extending IPosthogEvent.
|
||||
*/
|
||||
|
||||
private static ANALYTICS_EVENT_TYPE = "im.vector.analytics";
|
||||
private static ANALYTICS_EVENT_TYPE = "im.vector.analytics" as const;
|
||||
|
||||
// set true during the constructor if posthog config is present, otherwise false
|
||||
private static internalInstance: PosthogAnalytics | null = null;
|
||||
@@ -101,6 +96,7 @@ export class PosthogAnalytics {
|
||||
private anonymity = Anonymity.Disabled;
|
||||
private platformSuperProperties = {};
|
||||
private registrationType: RegistrationType = RegistrationType.Guest;
|
||||
private optInListener: Subscription | null = null;
|
||||
|
||||
public static hasInstance(): boolean {
|
||||
return Boolean(this.internalInstance);
|
||||
@@ -113,24 +109,27 @@ export class PosthogAnalytics {
|
||||
return this.internalInstance;
|
||||
}
|
||||
|
||||
public static resetInstance(): void {
|
||||
// Reset the singleton instance
|
||||
this.internalInstance = null;
|
||||
}
|
||||
|
||||
private constructor(private readonly posthog: PostHog) {
|
||||
const posthogConfig: PosthogSettings = {
|
||||
project_api_key: Config.get().posthog?.api_key,
|
||||
api_host: Config.get().posthog?.api_host,
|
||||
};
|
||||
let apiKey: string | undefined;
|
||||
let apiHost: string | undefined;
|
||||
if (import.meta.env.VITE_PACKAGE === "embedded") {
|
||||
// for the embedded package we always use the values from the URL as the widget host is responsible for analytics configuration
|
||||
apiKey = getUrlParams().posthogApiKey ?? undefined;
|
||||
apiHost = getUrlParams().posthogApiHost ?? undefined;
|
||||
} else if (import.meta.env.VITE_PACKAGE === "full") {
|
||||
// in full package it is the server responsible for the analytics
|
||||
apiKey = Config.get().posthog?.api_key;
|
||||
apiHost = Config.get().posthog?.api_host;
|
||||
}
|
||||
|
||||
if (posthogConfig.project_api_key && posthogConfig.api_host) {
|
||||
if (
|
||||
PosthogAnalytics.getPlatformProperties().matrixBackend === "embedded"
|
||||
) {
|
||||
const { analyticsID } = getUrlParams();
|
||||
// if the embedding platform (element web) already got approval to communicating with posthog
|
||||
// element call can also send events to posthog
|
||||
optInAnalytics.setValue(Boolean(analyticsID));
|
||||
}
|
||||
|
||||
this.posthog.init(posthogConfig.project_api_key, {
|
||||
api_host: posthogConfig.api_host,
|
||||
if (apiKey && apiHost) {
|
||||
this.posthog.init(apiKey, {
|
||||
api_host: apiHost,
|
||||
autocapture: false,
|
||||
mask_all_text: true,
|
||||
mask_all_element_attributes: true,
|
||||
@@ -146,7 +145,6 @@ export class PosthogAnalytics {
|
||||
);
|
||||
this.enabled = false;
|
||||
}
|
||||
this.startListeningToSettingsChanges(); // Triggers maybeIdentifyUser
|
||||
}
|
||||
|
||||
private sanitizeProperties = (
|
||||
@@ -272,14 +270,14 @@ export class PosthogAnalytics {
|
||||
|
||||
private async getAnalyticsId(): Promise<string | null> {
|
||||
const client: MatrixClient = window.matrixclient;
|
||||
let accountAnalyticsId;
|
||||
let accountAnalyticsId: string | null;
|
||||
if (widget) {
|
||||
accountAnalyticsId = getUrlParams().analyticsID;
|
||||
accountAnalyticsId = getUrlParams().posthogUserId;
|
||||
} else {
|
||||
const accountData = await client.getAccountDataFromServer(
|
||||
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
||||
);
|
||||
accountAnalyticsId = accountData?.id;
|
||||
accountAnalyticsId = accountData?.id ?? null;
|
||||
}
|
||||
if (accountAnalyticsId) {
|
||||
// we dont just use the element web analytics ID because that would allow to associate
|
||||
@@ -297,7 +295,7 @@ export class PosthogAnalytics {
|
||||
const posthogIdMaterial = "ec" + accountAnalyticsId + client.getUserId();
|
||||
const bufferForPosthogId = await crypto.subtle.digest(
|
||||
"sha-256",
|
||||
Buffer.from(posthogIdMaterial, "utf-8"),
|
||||
new TextEncoder().encode(posthogIdMaterial),
|
||||
);
|
||||
const view = new Int32Array(bufferForPosthogId);
|
||||
return Array.from(view)
|
||||
@@ -328,6 +326,8 @@ export class PosthogAnalytics {
|
||||
if (this.enabled) {
|
||||
this.posthog.reset();
|
||||
}
|
||||
this.optInListener?.unsubscribe();
|
||||
this.optInListener = null;
|
||||
this.setAnonymity(Anonymity.Disabled);
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ export class PosthogAnalytics {
|
||||
}
|
||||
}
|
||||
|
||||
private startListeningToSettingsChanges(): void {
|
||||
public startListeningToSettingsChanges(): void {
|
||||
// Listen to account data changes from sync so we can observe changes to relevant flags and update.
|
||||
// This is called -
|
||||
// * On page load, when the account data is first received by sync
|
||||
@@ -415,7 +415,7 @@ export class PosthogAnalytics {
|
||||
// * When the user changes their preferences on this device
|
||||
// Note that for new accounts, pseudonymousAnalyticsOptIn won't be set, so updateAnonymityFromSettings
|
||||
// won't be called (i.e. this.anonymity will be left as the default, until the setting changes)
|
||||
optInAnalytics.value$.subscribe((optIn) => {
|
||||
this.optInListener ??= optInAnalytics.value$.subscribe((optIn) => {
|
||||
this.setAnonymity(optIn ? Anonymity.Pseudonymous : Anonymity.Disabled);
|
||||
this.maybeIdentifyUser().catch(() =>
|
||||
logger.log("Could not identify user"),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type DisconnectReason } from "livekit-client";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc";
|
||||
|
||||
import {
|
||||
type IPosthogEvent,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
type Span,
|
||||
} from "@opentelemetry/sdk-trace-base";
|
||||
import { hrTimeToMilliseconds } from "@opentelemetry/core";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { PosthogAnalytics } from "./PosthogAnalytics";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2021-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -48,12 +48,12 @@ export const LoginPage: FC = () => {
|
||||
}
|
||||
|
||||
login(homeserver, usernameRef.current.value, passwordRef.current.value)
|
||||
.then(([client, session]) => {
|
||||
.then(async ([client, session]) => {
|
||||
if (!setClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
setClient({ client, session });
|
||||
setClient(client, session);
|
||||
|
||||
const locationState = location.state;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
@@ -61,9 +61,9 @@ export const LoginPage: FC = () => {
|
||||
if (locationState && locationState.from) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
navigate(locationState.from);
|
||||
await navigate(locationState.from);
|
||||
} else {
|
||||
navigate("/");
|
||||
await navigate("/");
|
||||
}
|
||||
PosthogAnalytics.instance.eventLogin.track();
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2021-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -16,9 +16,9 @@ import {
|
||||
} from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { captureException } from "@sentry/react";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import { sleep } from "matrix-js-sdk/lib/utils";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { Button, Text } from "@vector-im/compound-web";
|
||||
|
||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||
@@ -26,7 +26,7 @@ import { useClientLegacy } from "../ClientContext";
|
||||
import { useInteractiveRegistration } from "./useInteractiveRegistration";
|
||||
import styles from "./LoginPage.module.css";
|
||||
import Logo from "../icons/LogoLarge.svg?react";
|
||||
import { LoadingView } from "../FullScreenView";
|
||||
import { LoadingPage } from "../FullScreenView";
|
||||
import { useRecaptcha } from "./useRecaptcha";
|
||||
import { usePageTitle } from "../usePageTitle";
|
||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||
@@ -95,20 +95,20 @@ export const RegisterPage: FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
setClient?.({ client: newClient, session });
|
||||
setClient?.(newClient, session);
|
||||
PosthogAnalytics.instance.eventSignup.cacheSignupEnd(new Date());
|
||||
};
|
||||
|
||||
submit()
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (location.state?.from) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
navigate(location.state?.from);
|
||||
await navigate(location.state?.from);
|
||||
} else {
|
||||
navigate("/");
|
||||
await navigate("/");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -141,12 +141,14 @@ export const RegisterPage: FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && authenticated && !passwordlessUser && !registering) {
|
||||
navigate("/");
|
||||
navigate("/")?.catch((error) => {
|
||||
logger.error("Failed to navigate to /", error);
|
||||
});
|
||||
}
|
||||
}, [loading, navigate, authenticated, passwordlessUser, registering]);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingView />;
|
||||
return <LoadingPage />;
|
||||
} else {
|
||||
PosthogAnalytics.instance.eventSignup.cacheSignupStart(new Date());
|
||||
}
|
||||
@@ -202,7 +204,7 @@ export const RegisterPage: FC = () => {
|
||||
/>
|
||||
</FieldRow>
|
||||
<Text size="sm">
|
||||
<Trans i18nKey="recaptcha_caption">
|
||||
<Trans i18nKey="recaptcha_ssla_caption">
|
||||
This site is protected by ReCAPTCHA and the Google{" "}
|
||||
<ExternalLink href="https://www.google.com/policies/privacy/">
|
||||
Privacy Policy
|
||||
@@ -214,8 +216,8 @@ export const RegisterPage: FC = () => {
|
||||
apply.
|
||||
<br />
|
||||
By clicking "Register", you agree to our{" "}
|
||||
<ExternalLink href={Config.get().eula}>
|
||||
End User Licensing Agreement (EULA)
|
||||
<ExternalLink href={Config.get().ssla}>
|
||||
Software and Services License Agreement (SSLA)
|
||||
</ExternalLink>
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
||||
import { InteractiveAuth } from "matrix-js-sdk";
|
||||
import {
|
||||
createClient,
|
||||
type LoginResponse,
|
||||
type MatrixClient,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
} from "matrix-js-sdk";
|
||||
|
||||
import { initClient } from "../utils/matrix";
|
||||
import { type Session } from "../ClientContext";
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
||||
import { InteractiveAuth } from "matrix-js-sdk";
|
||||
import {
|
||||
createClient,
|
||||
type MatrixClient,
|
||||
type RegisterResponse,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
} from "matrix-js-sdk";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { initClient } from "../utils/matrix";
|
||||
import { type Session } from "../ClientContext";
|
||||
@@ -39,7 +39,7 @@ export const useInteractiveRegistration = (
|
||||
undefined,
|
||||
);
|
||||
|
||||
const authClient = useRef<MatrixClient>();
|
||||
const authClient = useRef<MatrixClient | undefined>(undefined);
|
||||
if (!authClient.current) {
|
||||
authClient.current = createClient({
|
||||
baseUrl: Config.defaultHomeserverUrl()!,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useEffect, useCallback, useRef, useState } from "react";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { secureRandomString } from "matrix-js-sdk/lib/randomstring";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { translatedError } from "../TranslatedError";
|
||||
declare global {
|
||||
@@ -31,8 +31,8 @@ export function useRecaptcha(sitekey?: string): {
|
||||
recaptchaId: string;
|
||||
} {
|
||||
const { t } = useTranslation();
|
||||
const [recaptchaId] = useState(() => randomString(16));
|
||||
const promiseRef = useRef<RecaptchaPromiseRef>();
|
||||
const [recaptchaId] = useState(() => secureRandomString(16));
|
||||
const promiseRef = useRef<RecaptchaPromiseRef | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sitekey) return;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { secureRandomString } from "matrix-js-sdk/lib/randomstring";
|
||||
|
||||
import { useClient } from "../ClientContext";
|
||||
import { useInteractiveRegistration } from "../auth/useInteractiveRegistration";
|
||||
@@ -42,12 +42,12 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
|
||||
const userName = generateRandomName();
|
||||
const [client, session] = await register(
|
||||
userName,
|
||||
randomString(16),
|
||||
secureRandomString(16),
|
||||
displayName,
|
||||
recaptchaResponse,
|
||||
true,
|
||||
);
|
||||
setClient({ client, session });
|
||||
setClient(client, session);
|
||||
} catch (e) {
|
||||
reset();
|
||||
throw e;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
import { type ComponentPropsWithoutRef, type FC } from "react";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ import { expect, test } from "vitest";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { userEvent } from "@testing-library/user-event";
|
||||
import { type ReactNode } from "react";
|
||||
import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
||||
import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc";
|
||||
|
||||
import { ReactionToggleButton } from "./ReactionToggleButton";
|
||||
import { ElementCallReactionEventType } from "../reactions";
|
||||
@@ -47,7 +47,7 @@ test("Can open menu", async () => {
|
||||
const { getByLabelText, container } = render(
|
||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||
);
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -58,8 +58,8 @@ test("Can raise hand", async () => {
|
||||
const { getByLabelText, container } = render(
|
||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||
);
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("action.raise_hand"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
await user.click(getByLabelText("Raise hand"));
|
||||
expect(rtcSession.room.client.sendEvent).toHaveBeenCalledWith(
|
||||
rtcSession.room.roomId,
|
||||
"m.reaction",
|
||||
@@ -92,8 +92,8 @@ test("Can lower hand", async () => {
|
||||
const { getByLabelText, container } = render(
|
||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||
);
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("action.raise_hand"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
await user.click(getByLabelText("Raise hand"));
|
||||
act(() => {
|
||||
handRaisedSubject$.next({
|
||||
[localIdent]: {
|
||||
@@ -103,8 +103,8 @@ test("Can lower hand", async () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("action.lower_hand"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
await user.click(getByLabelText("Lower hand"));
|
||||
expect(rtcSession.room.client.redactEvent).toHaveBeenCalledWith(
|
||||
rtcSession.room.roomId,
|
||||
reactionEventId,
|
||||
@@ -122,7 +122,7 @@ test("Can react with emoji", async () => {
|
||||
const { getByLabelText, getByText } = render(
|
||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||
);
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
await user.click(getByText("🐶"));
|
||||
expect(rtcSession.room.client.sendEvent).toHaveBeenCalledWith(
|
||||
rtcSession.room.roomId,
|
||||
@@ -144,8 +144,8 @@ test("Can fully expand emoji picker", async () => {
|
||||
const { getByLabelText, container, getByText } = render(
|
||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||
);
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("action.show_more"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
await user.click(getByLabelText("Show more"));
|
||||
expect(container).toMatchSnapshot();
|
||||
await user.click(getByText("🦗"));
|
||||
expect(rtcSession.room.client.sendEvent).toHaveBeenCalledWith(
|
||||
@@ -168,8 +168,8 @@ test("Can close reaction dialog", async () => {
|
||||
const { getByLabelText, container } = render(
|
||||
<TestComponent vm={vm} rtcSession={rtcSession} />,
|
||||
);
|
||||
await user.click(getByLabelText("common.reactions"));
|
||||
await user.click(getByLabelText("action.show_more"));
|
||||
await user.click(getByLabelText("action.show_less"));
|
||||
await user.click(getByLabelText("Reactions"));
|
||||
await user.click(getByLabelText("Show more"));
|
||||
await user.click(getByLabelText("Show less"));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import classNames from "classnames";
|
||||
import { useObservableState } from "observable-hooks";
|
||||
import { map } from "rxjs";
|
||||
@@ -87,7 +87,7 @@ export function ReactionPopupMenu({
|
||||
<Alert
|
||||
className={styles.alert}
|
||||
type="critical"
|
||||
title={t("common.something_went_wrong")}
|
||||
title={t("error.generic")}
|
||||
>
|
||||
{errorText}
|
||||
</Alert>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2021-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
type ConfigOptions,
|
||||
type ResolvedConfigOptions,
|
||||
} from "./ConfigOptions";
|
||||
import { isFailure } from "../utils/fetch";
|
||||
|
||||
export class Config {
|
||||
private static internalInstance: Config | undefined;
|
||||
@@ -28,7 +29,20 @@ export class Config {
|
||||
const internalInstance = new Config();
|
||||
Config.internalInstance = internalInstance;
|
||||
|
||||
Config.internalInstance.initPromise = downloadConfig("/config.json").then(
|
||||
let fetchTarget: string;
|
||||
|
||||
if (
|
||||
window.location.pathname.endsWith("/room/") ||
|
||||
window.location.pathname.endsWith("/room")
|
||||
) {
|
||||
// it looks like we are running in standalone mode so use the config at the root
|
||||
fetchTarget = new URL("/config.json", window.location.href).href;
|
||||
} else {
|
||||
// otherwise we are probably running as a widget so use the config in the same directory
|
||||
fetchTarget = "config.json";
|
||||
}
|
||||
|
||||
Config.internalInstance.initPromise = downloadConfig(fetchTarget).then(
|
||||
(config) => {
|
||||
internalInstance.config = merge({}, DEFAULT_CONFIG, config);
|
||||
},
|
||||
@@ -70,18 +84,15 @@ export class Config {
|
||||
private initPromise?: Promise<void>;
|
||||
}
|
||||
|
||||
async function downloadConfig(
|
||||
configJsonFilename: string,
|
||||
): Promise<ConfigOptions> {
|
||||
const url = new URL(configJsonFilename, window.location.href);
|
||||
const res = await fetch(url);
|
||||
async function downloadConfig(fetchTarget: string): Promise<ConfigOptions> {
|
||||
const response = await fetch(fetchTarget);
|
||||
|
||||
if (!res.ok || res.status === 404 || res.status === 0) {
|
||||
if (isFailure(response)) {
|
||||
// Lack of a config isn't an error, we should just use the defaults.
|
||||
// Also treat a blank config as no config, assuming the status code is 0, because we don't get 404s from file:
|
||||
// URIs so this is the only way we can not fail if the file doesn't exist when loading from a file:// URI.
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
return res.json();
|
||||
return response.json();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
export interface ConfigOptions {
|
||||
/**
|
||||
* The Posthog endpoint to which analytics data will be sent.
|
||||
* This is only used in the full package of Element Call.
|
||||
*/
|
||||
posthog?: {
|
||||
api_key: string;
|
||||
@@ -15,6 +16,7 @@ export interface ConfigOptions {
|
||||
};
|
||||
/**
|
||||
* The Sentry endpoint to which crash data will be sent.
|
||||
* This is only used in the full package of Element Call.
|
||||
*/
|
||||
sentry?: {
|
||||
DSN: string;
|
||||
@@ -22,6 +24,7 @@ export interface ConfigOptions {
|
||||
};
|
||||
/**
|
||||
* The rageshake server to which feedback and debug logs will be sent.
|
||||
* This is only used in the full package of Element Call.
|
||||
*/
|
||||
rageshake?: {
|
||||
submit_url: string;
|
||||
@@ -29,7 +32,7 @@ export interface ConfigOptions {
|
||||
|
||||
/**
|
||||
* Sets the URL to send opentelemetry data to. If unset, opentelemetry will
|
||||
* be disabled.
|
||||
* be disabled. This is only used in the full package of Element Call.
|
||||
*/
|
||||
opentelemetry?: {
|
||||
collector_url: string;
|
||||
@@ -74,9 +77,9 @@ export interface ConfigOptions {
|
||||
};
|
||||
|
||||
/**
|
||||
* A link to the end-user license agreement (EULA)
|
||||
* A link to the software and services license agreement (SSLA)
|
||||
*/
|
||||
eula: string;
|
||||
ssla?: string;
|
||||
|
||||
media_devices?: {
|
||||
/**
|
||||
@@ -131,6 +134,7 @@ export interface ResolvedConfigOptions extends ConfigOptions {
|
||||
server_name: string;
|
||||
};
|
||||
};
|
||||
ssla: string;
|
||||
media_devices: {
|
||||
enable_audio: boolean;
|
||||
enable_video: boolean;
|
||||
@@ -148,7 +152,7 @@ export const DEFAULT_CONFIG: ResolvedConfigOptions = {
|
||||
features: {
|
||||
feature_use_device_session_member_events: true,
|
||||
},
|
||||
eula: "https://static.element.io/legal/online-EULA.pdf",
|
||||
ssla: "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
|
||||
media_devices: {
|
||||
enable_audio: true,
|
||||
enable_video: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@ import { describe, expect, test, vi } from "vitest";
|
||||
import {
|
||||
type MatrixRTCSession,
|
||||
MatrixRTCSessionEvent,
|
||||
} from "matrix-js-sdk/src/matrixrtc";
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
import { KeyProviderEvent } from "livekit-client";
|
||||
|
||||
import { MatrixKeyProvider } from "./matrixKeyProvider";
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { BaseKeyProvider, createKeyMaterialFromBuffer } from "livekit-client";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import {
|
||||
type MatrixRTCSession,
|
||||
MatrixRTCSessionEvent,
|
||||
} from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
|
||||
export class MatrixKeyProvider extends BaseKeyProvider {
|
||||
private rtcSession?: MatrixRTCSession;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
} from "react";
|
||||
import useMeasure from "react-use-measure";
|
||||
import classNames from "classnames";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { useObservableEagerState } from "observable-hooks";
|
||||
import { fromEvent, map, startWith } from "rxjs";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
/*
|
||||
Copyright 2023, 2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type ComponentType, memo, type RefObject, useRef } from "react";
|
||||
import {
|
||||
type ComponentType,
|
||||
type JSX,
|
||||
memo,
|
||||
type RefObject,
|
||||
useRef,
|
||||
} from "react";
|
||||
import { type EventTypes, type Handler, useDrag } from "@use-gesture/react";
|
||||
import { type SpringValue } from "@react-spring/web";
|
||||
import classNames from "classnames";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render, type RenderResult } from "@testing-library/react";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { type RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { type Room } from "matrix-js-sdk/src/models/room";
|
||||
import { type RoomMember, type Room, type MatrixClient } from "matrix-js-sdk";
|
||||
import { type FC, useCallback, type MouseEvent, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconButton, Text } from "@vector-im/compound-web";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2021-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -9,10 +9,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { type FC } from "react";
|
||||
|
||||
import { useClientState } from "../ClientContext";
|
||||
import { ErrorView, LoadingView } from "../FullScreenView";
|
||||
import { ErrorPage, LoadingPage } from "../FullScreenView";
|
||||
import { UnauthenticatedView } from "./UnauthenticatedView";
|
||||
import { RegisteredView } from "./RegisteredView";
|
||||
import { usePageTitle } from "../usePageTitle";
|
||||
import { widget } from "../widget.ts";
|
||||
|
||||
export const HomePage: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -21,9 +22,9 @@ export const HomePage: FC = () => {
|
||||
const clientState = useClientState();
|
||||
|
||||
if (!clientState) {
|
||||
return <LoadingView />;
|
||||
return <LoadingPage />;
|
||||
} else if (clientState.state === "error") {
|
||||
return <ErrorView error={clientState.error} />;
|
||||
return <ErrorPage widget={widget} error={clientState.error} />;
|
||||
} else {
|
||||
return clientState.authenticated ? (
|
||||
<RegisteredView client={clientState.authenticated.client} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
@@ -12,10 +12,10 @@ import {
|
||||
type FormEventHandler,
|
||||
type FC,
|
||||
} from "react";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Heading, Text } from "@vector-im/compound-web";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
@@ -77,7 +77,7 @@ export const RegisteredView: FC<Props> = ({ client }) => {
|
||||
if (!createRoomResult.password)
|
||||
throw new Error("Failed to create room with shared secret");
|
||||
|
||||
navigate(
|
||||
await navigate(
|
||||
getRelativeRoomUrl(
|
||||
createRoomResult.roomId,
|
||||
{ kind: E2eeType.SHARED_KEY, secret: createRoomResult.password },
|
||||
@@ -106,7 +106,9 @@ export const RegisteredView: FC<Props> = ({ client }) => {
|
||||
|
||||
const [existingAlias, setExistingAlias] = useState<string>();
|
||||
const onJoinExistingRoom = useCallback(() => {
|
||||
navigate(`/${existingAlias}`);
|
||||
navigate(`/${existingAlias}`)?.catch((error) => {
|
||||
logger.error("Failed to navigate to existing alias", error);
|
||||
});
|
||||
}, [navigate, existingAlias]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
Copyright 2022-2024 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC, useCallback, useState, type FormEventHandler } from "react";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { secureRandomString } from "matrix-js-sdk/lib/randomstring";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Button, Heading, Text } from "@vector-im/compound-web";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useClient } from "../ClientContext";
|
||||
@@ -67,7 +67,7 @@ export const UnauthenticatedView: FC = () => {
|
||||
const userName = generateRandomName();
|
||||
const [client, session] = await register(
|
||||
userName,
|
||||
randomString(16),
|
||||
secureRandomString(16),
|
||||
displayName,
|
||||
recaptchaResponse,
|
||||
true,
|
||||
@@ -89,9 +89,11 @@ export const UnauthenticatedView: FC = () => {
|
||||
// @ts-ignore
|
||||
if (error.errcode === "M_ROOM_IN_USE") {
|
||||
setOnFinished(() => {
|
||||
setClient({ client, session });
|
||||
setClient(client, session);
|
||||
const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName);
|
||||
navigate(`/${aliasLocalpart}`);
|
||||
navigate(`/${aliasLocalpart}`)?.catch((error) => {
|
||||
logger.error("Failed to navigate to alias localpart", error);
|
||||
});
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
@@ -109,8 +111,8 @@ export const UnauthenticatedView: FC = () => {
|
||||
if (!createRoomResult.password)
|
||||
throw new Error("Failed to create room with shared secret");
|
||||
|
||||
setClient({ client, session });
|
||||
navigate(
|
||||
setClient(client, session);
|
||||
await navigate(
|
||||
getRelativeRoomUrl(
|
||||
createRoomResult.roomId,
|
||||
{ kind: E2eeType.SHARED_KEY, secret: createRoomResult.password },
|
||||
@@ -183,10 +185,10 @@ export const UnauthenticatedView: FC = () => {
|
||||
</Text>
|
||||
)}
|
||||
<Text size="sm" className={styles.notice}>
|
||||
<Trans i18nKey="unauthenticated_view_eula_caption">
|
||||
<Trans i18nKey="unauthenticated_view_ssla_caption">
|
||||
By clicking "Go", you agree to our{" "}
|
||||
<ExternalLink href={Config.get().eula}>
|
||||
End User Licensing Agreement (EULA)
|
||||
<ExternalLink href={Config.get().ssla}>
|
||||
Software and Services License Agreement (SSLA)
|
||||
</ExternalLink>
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user