Hide voice call button when redundant (#12639)

* Hide voice call button when redundant

i.e. when it'd do the same thing as the video call button like in non-dm rooms

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2024-06-19 16:49:09 +01:00 committed by GitHub
parent 04e1d7f6c0
commit 76844f5973
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 30 deletions

View file

@ -82,6 +82,7 @@ export default function RoomHeader({
isConnectedToCall, isConnectedToCall,
hasActiveCallSession, hasActiveCallSession,
callOptions, callOptions,
showVoiceCallButton,
} = useRoomCall(room); } = useRoomCall(room);
const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); const groupCallsEnabled = useFeatureEnabled("feature_group_calls");
@ -199,20 +200,25 @@ export default function RoomHeader({
)} )}
</> </>
); );
const voiceCallButton = (
<Tooltip label={voiceCallDisabledReason ?? _t("voip|voice_call")}> let voiceCallButton: JSX.Element | undefined;
<IconButton if (showVoiceCallButton) {
// We need both: isViewingCall and isConnectedToCall voiceCallButton = (
// - in the Lobby we are viewing a call but are not connected to it. <Tooltip label={voiceCallDisabledReason ?? _t("voip|voice_call")}>
// - in pip view we are connected to the call but not viewing it. <IconButton
disabled={!!voiceCallDisabledReason || isViewingCall || isConnectedToCall} // We need both: isViewingCall and isConnectedToCall
aria-label={voiceCallDisabledReason ?? _t("voip|voice_call")} // - in the Lobby we are viewing a call but are not connected to it.
onClick={(ev) => voiceCallClick(ev, callOptions[0])} // - in pip view we are connected to the call but not viewing it.
> disabled={!!voiceCallDisabledReason || isViewingCall || isConnectedToCall}
<VoiceCallIcon /> aria-label={voiceCallDisabledReason ?? _t("voip|voice_call")}
</IconButton> onClick={(ev) => voiceCallClick(ev, callOptions[0])}
</Tooltip> >
); <VoiceCallIcon />
</IconButton>
</Tooltip>
);
}
const closeLobbyButton = ( const closeLobbyButton = (
<Tooltip label={_t("voip|close_lobby")}> <Tooltip label={_t("voip|close_lobby")}>
<IconButton onClick={toggleCall} aria-label={_t("voip|close_lobby")}> <IconButton onClick={toggleCall} aria-label={_t("voip|close_lobby")}>

View file

@ -31,7 +31,7 @@ import { placeCall } from "../../utils/room/placeCall";
import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore"; import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore";
import { useRoomState } from "../useRoomState"; import { useRoomState } from "../useRoomState";
import { _t } from "../../languageHandler"; import { _t } from "../../languageHandler";
import { isManagedHybridWidget } from "../../widgets/ManagedHybrid"; import { isManagedHybridWidget, isManagedHybridWidgetEnabled } from "../../widgets/ManagedHybrid";
import { IApp } from "../../stores/WidgetStore"; import { IApp } from "../../stores/WidgetStore";
import { SdkContextClass } from "../../contexts/SDKContext"; import { SdkContextClass } from "../../contexts/SDKContext";
import { UPDATE_EVENT } from "../../stores/AsyncStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore";
@ -83,6 +83,7 @@ export const useRoomCall = (
isConnectedToCall: boolean; isConnectedToCall: boolean;
hasActiveCallSession: boolean; hasActiveCallSession: boolean;
callOptions: PlatformCallType[]; callOptions: PlatformCallType[];
showVoiceCallButton: boolean;
} => { } => {
// settings // settings
const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); const groupCallsEnabled = useFeatureEnabled("feature_group_calls");
@ -124,7 +125,7 @@ export const useRoomCall = (
// The options provided to the RoomHeader. // The options provided to the RoomHeader.
// If there are multiple options, the user will be prompted to choose. // If there are multiple options, the user will be prompted to choose.
const callOptions = useMemo((): PlatformCallType[] => { const callOptions = useMemo((): PlatformCallType[] => {
const options = []; const options: PlatformCallType[] = [];
if (memberCount <= 2) { if (memberCount <= 2) {
options.push(PlatformCallType.LegacyCall); options.push(PlatformCallType.LegacyCall);
} else if (mayEditWidgets || hasJitsiWidget) { } else if (mayEditWidgets || hasJitsiWidget) {
@ -266,6 +267,10 @@ export const useRoomCall = (
}); });
}, [isViewingCall, room.roomId]); }, [isViewingCall, room.roomId]);
// We hide the voice call button if it'd have the same effect as the video call button
const hideVoiceCallButton =
isManagedHybridWidgetEnabled(room.roomId) || !callOptions.includes(PlatformCallType.LegacyCall);
/** /**
* We've gone through all the steps * We've gone through all the steps
*/ */
@ -279,5 +284,6 @@ export const useRoomCall = (
isConnectedToCall: isConnectedToCall, isConnectedToCall: isConnectedToCall,
hasActiveCallSession: hasActiveCallSession, hasActiveCallSession: hasActiveCallSession,
callOptions, callOptions,
showVoiceCallButton: !hideVoiceCallButton,
}; };
}; };

View file

