Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/call-auto-leave-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: minor
---

Add optional `callOptions.autoLeaveWhenOthersLeft` to client config so admins can opt out of Element Call's default auto-leave behavior
20 changes: 16 additions & 4 deletions src/app/hooks/useCallEmbed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useSetAtom } from 'jotai';
import * as Sentry from '@sentry/react';
import {
CallEmbed,
CallOptions,
ElementCallThemeKind,
ElementWidgetActions,
useClientWidgetApiEvent,
Expand All @@ -17,6 +18,7 @@ import { CallControlState } from '../plugins/call/CallControlState';
import { useCallMembersChange, useCallSession } from './useCall';
import { CallPreferences } from '../state/callPreferences';
import { createDebugLogger } from '../utils/debugLogger';
import { useClientConfig } from './useClientConfig';

const debugLog = createDebugLogger('useCallEmbed');

Expand Down Expand Up @@ -46,14 +48,15 @@ export const createCallEmbed = (
dm: boolean,
themeKind: ElementCallThemeKind,
container: HTMLElement,
pref?: CallPreferences
pref?: CallPreferences,
callOptions?: CallOptions
): CallEmbed => {
const rtcSession = mx.matrixRTC.getRoomSession(room);
const ongoing =
MatrixRTCSession.sessionMembershipsForRoom(room, rtcSession.sessionDescription).length > 0;

const intent = CallEmbed.getIntent(dm, ongoing, pref?.video);
const widget = CallEmbed.getWidget(mx, room, intent, themeKind);
const widget = CallEmbed.getWidget(mx, room, intent, themeKind, callOptions);
const controlState = pref && new CallControlState(pref.microphone, pref.video, pref.sound);

const embed = new CallEmbed(mx, room, widget, container, controlState);
Expand All @@ -66,6 +69,7 @@ export const useCallStart = (dm = false) => {
const theme = useTheme();
const setCallEmbed = useSetAtom(callEmbedAtom);
const callEmbedRef = useCallEmbedRef();
const clientConfig = useClientConfig();

const startCall = useCallback(
(room: Room, pref?: CallPreferences) => {
Expand All @@ -82,7 +86,15 @@ export const useCallStart = (dm = false) => {
try {
debugLog.info('call', 'Starting call', { roomId: room.roomId, dm });
Sentry.metrics.count('sable.call.start.attempt', 1, { attributes: { dm: String(dm) } });
const callEmbed = createCallEmbed(mx, room, dm, theme.kind, container, pref);
const callEmbed = createCallEmbed(
mx,
room,
dm,
theme.kind,
container,
pref,
clientConfig.callOptions
);
setCallEmbed(callEmbed);
} catch (err) {
debugLog.error('call', 'Call embed creation failed', {
Expand All @@ -95,7 +107,7 @@ export const useCallStart = (dm = false) => {
throw err;
}
},
[mx, dm, theme, setCallEmbed, callEmbedRef]
[mx, dm, theme, setCallEmbed, callEmbedRef, clientConfig.callOptions]
);

return startCall;
Expand Down
5 changes: 5 additions & 0 deletions src/app/hooks/useClientConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ export type HashRouterConfig = {
basename?: string;
};

export type CallOptionsConfig = {
autoLeaveWhenOthersLeft?: boolean;
};

export type ClientConfig = {
defaultHomeserver?: number;
homeserverList?: string[];
allowCustomHomeservers?: boolean;
elementCallUrl?: string;
callOptions?: CallOptionsConfig;

disableAccountSwitcher?: boolean;
hideUsernamePasswordFields?: boolean;
Expand Down
11 changes: 10 additions & 1 deletion src/app/plugins/call/CallEmbed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@

const debugLog = createDebugLogger('CallEmbed');

export type CallOptions = {
autoLeaveWhenOthersLeft?: boolean;
};

export class CallEmbed {
private mx: MatrixClient;

Expand Down Expand Up @@ -68,7 +72,8 @@
mx: MatrixClient,
room: Room,
intent: ElementCallIntent,
themeKind: ElementCallThemeKind
themeKind: ElementCallThemeKind,
callOptions?: CallOptions
): Widget {
const userId = mx.getSafeUserId();
const deviceId = mx.getDeviceId() ?? '';
Expand All @@ -92,6 +97,10 @@
theme: themeKind,
});

if (callOptions?.autoLeaveWhenOthersLeft !== undefined) {
params.set('autoLeave', callOptions.autoLeaveWhenOthersLeft.toString());
}

const widgetUrl = new URL(
`${trimTrailingSlash(import.meta.env.BASE_URL)}/public/element-call/index.html`,
window.location.origin
Expand Down Expand Up @@ -314,7 +323,7 @@
if (this.call === null) return;
const raw = ev.getEffectiveEvent();
this.call.feedStateUpdate(raw as IRoomEvent).catch((e) => {
console.error('Error sending state update to widget: ', e);

Check warning on line 326 in src/app/plugins/call/CallEmbed.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected console statement
});
}

Expand Down Expand Up @@ -420,7 +429,7 @@
} else {
const raw = ev.getEffectiveEvent();
this.call.feedEvent(raw as IRoomEvent).catch((e) => {
console.error('Error sending event to widget: ', e);

Check warning on line 432 in src/app/plugins/call/CallEmbed.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected console statement
});
}
}
Expand Down
Loading