Merge branch 'livekit' into toger5/track-processor-blur

This commit is contained in:
Hugh Nimmo-Smith
2025-01-06 17:21:30 +00:00
committed by GitHub
19 changed files with 115 additions and 264 deletions

View File

@@ -6,14 +6,8 @@ Please see LICENSE in the repository root for full details.
*/
import { type FC, Suspense, useEffect, useState } from "react";
import {
BrowserRouter as Router,
Switch,
Route,
useLocation,
} from "react-router-dom";
import { BrowserRouter, Route, useLocation, Routes } from "react-router-dom";
import * as Sentry from "@sentry/react";
import { type History } from "history";
import { TooltipProvider } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";
@@ -30,7 +24,7 @@ import { widget } from "./widget";
import { useTheme } from "./useTheme";
import { ProcessorProvider } from "./livekit/TrackProcessorContext";
const SentryRoute = Sentry.withSentryRouting(Route);
const SentryRoute = Sentry.withSentryReactRouterV6Routing(Route);
interface SimpleProviderProps {
children: JSX.Element;
@@ -56,11 +50,7 @@ const ThemeProvider: FC<SimpleProviderProps> = ({ children }) => {
return children;
};
interface AppProps {
history: History;
}
export const App: FC<AppProps> = ({ history }) => {
export const App: FC = () => {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
Initializer.init()
@@ -77,7 +67,7 @@ export const App: FC<AppProps> = ({ history }) => {
return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<Router history={history}>
<BrowserRouter>
<BackgroundProvider>
<ThemeProvider>
<TooltipProvider>
@@ -86,20 +76,15 @@ export const App: FC<AppProps> = ({ history }) => {
<Providers>
<Sentry.ErrorBoundary fallback={errorPage}>
<DisconnectedBanner />
<Switch>
<SentryRoute exact path="/">
<HomePage />
</SentryRoute>
<SentryRoute exact path="/login">
<LoginPage />
</SentryRoute>
<SentryRoute exact path="/register">
<RegisterPage />
</SentryRoute>
<SentryRoute path="*">
<RoomPage />
</SentryRoute>
</Switch>
<Routes>
<SentryRoute path="/" element={<HomePage />} />
<SentryRoute path="/login" element={<LoginPage />} />
<SentryRoute
path="/register"
element={<RegisterPage />}
/>
<SentryRoute path="*" element={<RoomPage />} />
</Routes>
</Sentry.ErrorBoundary>
</Providers>
</Suspense>
@@ -109,7 +94,7 @@ export const App: FC<AppProps> = ({ history }) => {
</TooltipProvider>
</ThemeProvider>
</BackgroundProvider>
</Router>
</BrowserRouter>
);
};

View File

@@ -15,7 +15,7 @@ import {
useRef,
useMemo,
} from "react";
import { useHistory } from "react-router-dom";
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";
@@ -144,7 +144,7 @@ interface Props {
}
export const ClientProvider: FC<Props> = ({ children }) => {
const history = useHistory();
const navigate = useNavigate();
// null = signed out, undefined = loading
const [initClientState, setInitClientState] = useState<
@@ -228,9 +228,9 @@ export const ClientProvider: FC<Props> = ({ children }) => {
await client.clearStores();
clearSession();
setInitClientState(null);
history.push("/");
navigate("/");
PosthogAnalytics.instance.setRegistrationType(RegistrationType.Guest);
}, [history, initClientState?.client]);
}, [navigate, initClientState?.client]);
const { t } = useTranslation();

View File

@@ -82,7 +82,7 @@ export const UserMenu: FC<Props> = ({
if (!isAuthenticated) {
return (
<LinkButton to={{ pathname: "/login", state: { from: location } }}>
<LinkButton to={{ pathname: "/login" }} state={{ from: location }}>
{t("log_in")}
</LinkButton>
);

View File

@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
*/
import { type FC, useCallback, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";
import { useClientLegacy } from "./ClientContext";
import { useProfile } from "./profile/useProfile";
@@ -19,7 +19,7 @@ interface Props {
export const UserMenuContainer: FC<Props> = ({ preventNavigation = false }) => {
const location = useLocation();
const history = useHistory();
const navigate = useNavigate();
const { client, logout, authenticated, passwordlessUser } = useClientLegacy();
const { displayName, avatarUrl } = useProfile(client);
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
@@ -45,11 +45,11 @@ export const UserMenuContainer: FC<Props> = ({ preventNavigation = false }) => {
logout?.();
break;
case "login":
history.push("/login", { state: { from: location } });
navigate("/login", { state: { from: location } });
break;
}
},
[history, location, logout, setSettingsModalOpen],
[navigate, location, logout, setSettingsModalOpen],
);
const userName = client?.getUserIdLocalpart() ?? "";

View File

@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
*/
import { type FC, type FormEvent, useCallback, useRef, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";
import { Trans, useTranslation } from "react-i18next";
import { Button } from "@vector-im/compound-web";
@@ -29,7 +29,7 @@ export const LoginPage: FC = () => {
const homeserver = Config.defaultHomeserverUrl(); // TODO: Make this configurable
const usernameRef = useRef<HTMLInputElement>(null);
const passwordRef = useRef<HTMLInputElement>(null);
const history = useHistory();
const navigate = useNavigate();
const location = useLocation();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error>();
@@ -61,9 +61,9 @@ export const LoginPage: FC = () => {
if (locationState && locationState.from) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
history.push(locationState.from);
navigate(locationState.from);
} else {
history.push("/");
navigate("/");
}
PosthogAnalytics.instance.eventLogin.track();
})
@@ -72,7 +72,7 @@ export const LoginPage: FC = () => {
setLoading(false);
});
},
[login, location, history, homeserver, setClient],
[login, location, navigate, homeserver, setClient],
);
// we need to limit the length of the homserver name to not cover the whole loginview input with the string.
let shortendHomeserverName = Config.defaultServerName()?.slice(0, 25);

View File

@@ -14,7 +14,7 @@ import {
useRef,
useState,
} from "react";
import { useHistory, useLocation } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";
import { captureException } from "@sentry/react";
import { sleep } from "matrix-js-sdk/src/utils";
import { Trans, useTranslation } from "react-i18next";
@@ -41,7 +41,7 @@ export const RegisterPage: FC = () => {
useClientLegacy();
const confirmPasswordRef = useRef<HTMLInputElement>(null);
const history = useHistory();
const navigate = useNavigate();
const location = useLocation();
const [registering, setRegistering] = useState(false);
const [error, setError] = useState<Error>();
@@ -106,9 +106,9 @@ export const RegisterPage: FC = () => {
if (location.state?.from) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
history.push(location.state?.from);
navigate(location.state?.from);
} else {
history.push("/");
navigate("/");
}
})
.catch((error) => {
@@ -120,7 +120,7 @@ export const RegisterPage: FC = () => {
[
register,
location,
history,
navigate,
passwordlessUser,
reset,
execute,
@@ -141,9 +141,9 @@ export const RegisterPage: FC = () => {
useEffect(() => {
if (!loading && authenticated && !passwordlessUser && !registering) {
history.push("/");
navigate("/");
}
}, [loading, history, authenticated, passwordlessUser, registering]);
}, [loading, navigate, authenticated, passwordlessUser, registering]);
if (loading) {
return <LoadingView />;

View File

@@ -9,42 +9,27 @@ import {
type ComponentPropsWithoutRef,
forwardRef,
type MouseEvent,
useCallback,
useMemo,
} from "react";
import { Link as CpdLink } from "@vector-im/compound-web";
import { useHistory } from "react-router-dom";
import { createPath, type LocationDescriptor, type Path } from "history";
import { type LinkProps, useHref, useLinkClickHandler } from "react-router-dom";
import classNames from "classnames";
import { useLatest } from "../useLatest";
import styles from "./Link.module.css";
export function useLink(
to: LocationDescriptor,
to: LinkProps["to"],
state?: unknown,
): [Path, (e: MouseEvent) => void] {
const latestState = useLatest(state);
const history = useHistory();
const path = useMemo(
() => (typeof to === "string" ? to : createPath(to)),
[to],
);
const onClick = useCallback(
(e: MouseEvent) => {
e.preventDefault();
history.push(to, latestState.current);
},
[history, to, latestState],
);
): [string, (e: MouseEvent<HTMLAnchorElement>) => void] {
const href = useHref(to);
const onClick = useLinkClickHandler(to, { state });
return [path, onClick];
return [href, onClick];
}
type Props = Omit<
ComponentPropsWithoutRef<typeof CpdLink>,
"href" | "onClick"
> & { to: LocationDescriptor; state?: unknown };
> & { to: LinkProps["to"]; state?: unknown };
/**
* A version of Compound's link component that integrates with our router setup.

View File

@@ -7,22 +7,22 @@ Please see LICENSE in the repository root for full details.
import { type ComponentPropsWithoutRef, forwardRef } from "react";
import { Button } from "@vector-im/compound-web";
import { type LocationDescriptor } from "history";
import type { LinkProps } from "react-router-dom";
import { useLink } from "./Link";
type Props = Omit<
ComponentPropsWithoutRef<typeof Button<"a">>,
"as" | "href"
> & { to: LocationDescriptor };
> & { to: LinkProps["to"]; state?: unknown };
/**
* A version of Compound's button component that acts as a link and integrates
* with our router setup.
*/
export const LinkButton = forwardRef<HTMLAnchorElement, Props>(
function LinkButton({ to, ...props }, ref) {
const [path, onClick] = useLink(to);
function LinkButton({ to, state, ...props }, ref) {
const [path, onClick] = useLink(to, state);
return <Button as="a" ref={ref} {...props} href={path} onClick={onClick} />;
},
);

View File

@@ -12,12 +12,12 @@ import {
type FormEventHandler,
type FC,
} from "react";
import { useHistory } from "react-router-dom";
import { type MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next";
import { Heading, Text } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";
import { Button } from "@vector-im/compound-web";
import { useNavigate } from "react-router-dom";
import {
createRoom,
@@ -46,7 +46,7 @@ export const RegisteredView: FC<Props> = ({ client }) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error>();
const [optInAnalytics] = useOptInAnalytics();
const history = useHistory();
const navigate = useNavigate();
const { t } = useTranslation();
const [joinExistingCallModalOpen, setJoinExistingCallModalOpen] =
useState(false);
@@ -77,7 +77,7 @@ export const RegisteredView: FC<Props> = ({ client }) => {
if (!createRoomResult.password)
throw new Error("Failed to create room with shared secret");
history.push(
navigate(
getRelativeRoomUrl(
createRoomResult.roomId,
{ kind: E2eeType.SHARED_KEY, secret: createRoomResult.password },
@@ -99,15 +99,15 @@ export const RegisteredView: FC<Props> = ({ client }) => {
}
});
},
[client, history, setJoinExistingCallModalOpen],
[client, navigate, setJoinExistingCallModalOpen],
);
const recentRooms = useGroupCallRooms(client);
const [existingAlias, setExistingAlias] = useState<string>();
const onJoinExistingRoom = useCallback(() => {
history.push(`/${existingAlias}`);
}, [history, existingAlias]);
navigate(`/${existingAlias}`);
}, [navigate, existingAlias]);
return (
<>

View File

@@ -6,11 +6,11 @@ Please see LICENSE in the repository root for full details.
*/
import { type FC, useCallback, useState, type FormEventHandler } from "react";
import { useHistory } from "react-router-dom";
import { randomString } from "matrix-js-sdk/src/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 { useNavigate } from "react-router-dom";
import { useClient } from "../ClientContext";
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
@@ -50,7 +50,7 @@ export const UnauthenticatedView: FC = () => {
[setJoinExistingCallModalOpen],
);
const [onFinished, setOnFinished] = useState<() => void>();
const history = useHistory();
const navigate = useNavigate();
const { t } = useTranslation();
const onSubmit: FormEventHandler<HTMLFormElement> = useCallback(
@@ -91,7 +91,7 @@ export const UnauthenticatedView: FC = () => {
setOnFinished(() => {
setClient({ client, session });
const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName);
history.push(`/${aliasLocalpart}`);
navigate(`/${aliasLocalpart}`);
});
setLoading(false);
@@ -110,7 +110,7 @@ export const UnauthenticatedView: FC = () => {
throw new Error("Failed to create room with shared secret");
setClient({ client, session });
history.push(
navigate(
getRelativeRoomUrl(
createRoomResult.roomId,
{ kind: E2eeType.SHARED_KEY, secret: createRoomResult.password },
@@ -130,7 +130,7 @@ export const UnauthenticatedView: FC = () => {
register,
reset,
execute,
history,
navigate,
setJoinExistingCallModalOpen,
setClient,
],

View File

@@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import React from "react";
import i18n, {
type BackendModule,
type ReadCallback,
@@ -16,6 +17,12 @@ import * as Sentry from "@sentry/react";
import { logger } from "matrix-js-sdk/src/logger";
import { shouldPolyfill as shouldPolyfillSegmenter } from "@formatjs/intl-segmenter/should-polyfill";
import { shouldPolyfill as shouldPolyfillDurationFormat } from "@formatjs/intl-durationformat/should-polyfill";
import {
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
} from "react-router-dom";
import { getUrlParams } from "./UrlParams";
import { Config } from "./config/Config";
@@ -217,7 +224,13 @@ export class Initializer {
dsn: Config.get().sentry?.DSN,
environment: Config.get().sentry?.environment,
integrations: [
Sentry.reactRouterV5BrowserTracingIntegration({ history }),
Sentry.reactRouterV6BrowserTracingIntegration({
useEffect: React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
}),
],
tracesSampleRate: 1.0,
});

View File

@@ -13,7 +13,6 @@ import "matrix-js-sdk/src/browser-index";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { createBrowserHistory } from "history";
import "./index.css";
import { logger } from "matrix-js-sdk/src/logger";
import {
@@ -57,11 +56,9 @@ if (fatalError !== null) {
Initializer.initBeforeReact()
.then(() => {
const history = createBrowserHistory();
root.render(
<StrictMode>
<App history={history} />
<App />
</StrictMode>,
);
})

View File

@@ -14,8 +14,8 @@ import {
} from "react";
import { type MatrixClient } from "matrix-js-sdk/src/client";
import { Trans, useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { Button, Heading, Text } from "@vector-im/compound-web";
import { useNavigate } from "react-router-dom";
import styles from "./CallEndedView.module.css";
import feedbackStyle from "../input/FeedbackInput.module.css";
@@ -46,7 +46,7 @@ export const CallEndedView: FC<Props> = ({
reconnect,
}) => {
const { t } = useTranslation();
const history = useHistory();
const navigate = useNavigate();
const { displayName } = useProfile(client);
const [surveySubmitted, setSurveySubmitted] = useState(false);
@@ -76,12 +76,12 @@ export const CallEndedView: FC<Props> = ({
setSurveySubmitted(true);
} else if (!confineToRoom) {
// if the user already has an account immediately go back to the home screen
history.push("/");
navigate("/");
}
}, 1000);
}, 1000);
},
[endedCallId, history, isPasswordlessUser, confineToRoom, starRating],
[endedCallId, navigate, isPasswordlessUser, confineToRoom, starRating],
);
const createAccountDialog = isPasswordlessUser && (

View File

@@ -11,8 +11,7 @@ import { type MatrixClient } from "matrix-js-sdk/src/client";
import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc";
import { of } from "rxjs";
import { JoinRule, type RoomState } from "matrix-js-sdk/src/matrix";
import { Router } from "react-router-dom";
import { createBrowserHistory } from "history";
import { BrowserRouter } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import { type RelationsContainer } from "matrix-js-sdk/src/models/relations-container";
@@ -78,7 +77,6 @@ function createGroupCallView(widget: WidgetHelpers | null): {
rtcSession: MockRTCSession;
getByText: ReturnType<typeof render>["getByText"];
} {
const history = createBrowserHistory();
const client = {
getUser: () => null,
getUserId: () => localRtcMember.sender,
@@ -111,7 +109,7 @@ function createGroupCallView(widget: WidgetHelpers | null): {
video: { enabled: false },
} as MuteStates;
const { getByText } = render(
<Router history={history}>
<BrowserRouter>
<GroupCallView
client={client}
isPasswordlessUser={false}
@@ -123,7 +121,7 @@ function createGroupCallView(widget: WidgetHelpers | null): {
muteStates={muteState}
widget={widget}
/>
</Router>,
</BrowserRouter>,
);
return {
getByText,

View File

@@ -13,7 +13,6 @@ import {
useRef,
useState,
} from "react";
import { useHistory } from "react-router-dom";
import { type MatrixClient } from "matrix-js-sdk/src/client";
import {
Room,
@@ -24,6 +23,7 @@ import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSess
import { JoinRule } from "matrix-js-sdk/src/matrix";
import { Heading, Text } from "@vector-im/compound-web";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import type { IWidgetApiRequest } from "matrix-widget-api";
import {
@@ -234,7 +234,7 @@ export const GroupCallView: FC<Props> = ({
const [left, setLeft] = useState(false);
const [leaveError, setLeaveError] = useState<Error | undefined>(undefined);
const history = useHistory();
const navigate = useNavigate();
const onLeave = useCallback(
(leaveError?: Error): void => {
@@ -263,7 +263,7 @@ export const GroupCallView: FC<Props> = ({
!confineToRoom &&
!PosthogAnalytics.instance.isEnabled()
) {
history.push("/");
navigate("/");
}
})
.catch((e) => {
@@ -276,7 +276,7 @@ export const GroupCallView: FC<Props> = ({
isPasswordlessUser,
confineToRoom,
leaveSoundContext,
history,
navigate,
],
);

View File

@@ -10,7 +10,6 @@ import { useTranslation } from "react-i18next";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { Button } from "@vector-im/compound-web";
import classNames from "classnames";
import { useHistory } from "react-router-dom";
import { logger } from "matrix-js-sdk/src/logger";
import { usePreviewTracks } from "@livekit/components-react";
import {
@@ -20,11 +19,11 @@ import {
} from "livekit-client";
import { useObservable } from "observable-hooks";
import { map } from "rxjs";
import { useNavigate } from "react-router-dom";
import inCallStyles from "./InCallView.module.css";
import styles from "./LobbyView.module.css";
import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header";
import { useLocationNavigation } from "../useLocationNavigation";
import { type MatrixInfo, VideoPreview } from "./VideoPreview";
import { type MuteStates } from "./MuteStates";
import { InviteButton } from "../button/InviteButton";
@@ -73,7 +72,6 @@ export const LobbyView: FC<Props> = ({
waitingForInvite,
}) => {
const { t } = useTranslation();
useLocationNavigation();
const onAudioPress = useCallback(
() => muteStates.audio.setEnabled?.((e) => !e),
@@ -96,8 +94,8 @@ export const LobbyView: FC<Props> = ({
[setSettingsModalOpen],
);
const history = useHistory();
const onLeaveClick = useCallback(() => history.push("/"), [history]);
const navigate = useNavigate();
const onLeaveClick = useCallback(() => navigate("/"), [navigate]);
const recentsButtonInFooter = useMediaQuery("(max-height: 500px)");
const recentsButton = !confineToRoom && (

View File

@@ -1,36 +0,0 @@
/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { useEffect } from "react";
import { useHistory } from "react-router-dom";
export function useLocationNavigation(enabled = false): void {
const history = useHistory();
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
let unblock = undefined;
if (enabled) {
unblock = history.block((tx) => {
const url = new URL(tx.pathname, window.location.href);
url.search = tx.search;
url.hash = tx.hash;
window.location.href = url.href;
});
}
return (): void => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (unblock) {
unblock();
}
};
}, [history, enabled]);
}