@ -34,6 +34,7 @@ import {
getByRole, getByRole,
getByText, getByText,
queryAllByLabelText, queryAllByLabelText,
queryByLabelText,
render, render,
RenderOptions, RenderOptions,
screen, screen,
@ -232,6 +233,28 @@ describe("RoomHeader", () => {
expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.NotificationPanel }); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.NotificationPanel });
}); });
it("should show both call buttons in rooms smaller than 3 members", async () => {
mockRoomMembers(room, 2);
const { container } = render(<RoomHeader room={room} />, getWrapper());
expect(getByLabelText(container, "Video call")).toBeInTheDocument();
expect(getByLabelText(container, "Voice call")).toBeInTheDocument();
});
it("should not show voice call button in managed hybrid environments", async () => {
mockRoomMembers(room, 2);
jest.spyOn(SdkConfig, "get").mockReturnValue({ widget_build_url: "https://widget.build.url" });
const { container } = render(<RoomHeader room={room} />, getWrapper());
expect(getByLabelText(container, "Video call")).toBeInTheDocument();
expect(queryByLabelText(container, "Voice call")).not.toBeInTheDocument();
});
it("should not show voice call button in rooms larger than 2 members", async () => {
mockRoomMembers(room, 3);
const { container } = render(<RoomHeader room={room} />, getWrapper());
expect(getByLabelText(container, "Video call")).toBeInTheDocument();
expect(queryByLabelText(container, "Voice call")).not.toBeInTheDocument();
});
describe("groups call disabled", () => { describe("groups call disabled", () => {
it("you can't call if you're alone", () => { it("you can't call if you're alone", () => {
mockRoomMembers(room, 1); mockRoomMembers(room, 1);
@ -270,12 +293,11 @@ describe("RoomHeader", () => {
} }
}); });
it("can calls in large rooms if able to edit widgets", () => { it("can call in large rooms if able to edit widgets", () => {
mockRoomMembers(room, 10); mockRoomMembers(room, 10);
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true); jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
const { container } = render(<RoomHeader room={room} />, getWrapper()); const { container } = render(<RoomHeader room={room} />, getWrapper());
expect(getByLabelText(container, "Voice call")).not.toHaveAttribute("aria-disabled", "true");
expect(getByLabelText(container, "Video call")).not.toHaveAttribute("aria-disabled", "true"); expect(getByLabelText(container, "Video call")).not.toHaveAttribute("aria-disabled", "true");
}); });
@ -283,9 +305,6 @@ describe("RoomHeader", () => {
mockRoomMembers(room, 10); mockRoomMembers(room, 10);
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(false); jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(false);
const { container } = render(<RoomHeader room={room} />, getWrapper()); const { container } = render(<RoomHeader room={room} />, getWrapper());
expect(
getByLabelText(container, "You do not have permission to start voice calls", { selector: "button" }),
).toHaveAttribute("aria-disabled", "true");
expect( expect(
getByLabelText(container, "You do not have permission to start video calls", { selector: "button" }), getByLabelText(container, "You do not have permission to start video calls", { selector: "button" }),
).toHaveAttribute("aria-disabled", "true"); ).toHaveAttribute("aria-disabled", "true");
@ -456,15 +475,10 @@ describe("RoomHeader", () => {
const { container } = render(<RoomHeader room={room} />, getWrapper()); const { container } = render(<RoomHeader room={room} />, getWrapper());
const voiceButton = getByLabelText(container, "Voice call");
const videoButton = getByLabelText(container, "Video call"); const videoButton = getByLabelText(container, "Video call");
expect(voiceButton).not.toHaveAttribute("aria-disabled", "true");
expect(videoButton).not.toHaveAttribute("aria-disabled", "true"); expect(videoButton).not.toHaveAttribute("aria-disabled", "true");
const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall"); const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall");
fireEvent.click(voiceButton);
expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Voice);
fireEvent.click(videoButton); fireEvent.click(videoButton);
expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Video); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Video);
}); });
@ -479,9 +493,7 @@ describe("RoomHeader", () => {
const { container } = render(<RoomHeader room={room} />, getWrapper()); const { container } = render(<RoomHeader room={room} />, getWrapper());
const voiceButton = getByLabelText(container, "Voice call");
const videoButton = getByLabelText(container, "Video call"); const videoButton = getByLabelText(container, "Video call");
expect(voiceButton).not.toHaveAttribute("aria-disabled", "true");
expect(videoButton).not.toHaveAttribute("aria-disabled", "true"); expect(videoButton).not.toHaveAttribute("aria-disabled", "true");
const dispatcherSpy = jest.spyOn(dispatcher, "dispatch"); const dispatcherSpy = jest.spyOn(dispatcher, "dispatch");
@ -497,9 +509,8 @@ describe("RoomHeader", () => {
); );
const { container } = render(<RoomHeader room={room} />, getWrapper()); const { container } = render(<RoomHeader room={room} />, getWrapper());
const [videoButton, voiceButton] = getAllByLabelText(container, "Ongoing call"); const [videoButton] = getAllByLabelText(container, "Ongoing call");
expect(voiceButton).toHaveAttribute("aria-disabled", "true");
expect(videoButton).toHaveAttribute("aria-disabled", "true"); expect(videoButton).toHaveAttribute("aria-disabled", "true");
}); });