Remove NoOneHere disabled reason. (#30524)

* Remove NoOneHere disabled reason.
This was used to prohibit starting calls if the user is alone in the room.
Since there are currently issues with the user count calculation this can disable the button even when not appropriate.

On top of that, there is a reason to start a call if the room was just created and the user is still waiting for the others to join the room to then join the call.

Signed-off-by: Timo K <toger5@hotmail.de>

* some ci fixes

Signed-off-by: Timo K <toger5@hotmail.de>

* fix test snapshots

Signed-off-by: Timo K <toger5@hotmail.de>

* fix test to expect enabled call buttons

* Update snapshot for unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx

---------

Signed-off-by: Timo K <toger5@hotmail.de>
Co-authored-by: fkwp <github-fkwp@w4ve.de>
This commit is contained in:
Timo 2025-08-12 12:07:59 +02:00 committed by GitHub
parent 76be5ccc9e
commit 789dba7b3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 24 additions and 114 deletions

View File

@ -37,11 +37,8 @@ test.describe("Room Header", () => {
await expect(header.locator(".mx_FacePile")).toBeVisible();
// There should be both a voice and a video call button
// but they'll be disabled
const callButtons = header.getByRole("button", { name: "There's no one here to call" });
await expect(callButtons).toHaveCount(2);
await expect(callButtons.first()).toBeVisible();
await expect(callButtons.last()).toBeVisible();
await expect(header.getByRole("button", { name: "Video call" })).toBeVisible();
await expect(header.getByRole("button", { name: "Voice call" })).toBeVisible();
await expect(header.getByRole("button", { name: "Threads" })).toBeVisible();
await expect(header.getByRole("button", { name: "Notifications" })).toBeVisible();

View File

@ -32,7 +32,6 @@ import { type ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"
import { Action } from "../../dispatcher/actions";
import { CallStore, CallStoreEvent } from "../../stores/CallStore";
import { isVideoRoom } from "../../utils/video-rooms";
import { useGuestAccessInformation } from "./useGuestAccessInformation";
import { UIFeature } from "../../settings/UIFeature";
import { BetaPill } from "../../components/views/beta/BetaCard";
import { type InteractionName } from "../../PosthogTrackers";
@ -73,7 +72,6 @@ export const getPlatformCallTypeProps = (
const enum State {
NoCall,
NoOneHere,
NoPermission,
Unpinned,
Ongoing,
@ -197,7 +195,6 @@ export const useRoomCall = (
const connectedCalls = useEventEmitterState(CallStore.instance, CallStoreEvent.ConnectedCalls, () =>
Array.from(CallStore.instance.connectedCalls),
);
const { canInviteGuests } = useGuestAccessInformation(room);
const state = useMemo((): State => {
if (connectedCalls.find((call) => call.roomId != room.roomId)) {
@ -209,9 +206,6 @@ export const useRoomCall = (
if (hasLegacyCall) {
return State.Ongoing;
}
if (memberCount <= 1 && !canInviteGuests) {
return State.NoOneHere;
}
if (!callOptions.includes(PlatformCallType.LegacyCall) && !mayCreateElementCalls && !mayEditWidgets) {
return State.NoPermission;
@ -220,14 +214,12 @@ export const useRoomCall = (
}, [
callOptions,
connectedCalls,
canInviteGuests,
hasGroupCall,
hasJitsiWidget,
hasLegacyCall,
hasManagedHybridWidget,
mayCreateElementCalls,
mayEditWidgets,
memberCount,
promptPinWidget,
room.roomId,
]);
@ -266,10 +258,6 @@ export const useRoomCall = (
voiceCallDisabledReason = _t("voip|disabled_ongoing_call");
videoCallDisabledReason = _t("voip|disabled_ongoing_call");
break;
case State.NoOneHere:
voiceCallDisabledReason = _t("voip|disabled_no_one_here");
videoCallDisabledReason = _t("voip|disabled_no_one_here");
break;
case State.Unpinned:
case State.NotJoined:
case State.NoCall:

View File

@ -3965,7 +3965,6 @@
"dialpad": "Dialpad",
"disable_camera": "Turn off camera",
"disable_microphone": "Mute microphone",
"disabled_no_one_here": "There's no one here to call",
"disabled_no_perms_start_video_call": "You do not have permission to start video calls",
"disabled_no_perms_start_voice_call": "You do not have permission to start voice calls",
"disabled_ongoing_call": "Ongoing call",

View File

@ -1449,8 +1449,8 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-disabled="false"
aria-label="Video call"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1459,7 +1459,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«r2c»"
@ -1476,8 +1476,8 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«r2h»"
class="_icon-button_1pz9o_8"
data-kind="primary"
@ -1487,7 +1487,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
@ -1661,8 +1661,8 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-disabled="false"
aria-label="Video call"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1671,7 +1671,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«r2c»"
@ -1688,8 +1688,8 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«r2h»"
class="_icon-button_1pz9o_8"
data-kind="primary"
@ -1699,7 +1699,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"

View File

@ -26,7 +26,6 @@ import {
getAllByLabelText,
getByLabelText,
getByText,
queryAllByLabelText,
queryByLabelText,
render,
type RenderOptions,
@ -327,14 +326,6 @@ describe("RoomHeader", () => {
SdkConfig.reset();
});
it("you can't call if you're alone", () => {
mockRoomMembers(room, 1);
const { container } = render(<RoomHeader room={room} />, getWrapper());
for (const button of getAllByLabelText(container, "There's no one here to call")) {
expect(button).toHaveAttribute("aria-disabled", "true");
}
});
it("you can call when you're two in the room", async () => {
const user = userEvent.setup();
mockRoomMembers(room, 2);
@ -489,71 +480,6 @@ describe("RoomHeader", () => {
}
});
it("can't call if you have no friends and cannot invite friends", () => {
mockRoomMembers(room, 1);
const { container } = render(<RoomHeader room={room} />, getWrapper());
for (const button of getAllByLabelText(container, "There's no one here to call")) {
expect(button).toHaveAttribute("aria-disabled", "true");
}
});
it("can call if you have no friends but can invite friends", () => {
mockRoomMembers(room, 1);
// go through all the different `canInvite` and `getJoinRule` combinations
// check where we can't do anything but can upgrade
jest.spyOn(room.currentState, "maySendStateEvent").mockReturnValue(true);
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Invite);
jest.spyOn(room, "canInvite").mockReturnValue(false);
SdkConfig.add({
element_call: {
guest_spa_url: "https://guest_spa_url.com",
},
});
const { container: containerNoInviteNotPublicCanUpgradeAccess } = render(
<RoomHeader room={room} />,
getWrapper(),
);
expect(
queryAllByLabelText(containerNoInviteNotPublicCanUpgradeAccess, "There's no one here to call"),
).toHaveLength(0);
// dont allow upgrading anymore and go through the other combinations
jest.spyOn(room.currentState, "maySendStateEvent").mockReturnValue(false);
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Invite);
jest.spyOn(room, "canInvite").mockReturnValue(false);
SdkConfig.add({
element_call: {
guest_spa_url: "https://guest_spa_url.com",
},
});
const { container: containerNoInviteNotPublic } = render(<RoomHeader room={room} />, getWrapper());
expect(queryAllByLabelText(containerNoInviteNotPublic, "There's no one here to call")).toHaveLength(2);
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
jest.spyOn(room, "canInvite").mockReturnValue(false);
const { container: containerNoInvitePublic } = render(<RoomHeader room={room} />, getWrapper());
expect(queryAllByLabelText(containerNoInvitePublic, "There's no one here to call")).toHaveLength(2);
jest.spyOn(room, "canInvite").mockReturnValue(true);
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Invite);
const { container: containerInviteNotPublic } = render(<RoomHeader room={room} />, getWrapper());
expect(queryAllByLabelText(containerInviteNotPublic, "There's no one here to call")).toHaveLength(2);
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
jest.spyOn(room, "canInvite").mockReturnValue(true);
const { container: containerInvitePublic } = render(<RoomHeader room={room} />, getWrapper());
expect(queryAllByLabelText(containerInvitePublic, "There's no one here to call")).toHaveLength(0);
// Clear guest_spa_url
SdkConfig.reset();
// last we can allow everything but without guest_spa_url nothing will work
const { container: containerAllAllowedButNoGuestSpaUrl } = render(<RoomHeader room={room} />, getWrapper());
expect(
queryAllByLabelText(containerAllAllowedButNoGuestSpaUrl, "There's no one here to call"),
).toHaveLength(2);
});
it("calls using legacy or jitsi", async () => {
const user = userEvent.setup();
mockRoomMembers(room, 2);

View File

@ -43,8 +43,8 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-disabled="false"
aria-label="Video call"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -53,10 +53,10 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«r1do»"
aria-labelledby="«r14k»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@ -70,9 +70,9 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-labelledby="«r1dt»"
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«r14p»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -81,7 +81,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
@ -98,7 +98,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
</button>
<button
aria-label="Threads"
aria-labelledby="«r1e2»"
aria-labelledby="«r14u»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -125,7 +125,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
</button>
<button
aria-label="Room info"
aria-labelledby="«r1e7»"
aria-labelledby="«r153»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"