Show correct call icon for joining a call. (#31489)
* Show correct call icon in header. * fix import * Simply useRoomCall output * Add tests and a label * update snap * update test
This commit is contained in:
@@ -21,6 +21,7 @@ import PublicIcon from "@vector-im/compound-design-tokens/assets/web/icons/publi
|
|||||||
import { JoinRule, type Room } from "matrix-js-sdk/src/matrix";
|
import { JoinRule, type Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { type ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
|
import { type ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
|
||||||
import { Flex, Box } from "@element-hq/web-shared-components";
|
import { Flex, Box } from "@element-hq/web-shared-components";
|
||||||
|
import { CallType } from "matrix-js-sdk/src/webrtc/call";
|
||||||
|
|
||||||
import { useRoomName } from "../../../../hooks/useRoomName.ts";
|
import { useRoomName } from "../../../../hooks/useRoomName.ts";
|
||||||
import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases.ts";
|
import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases.ts";
|
||||||
@@ -73,7 +74,7 @@ function RoomHeaderButtons({
|
|||||||
toggleCallMaximized: toggleCall,
|
toggleCallMaximized: toggleCall,
|
||||||
isViewingCall,
|
isViewingCall,
|
||||||
isConnectedToCall,
|
isConnectedToCall,
|
||||||
hasActiveCallSession,
|
activeCallSessionType,
|
||||||
callOptions,
|
callOptions,
|
||||||
showVoiceCallButton,
|
showVoiceCallButton,
|
||||||
showVideoCallButton,
|
showVideoCallButton,
|
||||||
@@ -105,15 +106,26 @@ function RoomHeaderButtons({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const joinCallButton = (
|
const joinCallButton = (
|
||||||
<Tooltip label={videoCallDisabledReason ?? _t("voip|video_call")}>
|
<Tooltip
|
||||||
|
label={
|
||||||
|
videoCallDisabledReason ??
|
||||||
|
(activeCallSessionType === CallType.Voice ? _t("voip|voice_call") : _t("voip|video_call"))
|
||||||
|
}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={videoClick}
|
onClick={videoClick}
|
||||||
Icon={VideoCallIcon}
|
// If we know this is a voice session, show the voice call. All other kinds of call are video calls.
|
||||||
|
Icon={activeCallSessionType === CallType.Voice ? VoiceCallIcon : VideoCallIcon}
|
||||||
className="mx_RoomHeader_join_button"
|
className="mx_RoomHeader_join_button"
|
||||||
disabled={!!videoCallDisabledReason}
|
disabled={!!videoCallDisabledReason}
|
||||||
color="primary"
|
color="primary"
|
||||||
aria-label={videoCallDisabledReason ?? _t("action|join")}
|
aria-label={
|
||||||
|
videoCallDisabledReason ??
|
||||||
|
(activeCallSessionType === CallType.Voice
|
||||||
|
? _t("room|header|join_voice_call")
|
||||||
|
: _t("room|header|join_video_call"))
|
||||||
|
}
|
||||||
data-testId="join-call-button"
|
data-testId="join-call-button"
|
||||||
>
|
>
|
||||||
{_t("action|join")}
|
{_t("action|join")}
|
||||||
@@ -303,7 +315,7 @@ function RoomHeaderButtons({
|
|||||||
|
|
||||||
{isViewingCall && <CallGuestLinkButton room={room} />}
|
{isViewingCall && <CallGuestLinkButton room={room} />}
|
||||||
|
|
||||||
{hasActiveCallSession && !isConnectedToCall && !isViewingCall ? (
|
{activeCallSessionType && !isConnectedToCall && !isViewingCall ? (
|
||||||
joinCallButton
|
joinCallButton
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ export const useRoomCall = (
|
|||||||
toggleCallMaximized: () => void;
|
toggleCallMaximized: () => void;
|
||||||
isViewingCall: boolean;
|
isViewingCall: boolean;
|
||||||
isConnectedToCall: boolean;
|
isConnectedToCall: boolean;
|
||||||
hasActiveCallSession: boolean;
|
/**
|
||||||
|
* The type of call in progress, or `null` if no call is ongoing.
|
||||||
|
*/
|
||||||
|
activeCallSessionType: CallType | null;
|
||||||
callOptions: PlatformCallType[];
|
callOptions: PlatformCallType[];
|
||||||
showVideoCallButton: boolean;
|
showVideoCallButton: boolean;
|
||||||
showVoiceCallButton: boolean;
|
showVoiceCallButton: boolean;
|
||||||
@@ -123,13 +126,20 @@ export const useRoomCall = (
|
|||||||
const groupCall = useCall(room.roomId);
|
const groupCall = useCall(room.roomId);
|
||||||
const isConnectedToCall = useConnectionState(groupCall) === ConnectionState.Connected;
|
const isConnectedToCall = useConnectionState(groupCall) === ConnectionState.Connected;
|
||||||
const hasGroupCall = groupCall !== null;
|
const hasGroupCall = groupCall !== null;
|
||||||
const hasActiveCallSession = useParticipantCount(groupCall) > 0;
|
|
||||||
const isViewingCall = useEventEmitterState(
|
const isViewingCall = useEventEmitterState(
|
||||||
roomViewStore,
|
roomViewStore,
|
||||||
UPDATE_EVENT,
|
UPDATE_EVENT,
|
||||||
() => roomViewStore.isViewingCall() || isVideoRoom(room),
|
() => roomViewStore.isViewingCall() || isVideoRoom(room),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const participantCount = useParticipantCount(groupCall);
|
||||||
|
const activeCallSessionType = useMemo(() => {
|
||||||
|
if (!groupCall || participantCount === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return groupCall.callType;
|
||||||
|
}, [participantCount, groupCall]);
|
||||||
|
|
||||||
// room
|
// room
|
||||||
const memberCount = useRoomMemberCount(room);
|
const memberCount = useRoomMemberCount(room);
|
||||||
|
|
||||||
@@ -307,7 +317,7 @@ export const useRoomCall = (
|
|||||||
toggleCallMaximized: toggleCallMaximized,
|
toggleCallMaximized: toggleCallMaximized,
|
||||||
isViewingCall: isViewingCall,
|
isViewingCall: isViewingCall,
|
||||||
isConnectedToCall: isConnectedToCall,
|
isConnectedToCall: isConnectedToCall,
|
||||||
hasActiveCallSession: hasActiveCallSession,
|
activeCallSessionType: activeCallSessionType,
|
||||||
callOptions,
|
callOptions,
|
||||||
showVoiceCallButton: !hideVoiceCallButton,
|
showVoiceCallButton: !hideVoiceCallButton,
|
||||||
showVideoCallButton: !hideVideoCallButton,
|
showVideoCallButton: !hideVideoCallButton,
|
||||||
|
|||||||
@@ -2029,6 +2029,8 @@
|
|||||||
"forget_room": "Forget this room",
|
"forget_room": "Forget this room",
|
||||||
"forget_space": "Forget this space",
|
"forget_space": "Forget this space",
|
||||||
"header": {
|
"header": {
|
||||||
|
"join_video_call": "Join video call",
|
||||||
|
"join_voice_call": "Join voice call",
|
||||||
"n_people_asking_to_join": {
|
"n_people_asking_to_join": {
|
||||||
"one": "Asking to join",
|
"one": "Asking to join",
|
||||||
"other": "%(count)s people asking to join"
|
"other": "%(count)s people asking to join"
|
||||||
|
|||||||
@@ -582,12 +582,21 @@ describe("RoomHeader", () => {
|
|||||||
expect(videoButton).toHaveAttribute("aria-disabled", "true");
|
expect(videoButton).toHaveAttribute("aria-disabled", "true");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("join button is shown if there is an ongoing call", async () => {
|
it("join video call button is shown if there is an ongoing call", async () => {
|
||||||
mockRoomMembers(room, 3);
|
mockRoomMembers(room, 3);
|
||||||
// Mock CallStore to return a call with 3 participants
|
// Mock CallStore to return a call with 3 participants
|
||||||
jest.spyOn(CallStore.instance, "getCall").mockReturnValue(createMockCall(ROOM_ID, 3));
|
jest.spyOn(CallStore.instance, "getCall").mockReturnValue(createMockCall(ROOM_ID, 3));
|
||||||
render(<RoomHeader room={room} />, getWrapper());
|
render(<RoomHeader room={room} />, getWrapper());
|
||||||
const joinButton = getByLabelText(document.body, "Join");
|
const joinButton = getByLabelText(document.body, "Join video call");
|
||||||
|
expect(joinButton).not.toHaveAttribute("aria-disabled", "true");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("join voice call button is shown if there is an ongoing call", async () => {
|
||||||
|
mockRoomMembers(room, 3);
|
||||||
|
// Mock CallStore to return a call with 3 participants
|
||||||
|
jest.spyOn(CallStore.instance, "getCall").mockReturnValue(createMockCall(ROOM_ID, 3, CallType.Voice));
|
||||||
|
render(<RoomHeader room={room} />, getWrapper());
|
||||||
|
const joinButton = getByLabelText(document.body, "Join voice call");
|
||||||
expect(joinButton).not.toHaveAttribute("aria-disabled", "true");
|
expect(joinButton).not.toHaveAttribute("aria-disabled", "true");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -859,7 +868,11 @@ describe("RoomHeader", () => {
|
|||||||
/**
|
/**
|
||||||
* Creates a mock Call object with stable participants to prevent React dependency errors
|
* Creates a mock Call object with stable participants to prevent React dependency errors
|
||||||
*/
|
*/
|
||||||
function createMockCall(roomId: string = "!1:example.org", participantCount: number = 0): Call {
|
function createMockCall(
|
||||||
|
roomId: string = "!1:example.org",
|
||||||
|
participantCount: number = 0,
|
||||||
|
callType: CallType = CallType.Video,
|
||||||
|
): Call {
|
||||||
const participants = new Map();
|
const participants = new Map();
|
||||||
|
|
||||||
// Create mock participants with devices
|
// Create mock participants with devices
|
||||||
@@ -878,6 +891,7 @@ function createMockCall(roomId: string = "!1:example.org", participantCount: num
|
|||||||
participants,
|
participants,
|
||||||
widget: { id: "test-widget" },
|
widget: { id: "test-widget" },
|
||||||
connectionState: "disconnected",
|
connectionState: "disconnected",
|
||||||
|
callType,
|
||||||
on: jest.fn(),
|
on: jest.fn(),
|
||||||
off: jest.fn(),
|
off: jest.fn(),
|
||||||
emit: jest.fn(),
|
emit: jest.fn(),
|
||||||
|
|||||||
+4
-4
@@ -56,7 +56,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
|||||||
style="--cpd-icon-button-size: 100%;"
|
style="--cpd-icon-button-size: 100%;"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-labelledby="_r_17e_"
|
aria-labelledby="_r_18c_"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -83,7 +83,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
|||||||
style="--cpd-icon-button-size: 100%;"
|
style="--cpd-icon-button-size: 100%;"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-labelledby="_r_17j_"
|
aria-labelledby="_r_18h_"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -98,7 +98,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby="_r_17o_"
|
aria-labelledby="_r_18m_"
|
||||||
class="_icon-button_1pz9o_8"
|
class="_icon-button_1pz9o_8"
|
||||||
data-kind="primary"
|
data-kind="primary"
|
||||||
role="button"
|
role="button"
|
||||||
@@ -125,7 +125,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Room info"
|
aria-label="Room info"
|
||||||
aria-labelledby="_r_17t_"
|
aria-labelledby="_r_18r_"
|
||||||
class="_icon-button_1pz9o_8"
|
class="_icon-button_1pz9o_8"
|
||||||
data-kind="primary"
|
data-kind="primary"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
Reference in New Issue
Block a user