Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Room call banner #9378

Merged
merged 23 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 35 additions & 25 deletions res/css/views/rooms/_RoomCallBanner.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,40 @@ limitations under the License.
*/

.mx_RoomCallBanner {
.mx_RoomCallBanner_text {
display: flex;
flex: 1;
align-items: center;
}
.mx_RoomCallBanner_label {
color: $primary-content;
font-weight: 600;
padding-right: 8px;

&::before {
display: inline-block;
vertical-align: text-top;
content: "";
background-color: $secondary-content;
mask-size: 16px;
width: 16px;
height: 16px;
margin-right: 4px;
bottom: 2px;
mask-image: url("$(res)/img/element-icons/call/video-call.svg");
}
}
.mx_RoomCallBanner_button {
left: 16px;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;

box-sizing: border-box;
padding: $spacing-12 $spacing-16;

color: $primary-content;
background-color: $system;
cursor: pointer;
}

.mx_RoomCallBanner_text {
display: flex;
flex: 1;
align-items: center;
}

.mx_RoomCallBanner_label {
color: $primary-content;
font-weight: 600;
padding-right: $spacing-8;

&::before {
display: inline-block;
vertical-align: text-top;
content: "";
background-color: $secondary-content;
mask-size: 16px;
width: 16px;
height: 16px;
margin-right: 4px;
bottom: 2px;
mask-image: url("$(res)/img/element-icons/call/video-call.svg");
}
}
71 changes: 11 additions & 60 deletions src/components/views/beacon/RoomCallBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { Fragment, useCallback } from "react";
import React, { useCallback } from "react";
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";

import { _t } from "../../../languageHandler";
Expand All @@ -31,7 +31,6 @@ import {
OwnBeaconStoreEvent,
} from "../../../stores/OwnBeaconStore";
import { CallDurationFromEvent } from "../voip/CallDuration";
import { MockedCall } from "../../../../test/test-utils/call";

interface RoomCallBannerProps {
roomId: Room["roomId"];
Expand All @@ -42,14 +41,7 @@ const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
roomId,
call,
}) => {
let callEvent: MatrixEvent | null = null;

if (!!(call as ElementCall).groupCall) {
callEvent = (call as ElementCall).groupCall;
}
if (!!(call as MockedCall).event) {
callEvent = (call as MockedCall).event;
}
const callEvent: MatrixEvent | null = (call as ElementCall)?.groupCall;

const connect = useCallback(
(ev: ButtonEvent) => {
Expand All @@ -64,15 +56,7 @@ const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
[roomId],
);

const disconnect = useCallback(
(ev: ButtonEvent) => {
ev.preventDefault();
call?.disconnect();
},
[call],
);

const onClick = () => {
const onClick = useCallback(() => {
dispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
Expand All @@ -81,45 +65,11 @@ const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
scroll_into_view: true,
highlighted: true,
});
};

let [buttonText, buttonKind, onButtonClick] = [null, null, null, null];

switch (call.connectionState) {
case ConnectionState.Disconnected:
[buttonText, buttonKind, onButtonClick] = [
_t("Join"),
"primary",
connect,
];
break;
case ConnectionState.Connecting:
[buttonText, buttonKind, onButtonClick] = [
_t("Join"),
"primary",
null,
];
break;
case ConnectionState.Connected:
[buttonText, buttonKind, onButtonClick] = [
_t("Leave"),
"danger",
disconnect,
];
break;
case ConnectionState.Disconnecting:
[buttonText, buttonKind, onButtonClick] = [
_t("Leave"),
"danger",
null,
];
break;
}
if (!call) return <Fragment />;
}, [callEvent, roomId]);

