Voice broadcast indicator in room list (#9709)
This commit is contained in:
parent
89439d4f10
commit
474f464e48
10 changed files with 295 additions and 36 deletions
|
@ -375,4 +375,5 @@
|
||||||
@import "./voice-broadcast/atoms/_PlaybackControlButton.pcss";
|
@import "./voice-broadcast/atoms/_PlaybackControlButton.pcss";
|
||||||
@import "./voice-broadcast/atoms/_VoiceBroadcastControl.pcss";
|
@import "./voice-broadcast/atoms/_VoiceBroadcastControl.pcss";
|
||||||
@import "./voice-broadcast/atoms/_VoiceBroadcastHeader.pcss";
|
@import "./voice-broadcast/atoms/_VoiceBroadcastHeader.pcss";
|
||||||
|
@import "./voice-broadcast/atoms/_VoiceBroadcastRoomSubtitle.pcss";
|
||||||
@import "./voice-broadcast/molecules/_VoiceBroadcastBody.pcss";
|
@import "./voice-broadcast/molecules/_VoiceBroadcastBody.pcss";
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_RoomTile .mx_RoomTile_titleContainer .mx_RoomTile_subtitle.mx_RoomTile_subtitle--voice-broadcast {
|
||||||
|
align-items: center;
|
||||||
|
color: $alert;
|
||||||
|
display: flex;
|
||||||
|
gap: $spacing-4;
|
||||||
|
}
|
|
@ -48,20 +48,25 @@ import { RoomTileCallSummary } from "./RoomTileCallSummary";
|
||||||
import { RoomGeneralContextMenu } from "../context_menus/RoomGeneralContextMenu";
|
import { RoomGeneralContextMenu } from "../context_menus/RoomGeneralContextMenu";
|
||||||
import { CallStore, CallStoreEvent } from "../../../stores/CallStore";
|
import { CallStore, CallStoreEvent } from "../../../stores/CallStore";
|
||||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||||
|
import { useHasRoomLiveVoiceBroadcast, VoiceBroadcastRoomSubtitle } from "../../../voice-broadcast";
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
room: Room;
|
room: Room;
|
||||||
showMessagePreview: boolean;
|
showMessagePreview: boolean;
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
tag: TagID;
|
tag: TagID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ClassProps extends Props {
|
||||||
|
hasLiveVoiceBroadcast: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
type PartialDOMRect = Pick<DOMRect, "left" | "bottom">;
|
type PartialDOMRect = Pick<DOMRect, "left" | "bottom">;
|
||||||
|
|
||||||
interface IState {
|
interface State {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
notificationsMenuPosition: PartialDOMRect;
|
notificationsMenuPosition: PartialDOMRect | null;
|
||||||
generalMenuPosition: PartialDOMRect;
|
generalMenuPosition: PartialDOMRect | null;
|
||||||
call: Call | null;
|
call: Call | null;
|
||||||
messagePreview?: string;
|
messagePreview?: string;
|
||||||
}
|
}
|
||||||
|
@ -76,13 +81,13 @@ export const contextMenuBelow = (elementRect: PartialDOMRect) => {
|
||||||
return { left, top, chevronFace };
|
return { left, top, chevronFace };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RoomTile extends React.PureComponent<IProps, IState> {
|
export class RoomTile extends React.PureComponent<ClassProps, State> {
|
||||||
private dispatcherRef: string;
|
private dispatcherRef?: string;
|
||||||
private roomTileRef = createRef<HTMLDivElement>();
|
private roomTileRef = createRef<HTMLDivElement>();
|
||||||
private notificationState: NotificationState;
|
private notificationState: NotificationState;
|
||||||
private roomProps: RoomEchoChamber;
|
private roomProps: RoomEchoChamber;
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: ClassProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -120,7 +125,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
return !this.props.isMinimized && this.props.showMessagePreview;
|
return !this.props.isMinimized && this.props.showMessagePreview;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>) {
|
public componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>) {
|
||||||
const showMessageChanged = prevProps.showMessagePreview !== this.props.showMessagePreview;
|
const showMessageChanged = prevProps.showMessagePreview !== this.props.showMessagePreview;
|
||||||
const minimizedChanged = prevProps.isMinimized !== this.props.isMinimized;
|
const minimizedChanged = prevProps.isMinimized !== this.props.isMinimized;
|
||||||
if (showMessageChanged || minimizedChanged) {
|
if (showMessageChanged || minimizedChanged) {
|
||||||
|
@ -169,7 +174,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
this.onRoomPreviewChanged,
|
this.onRoomPreviewChanged,
|
||||||
);
|
);
|
||||||
this.props.room.off(RoomEvent.Name, this.onRoomNameUpdate);
|
this.props.room.off(RoomEvent.Name, this.onRoomNameUpdate);
|
||||||
defaultDispatcher.unregister(this.dispatcherRef);
|
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
|
||||||
this.notificationState.off(NotificationStateEvents.Update, this.onNotificationUpdate);
|
this.notificationState.off(NotificationStateEvents.Update, this.onNotificationUpdate);
|
||||||
this.roomProps.off(PROPERTY_UPDATED, this.onRoomPropertyUpdate);
|
this.roomProps.off(PROPERTY_UPDATED, this.onRoomPropertyUpdate);
|
||||||
CallStore.instance.off(CallStoreEvent.Call, this.onCallChanged);
|
CallStore.instance.off(CallStoreEvent.Call, this.onCallChanged);
|
||||||
|
@ -218,12 +223,14 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||||
|
const clearSearch = ([KeyBindingAction.Enter, KeyBindingAction.Space] as Array<string | undefined>)
|
||||||
|
.includes(action);
|
||||||
|
|
||||||
defaultDispatcher.dispatch<ViewRoomPayload>({
|
defaultDispatcher.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
show_room_tile: true, // make sure the room is visible in the list
|
show_room_tile: true, // make sure the room is visible in the list
|
||||||
room_id: this.props.room.roomId,
|
room_id: this.props.room.roomId,
|
||||||
clear_search: [KeyBindingAction.Enter, KeyBindingAction.Space].includes(action),
|
clear_search: clearSearch,
|
||||||
metricsTrigger: "RoomList",
|
metricsTrigger: "RoomList",
|
||||||
metricsViaKeyboard: ev.type !== "click",
|
metricsViaKeyboard: ev.type !== "click",
|
||||||
});
|
});
|
||||||
|
@ -233,7 +240,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
this.setState({ selected: isActive });
|
this.setState({ selected: isActive });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onNotificationsMenuOpenClick = (ev: React.MouseEvent) => {
|
private onNotificationsMenuOpenClick = (ev: ButtonEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const target = ev.target as HTMLButtonElement;
|
const target = ev.target as HTMLButtonElement;
|
||||||
|
@ -246,7 +253,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
this.setState({ notificationsMenuPosition: null });
|
this.setState({ notificationsMenuPosition: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onGeneralMenuOpenClick = (ev: React.MouseEvent) => {
|
private onGeneralMenuOpenClick = (ev: ButtonEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const target = ev.target as HTMLButtonElement;
|
const target = ev.target as HTMLButtonElement;
|
||||||
|
@ -271,7 +278,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
this.setState({ generalMenuPosition: null });
|
this.setState({ generalMenuPosition: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderNotificationsMenu(isActive: boolean): React.ReactElement {
|
private renderNotificationsMenu(isActive: boolean): React.ReactElement | null {
|
||||||
if (MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Archived ||
|
if (MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Archived ||
|
||||||
!this.showContextMenu || this.props.isMinimized
|
!this.showContextMenu || this.props.isMinimized
|
||||||
) {
|
) {
|
||||||
|
@ -313,7 +320,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderGeneralMenu(): React.ReactElement {
|
private renderGeneralMenu(): React.ReactElement | null {
|
||||||
if (!this.showContextMenu) return null; // no menu to show
|
if (!this.showContextMenu) return null; // no menu to show
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -379,6 +386,8 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
<RoomTileCallSummary call={this.state.call} />
|
<RoomTileCallSummary call={this.state.call} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (this.props.hasLiveVoiceBroadcast) {
|
||||||
|
subtitle = <VoiceBroadcastRoomSubtitle />;
|
||||||
} else if (this.showMessagePreview && this.state.messagePreview) {
|
} else if (this.showMessagePreview && this.state.messagePreview) {
|
||||||
subtitle = (
|
subtitle = (
|
||||||
<div
|
<div
|
||||||
|
@ -472,3 +481,10 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RoomTileHOC: React.FC<Props> = (props: Props) => {
|
||||||
|
const hasLiveVoiceBroadcast = useHasRoomLiveVoiceBroadcast(props.room);
|
||||||
|
return <RoomTile {...props} hasLiveVoiceBroadcast={hasLiveVoiceBroadcast} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RoomTileHOC;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
|
export const VoiceBroadcastRoomSubtitle = () => {
|
||||||
|
return <div className="mx_RoomTile_subtitle mx_RoomTile_subtitle--voice-broadcast">
|
||||||
|
<LiveIcon className="mx_Icon mx_Icon_16" />
|
||||||
|
{ _t("Live") }
|
||||||
|
</div>;
|
||||||
|
};
|
35
src/voice-broadcast/hooks/useHasRoomLiveVoiceBroadcast.ts
Normal file
35
src/voice-broadcast/hooks/useHasRoomLiveVoiceBroadcast.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { hasRoomLiveVoiceBroadcast } from "../utils/hasRoomLiveVoiceBroadcast";
|
||||||
|
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
|
||||||
|
|
||||||
|
export const useHasRoomLiveVoiceBroadcast = (room: Room) => {
|
||||||
|
const [hasLiveVoiceBroadcast, setHasLiveVoiceBroadcast] = useState(hasRoomLiveVoiceBroadcast(room).hasBroadcast);
|
||||||
|
|
||||||
|
useTypedEventEmitter(
|
||||||
|
room.currentState,
|
||||||
|
RoomStateEvent.Update,
|
||||||
|
() => {
|
||||||
|
setHasLiveVoiceBroadcast(hasRoomLiveVoiceBroadcast(room).hasBroadcast);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return hasLiveVoiceBroadcast;
|
||||||
|
};
|
|
@ -29,12 +29,14 @@ export * from "./components/VoiceBroadcastBody";
|
||||||
export * from "./components/atoms/LiveBadge";
|
export * from "./components/atoms/LiveBadge";
|
||||||
export * from "./components/atoms/VoiceBroadcastControl";
|
export * from "./components/atoms/VoiceBroadcastControl";
|
||||||
export * from "./components/atoms/VoiceBroadcastHeader";
|
export * from "./components/atoms/VoiceBroadcastHeader";
|
||||||
|
export * from "./components/atoms/VoiceBroadcastRoomSubtitle";
|
||||||
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
||||||
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
||||||
export * from "./components/molecules/VoiceBroadcastRecordingBody";
|
export * from "./components/molecules/VoiceBroadcastRecordingBody";
|
||||||
export * from "./components/molecules/VoiceBroadcastRecordingPip";
|
export * from "./components/molecules/VoiceBroadcastRecordingPip";
|
||||||
export * from "./hooks/useCurrentVoiceBroadcastPreRecording";
|
export * from "./hooks/useCurrentVoiceBroadcastPreRecording";
|
||||||
export * from "./hooks/useCurrentVoiceBroadcastRecording";
|
export * from "./hooks/useCurrentVoiceBroadcastRecording";
|
||||||
|
export * from "./hooks/useHasRoomLiveVoiceBroadcast";
|
||||||
export * from "./hooks/useVoiceBroadcastRecording";
|
export * from "./hooks/useVoiceBroadcastRecording";
|
||||||
export * from "./stores/VoiceBroadcastPlaybacksStore";
|
export * from "./stores/VoiceBroadcastPlaybacksStore";
|
||||||
export * from "./stores/VoiceBroadcastPreRecordingStore";
|
export * from "./stores/VoiceBroadcastPreRecordingStore";
|
||||||
|
|
|
@ -32,7 +32,7 @@ import RoomListStore, { RoomListStoreClass } from "../../../../src/stores/room-l
|
||||||
import RoomListLayoutStore from "../../../../src/stores/room-list/RoomListLayoutStore";
|
import RoomListLayoutStore from "../../../../src/stores/room-list/RoomListLayoutStore";
|
||||||
import RoomList from "../../../../src/components/views/rooms/RoomList";
|
import RoomList from "../../../../src/components/views/rooms/RoomList";
|
||||||
import RoomSublist from "../../../../src/components/views/rooms/RoomSublist";
|
import RoomSublist from "../../../../src/components/views/rooms/RoomSublist";
|
||||||
import RoomTile from "../../../../src/components/views/rooms/RoomTile";
|
import { RoomTile } from "../../../../src/components/views/rooms/RoomTile";
|
||||||
import { getMockClientWithEventEmitter, mockClientMethodsUser } from '../../../test-utils';
|
import { getMockClientWithEventEmitter, mockClientMethodsUser } from '../../../test-utils';
|
||||||
import ResizeNotifier from '../../../../src/utils/ResizeNotifier';
|
import ResizeNotifier from '../../../../src/utils/ResizeNotifier';
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { render, screen, act } from "@testing-library/react";
|
import { render, screen, act, RenderResult } from "@testing-library/react";
|
||||||
import { mocked, Mocked } from "jest-mock";
|
import { mocked, Mocked } from "jest-mock";
|
||||||
import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
|
import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
import { Widget } from "matrix-widget-api";
|
import { Widget } from "matrix-widget-api";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import type { ClientWidgetApi } from "matrix-widget-api";
|
import type { ClientWidgetApi } from "matrix-widget-api";
|
||||||
|
@ -30,6 +31,7 @@ import {
|
||||||
MockedCall,
|
MockedCall,
|
||||||
useMockedCalls,
|
useMockedCalls,
|
||||||
setupAsyncStoreWithClient,
|
setupAsyncStoreWithClient,
|
||||||
|
filterConsole,
|
||||||
} from "../../../test-utils";
|
} from "../../../test-utils";
|
||||||
import { CallStore } from "../../../../src/stores/CallStore";
|
import { CallStore } from "../../../../src/stores/CallStore";
|
||||||
import RoomTile from "../../../../src/components/views/rooms/RoomTile";
|
import RoomTile from "../../../../src/components/views/rooms/RoomTile";
|
||||||
|
@ -39,38 +41,79 @@ import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
import PlatformPeg from "../../../../src/PlatformPeg";
|
import PlatformPeg from "../../../../src/PlatformPeg";
|
||||||
import BasePlatform from "../../../../src/BasePlatform";
|
import BasePlatform from "../../../../src/BasePlatform";
|
||||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||||
|
import { VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
|
||||||
|
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||||
|
|
||||||
describe("RoomTile", () => {
|
describe("RoomTile", () => {
|
||||||
jest.spyOn(PlatformPeg, "get")
|
jest.spyOn(PlatformPeg, "get")
|
||||||
.mockReturnValue({ overrideBrowserShortcuts: () => false } as unknown as BasePlatform);
|
.mockReturnValue({ overrideBrowserShortcuts: () => false } as unknown as BasePlatform);
|
||||||
useMockedCalls();
|
useMockedCalls();
|
||||||
|
|
||||||
|
const setUpVoiceBroadcast = (state: VoiceBroadcastInfoState): void => {
|
||||||
|
voiceBroadcastInfoEvent = mkVoiceBroadcastInfoStateEvent(
|
||||||
|
room.roomId,
|
||||||
|
state,
|
||||||
|
client.getUserId(),
|
||||||
|
client.getDeviceId(),
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
room.currentState.setStateEvents([voiceBroadcastInfoEvent]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderRoomTile = (): void => {
|
||||||
|
renderResult = render(
|
||||||
|
<RoomTile
|
||||||
|
room={room}
|
||||||
|
showMessagePreview={false}
|
||||||
|
isMinimized={false}
|
||||||
|
tag={DefaultTagID.Untagged}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
let client: Mocked<MatrixClient>;
|
let client: Mocked<MatrixClient>;
|
||||||
|
let restoreConsole: () => void;
|
||||||
|
let voiceBroadcastInfoEvent: MatrixEvent;
|
||||||
|
let room: Room;
|
||||||
|
let renderResult: RenderResult;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
restoreConsole = filterConsole(
|
||||||
|
// irrelevant for this test
|
||||||
|
"Room !1:example.org does not have an m.room.create event",
|
||||||
|
);
|
||||||
|
|
||||||
stubClient();
|
stubClient();
|
||||||
client = mocked(MatrixClientPeg.get());
|
client = mocked(MatrixClientPeg.get());
|
||||||
DMRoomMap.makeShared();
|
DMRoomMap.makeShared();
|
||||||
|
|
||||||
|
room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||||
|
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||||
|
});
|
||||||
|
|
||||||
|
client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null);
|
||||||
|
client.getRooms.mockReturnValue([room]);
|
||||||
|
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||||
|
|
||||||
|
renderRoomTile();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
restoreConsole();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("call subtitle", () => {
|
it("should render the room", () => {
|
||||||
let room: Room;
|
expect(renderResult.container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when a call starts", () => {
|
||||||
let call: MockedCall;
|
let call: MockedCall;
|
||||||
let widget: Widget;
|
let widget: Widget;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
room = new Room("!1:example.org", client, "@alice:example.org", {
|
|
||||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
|
||||||
});
|
|
||||||
|
|
||||||
client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null);
|
|
||||||
client.getRooms.mockReturnValue([room]);
|
|
||||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
|
||||||
|
|
||||||
setupAsyncStoreWithClient(CallStore.instance, client);
|
setupAsyncStoreWithClient(CallStore.instance, client);
|
||||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||||
|
|
||||||
|
@ -83,18 +126,10 @@ describe("RoomTile", () => {
|
||||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
||||||
stop: () => {},
|
stop: () => {},
|
||||||
} as unknown as ClientWidgetApi);
|
} as unknown as ClientWidgetApi);
|
||||||
|
|
||||||
render(
|
|
||||||
<RoomTile
|
|
||||||
room={room}
|
|
||||||
showMessagePreview={false}
|
|
||||||
isMinimized={false}
|
|
||||||
tag={DefaultTagID.Untagged}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
renderResult.unmount();
|
||||||
call.destroy();
|
call.destroy();
|
||||||
client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]);
|
client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]);
|
||||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||||
|
@ -147,5 +182,45 @@ describe("RoomTile", () => {
|
||||||
act(() => { call.participants = new Map(); });
|
act(() => { call.participants = new Map(); });
|
||||||
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("and a live broadcast starts", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setUpVoiceBroadcast(VoiceBroadcastInfoState.Started);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should still render the call subtitle", () => {
|
||||||
|
expect(screen.queryByText("Video")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Live")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when a live voice broadcast starts", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setUpVoiceBroadcast(VoiceBroadcastInfoState.Started);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the »Live« subtitle", () => {
|
||||||
|
expect(screen.queryByText("Live")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the broadcast stops", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const stopEvent = mkVoiceBroadcastInfoStateEvent(
|
||||||
|
room.roomId,
|
||||||
|
VoiceBroadcastInfoState.Stopped,
|
||||||
|
client.getUserId(),
|
||||||
|
client.getDeviceId(),
|
||||||
|
voiceBroadcastInfoEvent,
|
||||||
|
);
|
||||||
|
act(() => {
|
||||||
|
room.currentState.setStateEvents([stopEvent]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not render the »Live« subtitle", () => {
|
||||||
|
expect(screen.queryByText("Live")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`RoomTile should render the room 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-label="!1:example.org Unread messages."
|
||||||
|
aria-selected="false"
|
||||||
|
class="mx_AccessibleButton mx_RoomTile"
|
||||||
|
role="treeitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_DecoratedRoomAvatar"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_BaseAvatar"
|
||||||
|
role="presentation"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx_BaseAvatar_initial"
|
||||||
|
style="font-size: 20.8px; width: 32px; line-height: 32px;"
|
||||||
|
>
|
||||||
|
!
|
||||||
|
</span>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx_BaseAvatar_image"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
src="data:image/png;base64,00"
|
||||||
|
style="width: 32px; height: 32px;"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_RoomTile_titleContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomTile_title mx_RoomTile_titleHasUnreadEvents"
|
||||||
|
tabindex="-1"
|
||||||
|
title="!1:example.org"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
dir="auto"
|
||||||
|
>
|
||||||
|
!1:example.org
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx_RoomTile_badgeContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_dot"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_NotificationBadge_count"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-label="Room options"
|
||||||
|
class="mx_AccessibleButton mx_RoomTile_menuButton"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-label="Notification options"
|
||||||
|
class="mx_AccessibleButton mx_RoomTile_notificationsButton"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -39,7 +39,7 @@ export const filterConsole = (...ignoreList: string[]): () => void => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
originalFunction(data);
|
originalFunction(...data);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue