Support using Element Call for voice calls in DMs (#30817)

* Add voiceOnly options.

* tweaks

* Nearly working demo

* Lots of minor fixes

* Better working version

* remove unused payload

* bits and pieces

* Cleanup based on new hints

* Simple refactor for skipLobby (and remove returnToLobby)

* Tidyup

* Remove unused tests

* Update tests for voice calls

* Add video room support.

* Add a test for video rooms

* tidy

* remove console log line

* lint and tests

* Bunch of fixes

* Fixes

* Use correct title

* make linter happier

* Update tests

* cleanup

* Drop only

* update snaps

* Document

* lint

* Update snapshots

* Remove duplicate test

* add brackets

* fix jest
This commit is contained in:
Will Hunt
2025-11-17 11:50:22 +00:00
committed by GitHub
parent 3d683ec5c6
commit f3a880f1c3
25 changed files with 365 additions and 112 deletions

View File

@@ -10,12 +10,10 @@ import React, { type FC } from "react";
import classNames from "classnames";
import { _t } from "../../../languageHandler";
import { type Call } from "../../../models/Call";
import { useParticipantCount } from "../../../hooks/useCall";
export enum LiveContentType {
Video,
// More coming soon
Voice,
}
interface Props {
@@ -33,6 +31,7 @@ export const LiveContentSummary: FC<Props> = ({ type, text, active, participantC
<span
className={classNames("mx_LiveContentSummary_text", {
mx_LiveContentSummary_text_video: type === LiveContentType.Video,
mx_LiveContentSummary_text_voice: type === LiveContentType.Voice,
mx_LiveContentSummary_text_active: active,
})}
>
@@ -51,16 +50,3 @@ export const LiveContentSummary: FC<Props> = ({ type, text, active, participantC
)}
</span>
);
interface LiveContentSummaryWithCallProps {
call: Call;
}
export const LiveContentSummaryWithCall: FC<LiveContentSummaryWithCallProps> = ({ call }) => (
<LiveContentSummary
type={LiveContentType.Video}
text={_t("common|video")}
active={false}
participantCount={useParticipantCount(call)}
/>
);

View File

@@ -12,6 +12,8 @@ import NotificationOffIcon from "@vector-im/compound-design-tokens/assets/web/ic
import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call-solid";
import EmailIcon from "@vector-im/compound-design-tokens/assets/web/icons/email-solid";
import { UnreadCounter, Unread } from "@vector-im/compound-web";
import VoiceCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/voice-call-solid";
import { CallType } from "matrix-js-sdk/src/webrtc/call";
import { Flex } from "@element-hq/web-shared-components";
import { type RoomNotificationState } from "../../../stores/notifications/RoomNotificationState";
@@ -24,9 +26,9 @@ interface NotificationDecorationProps extends HTMLProps<HTMLDivElement> {
*/
notificationState: RoomNotificationState;
/**
* Whether the room has a video call.
* Whether the room has a voice or video call.
*/
hasVideoCall: boolean;
callType?: CallType;
}
/**
@@ -34,7 +36,7 @@ interface NotificationDecorationProps extends HTMLProps<HTMLDivElement> {
*/
export function NotificationDecoration({
notificationState,
hasVideoCall,
callType,
...props
}: NotificationDecorationProps): JSX.Element | null {
// Listen to the notification state and update the component when it changes
@@ -58,7 +60,7 @@ export function NotificationDecoration({
muted: notificationState.muted,
}));
if (!hasAnyNotificationOrActivity && !muted && !hasVideoCall) return null;
if (!hasAnyNotificationOrActivity && !muted && !callType) return null;
return (
<Flex
@@ -69,7 +71,12 @@ export function NotificationDecoration({
data-testid="notification-decoration"
>
{isUnsentMessage && <ErrorIcon width="20px" height="20px" fill="var(--cpd-color-icon-critical-primary)" />}
{hasVideoCall && <VideoCallIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />}
{callType === CallType.Video && (
<VideoCallIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />
)}
{callType === CallType.Voice && (
<VoiceCallIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />
)}
{invited && <EmailIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />}
{isMention && <MentionIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />}
{(isMention || isNotification) && <UnreadCounter count={count || null} />}

View File

@@ -132,7 +132,7 @@ export const RoomListItemView = memo(function RoomListItemView({
<NotificationDecoration
notificationState={vm.notificationState}
aria-hidden={true}
hasVideoCall={vm.hasParticipantInCall}
callType={vm.callType}
/>
)}
</>