return (
<div
className="mx_RoomLiveShareWarning mx_RoomCallBanner"
className="mx_RoomCallBanner"
onClick={onClick}
>
<div className="mx_RoomCallBanner_text">
Expand All @@ -128,14 +78,12 @@ const RoomCallBannerInner: React.FC<RoomCallBannerProps> = ({
</div>

<AccessibleButton
className="mx_RoomCallBanner_button"
data-test-id="room-live-share-primary-button"
onClick={onButtonClick}
kind={buttonKind}
onClick={connect}
kind="primary"
element="button"
disabled={false}
>
{ buttonText }
{ _t("Join") }
</AccessibleButton>
</div>
);
Expand Down Expand Up @@ -172,6 +120,9 @@ const RoomCallBanner: React.FC<Props> = ({ roomId }) => {

// split into outer/inner to avoid watching various parts if there is no call
if (call) {
// No banner if the call is not connected
toger5 marked this conversation as resolved.
Show resolved Hide resolved
if (call.connectionState !== ConnectionState.Disconnected) {return null;}
toger5 marked this conversation as resolved.
Show resolved Hide resolved

return <RoomCallBannerInner call={call} roomId={roomId} />;
}
return null;
Expand Down
1 change: 1 addition & 0 deletions src/components/views/beacon/RoomLiveShareWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
);

if (!isMonitoringLiveLocation || !liveBeaconIds.length) {
// This logic is entangled with the RoomCallBanner-test's. The tests need updating if this logic changes.
return null;
}

Expand Down
19 changes: 6 additions & 13 deletions test/components/views/beacon/RoomCallBanner-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import { CallStore } from "../../../../src/stores/CallStore";
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import { RoomViewStore } from "../../../../src/stores/RoomViewStore";
import { ConnectionState } from "../../../../src/models/Call";

describe("<RoomCallBanner />", () => {
toger5 marked this conversation as resolved.
Show resolved Hide resolved
let client: Mocked<MatrixClient>;
Expand Down Expand Up @@ -121,23 +120,17 @@ describe("<RoomCallBanner />", () => {

it("shows Join button if the user has not joined", async () => {
await renderBanner();
const videoCallLabel = await screen.findByText("Join");
expect(videoCallLabel.innerHTML).toBe("Join");
});

it("shows Leave button if the user has not joined", async () => {
call.setConnectionState(ConnectionState.Connected);
await renderBanner();
const videoCallLabel = await screen.findByText("Leave");
expect(videoCallLabel.innerHTML).toBe("Leave");
await screen.findByText("Join");
});

it("dont show banner if the call is shown", async () => {
jest.spyOn(RoomViewStore.instance, 'isViewingCall').mockReturnValue(false);
jest.spyOn(RoomViewStore.instance, 'isViewingCall').mockReturnValue(true);
await renderBanner();
const videoCallLabel = await screen.findByText("Video call");
expect(videoCallLabel.innerHTML).toBe("Video call");
const banner = await screen.queryByText("Video call");
expect(banner).toBeFalsy();
});
});

// TODO: test clicking buttons
// TODO: add live location share warning test (should not render if there is an active live location share)
});
10 changes: 5 additions & 5 deletions test/test-utils/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { Room } from "matrix-js-sdk/src/models/room";
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
import type { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { mkEvent } from "./test-utils";
import { Call, ConnectionState, ElementCall, JitsiCall } from "../../src/models/Call";
import { Call, ElementCall, JitsiCall } from "../../src/models/Call";

export class MockedCall extends Call {
public static readonly EVENT_TYPE = "org.example.mocked_call";
Expand Down Expand Up @@ -61,17 +61,17 @@ export class MockedCall extends Call {
})]);
}

public get groupCall(): MatrixEvent {
return this.event;
}

public get participants(): Set<RoomMember> {
return super.participants;
}
public set participants(value: Set<RoomMember>) {
super.participants = value;
}

public setConnectionState(value: ConnectionState) {
super.connectionState = value;
}

// No action needed for any of the following methods since this is just a mock
protected getDevices(): string[] { return []; }
protected async setDevices(): Promise<void> { }
Expand Down