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:
@@ -7,6 +7,7 @@
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { type Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { CallType } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import dispatcher from "../../../dispatcher/dispatcher";
|
||||
import type { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
@@ -19,7 +20,7 @@ import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
import { useEventEmitter, useEventEmitterState, useTypedEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import { DefaultTagID } from "../../../stores/room-list/models";
|
||||
import { useCall, useConnectionState, useParticipantCount } from "../../../hooks/useCall";
|
||||
import { type ConnectionState } from "../../../models/Call";
|
||||
import { CallEvent, type ConnectionState } from "../../../models/Call";
|
||||
import { NotificationStateEvents } from "../../../stores/notifications/NotificationState";
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
||||
@@ -67,6 +68,10 @@ export interface RoomListItemViewState {
|
||||
* Whether there are participants in the call.
|
||||
*/
|
||||
hasParticipantInCall: boolean;
|
||||
/**
|
||||
* Whether the call is a voice or video call.
|
||||
*/
|
||||
callType: CallType | undefined;
|
||||
/**
|
||||
* Pre-rendered and translated preview for the latest message in the room, or undefined
|
||||
* if no preview should be shown.
|
||||
@@ -123,10 +128,10 @@ export function useRoomListItemViewModel(room: Room): RoomListItemViewState {
|
||||
// EC video call or video room
|
||||
const call = useCall(room.roomId);
|
||||
const connectionState = useConnectionState(call);
|
||||
const hasParticipantInCall = useParticipantCount(call) > 0;
|
||||
const participantCount = useParticipantCount(call);
|
||||
const callConnectionState = call ? connectionState : null;
|
||||
|
||||
const showNotificationDecoration = hasVisibleNotification || hasParticipantInCall;
|
||||
const showNotificationDecoration = hasVisibleNotification || participantCount > 0;
|
||||
|
||||
// Actions
|
||||
|
||||
@@ -138,6 +143,9 @@ export function useRoomListItemViewModel(room: Room): RoomListItemViewState {
|
||||
});
|
||||
}, [room]);
|
||||
|
||||
const [callType, setCallType] = useState<CallType>(CallType.Video);
|
||||
useTypedEventEmitter(call ?? undefined, CallEvent.CallTypeChanged, setCallType);
|
||||
|
||||
return {
|
||||
name,
|
||||
notificationState,
|
||||
@@ -148,9 +156,10 @@ export function useRoomListItemViewModel(room: Room): RoomListItemViewState {
|
||||
isBold,
|
||||
isVideoRoom,
|
||||
callConnectionState,
|
||||
hasParticipantInCall,
|
||||
hasParticipantInCall: participantCount > 0,
|
||||
messagePreview,
|
||||
showNotificationDecoration,
|
||||
callType: call ? callType : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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} />}
|
||||
|
||||
@@ -132,7 +132,7 @@ export const RoomListItemView = memo(function RoomListItemView({
|
||||
<NotificationDecoration
|
||||
notificationState={vm.notificationState}
|
||||
aria-hidden={true}
|
||||
hasVideoCall={vm.hasParticipantInCall}
|
||||
callType={vm.callType}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user