Merge branch 'livekit' into toger5/test-with-new-membershipmanager

This commit is contained in:
Timo
2025-03-07 11:21:30 +01:00
9 changed files with 79 additions and 33 deletions

17
.github/workflows/blocked.yaml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: Prevent blocked
on:
pull_request:
types: [opened, labeled, unlabeled]
jobs:
prevent-blocked:
name: Prevent blocked
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- name: Add notice
uses: actions/github-script@v7
if: contains(github.event.pull_request.labels.*.name, 'X-Blocked')
with:
script: |
core.setFailed("PR has been labeled with X-Blocked; it cannot be merged.");

View File

@@ -8,26 +8,26 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
/>
<title><%- title %></title>
<title><%- brand %></title>
<script>
window.global = window;
</script>
<!-- Open graph meta tags -->
<meta property="og:title" content="<%- title %>" />
<meta property="og:title" content="<%- brand %>" />
<meta
property="og:description"
content="You're invited to join a call on Element Call"
content="You're invited to join a call on <%- brand %>"
/>
<meta property="og:type" content="website" />
<meta property="og:image" content="favicon.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="<%- title %>" />
<meta name="twitter:title" content="<%- brand %>" />
<meta
name="twitter:description"
content="You're invited to join a call on Element Call"
content="You're invited to join a call on <%- brand %>"
/>
<meta name="twitter:image" content="favicon.png" />
</head>

View File

@@ -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", () => {

View File

@@ -264,7 +264,9 @@ export const getUrlParams = (
"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,

View File

@@ -539,9 +539,7 @@ export const GroupCallView: FC<Props> = ({
}
} else if (left && widget !== null) {
// Left in widget mode:
if (!returnToLobby) {
body = null;
}
body = returnToLobby ? lobbyView : null;
} else if (preload || skipLobby) {
body = null;
} else {

View File

@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
*/
import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
import { expect, test, vi } from "vitest";
import { expect, onTestFinished, test, vi } from "vitest";
import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
import EventEmitter from "events";
@@ -15,11 +15,17 @@ import { mockConfig } from "./utils/test";
import { ElementWidgetActions, widget } from "./widget";
import { ErrorCode } from "./utils/errors.ts";
const getUrlParams = vi.hoisted(() => vi.fn(() => ({})));
vi.mock("./UrlParams", () => ({ getUrlParams }));
const actualWidget = await vi.hoisted(async () => vi.importActual("./widget"));
vi.mock("./widget", () => ({
...actualWidget,
widget: {
api: { transport: { send: vi.fn(), reply: vi.fn(), stop: vi.fn() } },
api: {
setAlwaysOnScreen: (): void => {},
transport: { send: vi.fn(), reply: vi.fn(), stop: vi.fn() },
},
lazyActions: new EventEmitter(),
},
}));
@@ -110,34 +116,45 @@ test("It joins the correct Session", async () => {
);
});
test("leaveRTCSession closes the widget on a normal hangup", async () => {
async function testLeaveRTCSession(
cause: "user" | "error",
expectClose: boolean,
): Promise<void> {
vi.clearAllMocks();
const session = { leaveRoomSession: vi.fn() } as unknown as MatrixRTCSession;
await leaveRTCSession(session, "user");
await leaveRTCSession(session, cause);
expect(session.leaveRoomSession).toHaveBeenCalled();
expect(widget!.api.transport.send).toHaveBeenCalledWith(
ElementWidgetActions.HangupCall,
expect.anything(),
);
expect(widget!.api.transport.send).toHaveBeenCalledWith(
ElementWidgetActions.Close,
expect.anything(),
);
if (expectClose) {
expect(widget!.api.transport.send).toHaveBeenCalledWith(
ElementWidgetActions.Close,
expect.anything(),
);
expect(widget!.api.transport.stop).toHaveBeenCalled();
} else {
expect(widget!.api.transport.send).not.toHaveBeenCalledWith(
ElementWidgetActions.Close,
expect.anything(),
);
expect(widget!.api.transport.stop).not.toHaveBeenCalled();
}
}
test("leaveRTCSession closes the widget on a normal hangup", async () => {
await testLeaveRTCSession("user", true);
});
test("leaveRTCSession doesn't close the widget on a fatal error", async () => {
vi.clearAllMocks();
const session = { leaveRoomSession: vi.fn() } as unknown as MatrixRTCSession;
await leaveRTCSession(session, "error");
expect(session.leaveRoomSession).toHaveBeenCalled();
expect(widget!.api.transport.send).toHaveBeenCalledWith(
ElementWidgetActions.HangupCall,
expect.anything(),
);
expect(widget!.api.transport.send).not.toHaveBeenCalledWith(
ElementWidgetActions.Close,
expect.anything(),
);
await testLeaveRTCSession("error", false);
});
test("leaveRTCSession doesn't close the widget when returning to lobby", async () => {
getUrlParams.mockReturnValue({ returnToLobby: true });
onTestFinished(() => void getUrlParams.mockReset());
await testLeaveRTCSession("user", false);
});
test("It fails with configuration error if no live kit url config is set in fallback", async () => {

View File

@@ -19,6 +19,7 @@ import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
import { Config } from "./config/Config";
import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget";
import { MatrixRTCFocusMissingError } from "./utils/errors.ts";
import { getUrlParams } from "./UrlParams.ts";
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
@@ -126,6 +127,13 @@ export async function enterRTCSession(
makeKeyDelay: matrixRtcSessionConfig?.key_rotation_on_leave_delay,
},
);
if (widget) {
try {
await widget.api.transport.send(ElementWidgetActions.JoinCall, {});
} catch (e) {
logger.error("Failed to send join action", e);
}
}
}
const widgetPostHangupProcedure = async (
@@ -151,7 +159,7 @@ const widgetPostHangupProcedure = async (
}
// On a normal user hangup we can shut down and close the widget. But if an
// error occurs we should keep the widget open until the user reads it.
if (cause === "user") {
if (cause === "user" && !getUrlParams().returnToLobby) {
try {
await widget.api.transport.send(ElementWidgetActions.Close, {});
} catch (e) {

View File

@@ -496,6 +496,10 @@ export class CallViewModel extends ViewModel {
}
return displaynameMap;
}),
// It turns out that doing the disambiguation above is rather expensive on Safari (10x slower
// than on Chrome/Firefox). This means it is important that we share() the result so that we
// don't do this work more times than we need to. This is achieve through the state() operator:
this.scope.state(),
);
/**

View File

@@ -29,7 +29,7 @@ export default defineConfig(({ mode }) => {
}),
htmlTemplate.default({
data: {
title: env.VITE_PRODUCT_NAME || "Element Call",
brand: env.VITE_PRODUCT_NAME || "Element Call",
},
}),