Let the shell say where home is, and drop the router from the call

The call reached for react-router in five places to send the user
"home": on leaving without a lobby, from the lobby's recents link, from
the post-call screen, from the error page's return button and from the
header logo. Home is the standalone app's home page; the call has no
idea where that is, and a component has no such place at all — its host
decides what follows a call. Yet the component had to mount a
MemoryRouter just so those hooks would not throw.

`useLeaveToHome` is the way home as the shell supplies it: the app
provides `navigate("/")` from inside its router, the component provides
nothing, and everything that used to link to "/" now either calls it or,
when there is none, offers no way out. The logo becomes a plain logo,
the recents and "not now" links disappear, the error page's button does
too. `ClientProvider`'s logout goes the same way. The component no
longer renders a router.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Timo K.
2026-09-08 15:39:00 +02:00
co-authored by Claude Fable 5.1
parent f5a94ce702
commit b7285064fd
15 changed files with 299 additions and 182 deletions
+10 -12
View File
@@ -25,7 +25,6 @@ import {
Track,
} from "livekit-client";
import { useObservableEagerState } from "observable-hooks";
import { useNavigate } from "react-router-dom";
import inCallStyles from "./InCallView.module.css";
import styles from "./LobbyView.module.css";
@@ -36,7 +35,8 @@ import { InviteButton } from "../button/InviteButton";
import { SettingsModal, defaultSettingsTab } from "../settings/SettingsModal";
import { useRootSizeMatches } from "../useRootSize";
import { E2eeType } from "../e2ee/e2eeType";
import { Link } from "../button/Link";
import { LeaveToHomeLink } from "../button/LeaveToHomeLink";
import { useLeaveToHome } from "../LeaveToHomeContext";
import { useMediaDevices } from "../MediaDevicesContext";
import { ObservableScope } from "../state/ObservableScope";
import { useInitial } from "../useInitial";
@@ -109,21 +109,19 @@ export const LobbyView: FC<Props> = ({
[setSettingsModalOpen],
);
const navigate = useNavigate();
const onLeaveClick = useCallback(() => {
navigate("/")?.catch((error) => {
logger.error("Failed to navigate to /", error);
});
}, [navigate]);
const hangup = confineToRoom ? undefined : onLeaveClick;
// Leaving the lobby means going back to wherever the user came from, if
// there is such a place
const leaveToHome = useLeaveToHome();
const hangup =
confineToRoom || leaveToHome === null ? undefined : leaveToHome;
const recentsButtonInFooter = useRootSizeMatches(
({ height }) => height <= 500,
);
const recentsButton = !confineToRoom && (
<Link className={styles.recents} to="/">
<LeaveToHomeLink className={styles.recents}>
{t("lobby.leave_button")}
</Link>
</LeaveToHomeLink>
);
const devices = useMediaDevices();
@@ -209,7 +207,7 @@ export const LobbyView: FC<Props> = ({
return (): void => {
footerScope.end();
};
}, [devices, hangup, hideHeader, muteStates, onLeaveClick, openSettings]);
}, [devices, hangup, hideHeader, muteStates, openSettings]);
// TODO: Unify this component with InCallView, so we can get slick joining
// animations and don't have to feel bad about reusing its CSS