Merge remote-tracking branch 'origin/livekit' into toger5/wait-for-pickup-overlay-sound+hs/sounds

This commit is contained in:
Half-Shot
2025-09-15 10:36:30 +01:00
5 changed files with 33 additions and 10 deletions

View File

@@ -132,9 +132,10 @@ test("ConnectionLostError: Action handling should reset error state", async () =
const WrapComponent = (): ReactNode => {
const [failState, setFailState] = useState(true);
const reconnectCallback = useCallback(
(action: CallErrorRecoveryAction) => {
async (action: CallErrorRecoveryAction) => {
reconnectCallbackSpy(action);
setFailState(false);
return Promise.resolve();
},
[setFailState],
);

View File

@@ -36,7 +36,9 @@ import { type WidgetHelpers } from "../widget.ts";
export type CallErrorRecoveryAction = "reconnect"; // | "retry" ;
export type RecoveryActionHandler = (action: CallErrorRecoveryAction) => void;
export type RecoveryActionHandler = (
action: CallErrorRecoveryAction,
) => Promise<void>;
interface ErrorPageProps {
error: ElementCallError;
@@ -71,7 +73,7 @@ const ErrorPage: FC<ErrorPageProps> = ({
if (error instanceof ConnectionLostError) {
actions.push({
label: t("call_ended_view.reconnect_button"),
onClick: () => recoveryActionHandler("reconnect"),
onClick: () => void recoveryActionHandler("reconnect"),
});
}
@@ -131,9 +133,9 @@ export const GroupCallErrorBoundary = ({
widget={widget ?? null}
error={callError}
resetError={resetError}
recoveryActionHandler={(action: CallErrorRecoveryAction) => {
recoveryActionHandler={async (action: CallErrorRecoveryAction) => {
await recoveryActionHandler(action);
resetError();
recoveryActionHandler(action);
}}
/>
);

View File

@@ -14,9 +14,12 @@ import {
vi,
vitest,
} from "vitest";
import { render, waitFor, screen } from "@testing-library/react";
import { render, waitFor, screen, act } from "@testing-library/react";
import { type MatrixClient, JoinRule, type RoomState } from "matrix-js-sdk";
import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc";
import {
MatrixRTCSessionEvent,
type MatrixRTCSession,
} from "matrix-js-sdk/lib/matrixrtc";
import { BrowserRouter } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import { type RelationsContainer } from "matrix-js-sdk/lib/models/relations-container";
@@ -261,3 +264,19 @@ test("GroupCallView shows errors that occur during joining", async () => {
await user.click(screen.getByRole("button", { name: "Join call" }));
screen.getByText("Call is not supported");
});
test("user can reconnect after a membership manager error", async () => {
const user = userEvent.setup();
const { rtcSession } = createGroupCallView(null, true);
await act(() =>
rtcSession.emit(MatrixRTCSessionEvent.MembershipManagerError, undefined),
);
// XXX: Wrapping the following click in act() shouldn't be necessary (the
// async state update should be processed automatically by the waitFor call),
// and yet here we are.
await act(async () =>
user.click(screen.getByRole("button", { name: "Reconnect" })),
);
// In-call controls should be visible again
await waitFor(() => screen.getByRole("button", { name: "Leave" }));
});

View File

@@ -497,10 +497,11 @@ export const GroupCallView: FC<Props> = ({
return (
<GroupCallErrorBoundary
widget={widget}
recoveryActionHandler={(action) => {
recoveryActionHandler={async (action) => {
setExternalError(null);
if (action == "reconnect") {
setLeft(false);
enterRTCSessionOrError(rtcSession).catch((e) => {
await enterRTCSessionOrError(rtcSession).catch((e) => {
logger.error("Error re-entering RTC session", e);
});
}

View File

@@ -30,7 +30,7 @@
opacity: 0;
}
.pulse::before .text {
.text {
color: var(--cpd-color-text-on-solid-primary);
}