mirror of
https://github.com/element-hq/element-web
synced 2024-11-26 11:15:53 +03:00
Tweak voice broadcast live icon (#9576)
This commit is contained in:
parent
973513cc75
commit
cf3c899dd1
21 changed files with 262 additions and 73 deletions
|
@ -25,3 +25,7 @@ limitations under the License.
|
||||||
gap: $spacing-4;
|
gap: $spacing-4;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LiveBadge--grey {
|
||||||
|
background-color: $quaternary-content;
|
||||||
|
}
|
||||||
|
|
|
@ -14,13 +14,27 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
|
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
export const LiveBadge: React.FC = () => {
|
interface Props {
|
||||||
return <div className="mx_LiveBadge">
|
grey?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LiveBadge: React.FC<Props> = ({
|
||||||
|
grey = false,
|
||||||
|
}) => {
|
||||||
|
const liveBadgeClasses = classNames(
|
||||||
|
"mx_LiveBadge",
|
||||||
|
{
|
||||||
|
"mx_LiveBadge--grey": grey,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return <div className={liveBadgeClasses}>
|
||||||
<LiveIcon className="mx_Icon mx_Icon_16" />
|
<LiveIcon className="mx_Icon mx_Icon_16" />
|
||||||
{ _t("Live") }
|
{ _t("Live") }
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import React from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/matrix";
|
import { Room } from "matrix-js-sdk/src/matrix";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
import { LiveBadge } from "../..";
|
import { LiveBadge, VoiceBroadcastLiveness } from "../..";
|
||||||
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
|
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
|
||||||
import { Icon as MicrophoneIcon } from "../../../../res/img/voip/call-view/mic-on.svg";
|
import { Icon as MicrophoneIcon } from "../../../../res/img/voip/call-view/mic-on.svg";
|
||||||
import { Icon as TimerIcon } from "../../../../res/img/element-icons/Timer.svg";
|
import { Icon as TimerIcon } from "../../../../res/img/element-icons/Timer.svg";
|
||||||
|
@ -27,7 +27,7 @@ import Clock from "../../../components/views/audio_messages/Clock";
|
||||||
import { formatTimeLeft } from "../../../DateUtils";
|
import { formatTimeLeft } from "../../../DateUtils";
|
||||||
|
|
||||||
interface VoiceBroadcastHeaderProps {
|
interface VoiceBroadcastHeaderProps {
|
||||||
live?: boolean;
|
live?: VoiceBroadcastLiveness;
|
||||||
onCloseClick?: () => void;
|
onCloseClick?: () => void;
|
||||||
onMicrophoneLineClick?: () => void;
|
onMicrophoneLineClick?: () => void;
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -38,7 +38,7 @@ interface VoiceBroadcastHeaderProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
|
export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
|
||||||
live = false,
|
live = "not-live",
|
||||||
onCloseClick = () => {},
|
onCloseClick = () => {},
|
||||||
onMicrophoneLineClick,
|
onMicrophoneLineClick,
|
||||||
room,
|
room,
|
||||||
|
@ -54,7 +54,9 @@ export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
|
||||||
</div>
|
</div>
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const liveBadge = live ? <LiveBadge /> : null;
|
const liveBadge = live === "not-live"
|
||||||
|
? null
|
||||||
|
: <LiveBadge grey={live === "grey"} />;
|
||||||
|
|
||||||
const closeButton = showClose
|
const closeButton = showClose
|
||||||
? <AccessibleButton onClick={onCloseClick}>
|
? <AccessibleButton onClick={onCloseClick}>
|
||||||
|
|
|
@ -39,7 +39,7 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
duration,
|
duration,
|
||||||
live,
|
liveness,
|
||||||
room,
|
room,
|
||||||
sender,
|
sender,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -79,7 +79,7 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
|
||||||
return (
|
return (
|
||||||
<div className="mx_VoiceBroadcastBody">
|
<div className="mx_VoiceBroadcastBody">
|
||||||
<VoiceBroadcastHeader
|
<VoiceBroadcastHeader
|
||||||
live={live}
|
live={liveness}
|
||||||
microphoneLabel={sender?.name}
|
microphoneLabel={sender?.name}
|
||||||
room={room}
|
room={room}
|
||||||
showBroadcast={true}
|
showBroadcast={true}
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const VoiceBroadcastRecordingBody: React.FC<VoiceBroadcastRecordingBodyPr
|
||||||
return (
|
return (
|
||||||
<div className="mx_VoiceBroadcastBody">
|
<div className="mx_VoiceBroadcastBody">
|
||||||
<VoiceBroadcastHeader
|
<VoiceBroadcastHeader
|
||||||
live={live}
|
live={live ? "live" : "grey"}
|
||||||
microphoneLabel={sender?.name}
|
microphoneLabel={sender?.name}
|
||||||
room={room}
|
room={room}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -55,7 +55,7 @@ export const VoiceBroadcastRecordingPip: React.FC<VoiceBroadcastRecordingPipProp
|
||||||
className="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
|
className="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
|
||||||
>
|
>
|
||||||
<VoiceBroadcastHeader
|
<VoiceBroadcastHeader
|
||||||
live={live}
|
live={live ? "live" : "grey"}
|
||||||
room={room}
|
room={room}
|
||||||
timeLeft={timeLeft}
|
timeLeft={timeLeft}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { useState } from "react";
|
||||||
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
|
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
import {
|
import {
|
||||||
VoiceBroadcastInfoState,
|
|
||||||
VoiceBroadcastPlayback,
|
VoiceBroadcastPlayback,
|
||||||
VoiceBroadcastPlaybackEvent,
|
VoiceBroadcastPlaybackEvent,
|
||||||
VoiceBroadcastPlaybackState,
|
VoiceBroadcastPlaybackState,
|
||||||
|
@ -41,13 +40,6 @@ export const useVoiceBroadcastPlayback = (playback: VoiceBroadcastPlayback) => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const [playbackInfoState, setPlaybackInfoState] = useState(playback.getInfoState());
|
|
||||||
useTypedEventEmitter(
|
|
||||||
playback,
|
|
||||||
VoiceBroadcastPlaybackEvent.InfoStateChanged,
|
|
||||||
setPlaybackInfoState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [duration, setDuration] = useState(playback.durationSeconds);
|
const [duration, setDuration] = useState(playback.durationSeconds);
|
||||||
useTypedEventEmitter(
|
useTypedEventEmitter(
|
||||||
playback,
|
playback,
|
||||||
|
@ -55,9 +47,16 @@ export const useVoiceBroadcastPlayback = (playback: VoiceBroadcastPlayback) => {
|
||||||
d => setDuration(d / 1000),
|
d => setDuration(d / 1000),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [liveness, setLiveness] = useState(playback.getLiveness());
|
||||||
|
useTypedEventEmitter(
|
||||||
|
playback,
|
||||||
|
VoiceBroadcastPlaybackEvent.LivenessChanged,
|
||||||
|
l => setLiveness(l),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
duration,
|
duration,
|
||||||
live: playbackInfoState !== VoiceBroadcastInfoState.Stopped,
|
liveness: liveness,
|
||||||
room: room,
|
room: room,
|
||||||
sender: playback.infoEvent.sender,
|
sender: playback.infoEvent.sender,
|
||||||
toggle: playbackToggle,
|
toggle: playbackToggle,
|
||||||
|
|
|
@ -74,7 +74,6 @@ export const useVoiceBroadcastRecording = (recording: VoiceBroadcastRecording) =
|
||||||
|
|
||||||
const live = [
|
const live = [
|
||||||
VoiceBroadcastInfoState.Started,
|
VoiceBroadcastInfoState.Started,
|
||||||
VoiceBroadcastInfoState.Paused,
|
|
||||||
VoiceBroadcastInfoState.Resumed,
|
VoiceBroadcastInfoState.Resumed,
|
||||||
].includes(recordingState);
|
].includes(recordingState);
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ export * from "./utils/VoiceBroadcastResumer";
|
||||||
export const VoiceBroadcastInfoEventType = "io.element.voice_broadcast_info";
|
export const VoiceBroadcastInfoEventType = "io.element.voice_broadcast_info";
|
||||||
export const VoiceBroadcastChunkEventType = "io.element.voice_broadcast_chunk";
|
export const VoiceBroadcastChunkEventType = "io.element.voice_broadcast_chunk";
|
||||||
|
|
||||||
|
export type VoiceBroadcastLiveness = "live" | "not-live" | "grey";
|
||||||
|
|
||||||
export enum VoiceBroadcastInfoState {
|
export enum VoiceBroadcastInfoState {
|
||||||
Started = "started",
|
Started = "started",
|
||||||
Paused = "paused",
|
Paused = "paused",
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { PlaybackManager } from "../../audio/PlaybackManager";
|
||||||
import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
||||||
import { MediaEventHelper } from "../../utils/MediaEventHelper";
|
import { MediaEventHelper } from "../../utils/MediaEventHelper";
|
||||||
import { IDestroyable } from "../../utils/IDestroyable";
|
import { IDestroyable } from "../../utils/IDestroyable";
|
||||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
import { VoiceBroadcastLiveness, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
||||||
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
|
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
|
||||||
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
|
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ export enum VoiceBroadcastPlaybackState {
|
||||||
export enum VoiceBroadcastPlaybackEvent {
|
export enum VoiceBroadcastPlaybackEvent {
|
||||||
PositionChanged = "position_changed",
|
PositionChanged = "position_changed",
|
||||||
LengthChanged = "length_changed",
|
LengthChanged = "length_changed",
|
||||||
|
LivenessChanged = "liveness_changed",
|
||||||
StateChanged = "state_changed",
|
StateChanged = "state_changed",
|
||||||
InfoStateChanged = "info_state_changed",
|
InfoStateChanged = "info_state_changed",
|
||||||
}
|
}
|
||||||
|
@ -51,6 +52,7 @@ export enum VoiceBroadcastPlaybackEvent {
|
||||||
interface EventMap {
|
interface EventMap {
|
||||||
[VoiceBroadcastPlaybackEvent.PositionChanged]: (position: number) => void;
|
[VoiceBroadcastPlaybackEvent.PositionChanged]: (position: number) => void;
|
||||||
[VoiceBroadcastPlaybackEvent.LengthChanged]: (length: number) => void;
|
[VoiceBroadcastPlaybackEvent.LengthChanged]: (length: number) => void;
|
||||||
|
[VoiceBroadcastPlaybackEvent.LivenessChanged]: (liveness: VoiceBroadcastLiveness) => void;
|
||||||
[VoiceBroadcastPlaybackEvent.StateChanged]: (
|
[VoiceBroadcastPlaybackEvent.StateChanged]: (
|
||||||
state: VoiceBroadcastPlaybackState,
|
state: VoiceBroadcastPlaybackState,
|
||||||
playback: VoiceBroadcastPlayback
|
playback: VoiceBroadcastPlayback
|
||||||
|
@ -70,6 +72,7 @@ export class VoiceBroadcastPlayback
|
||||||
/** @var current playback position in milliseconds */
|
/** @var current playback position in milliseconds */
|
||||||
private position = 0;
|
private position = 0;
|
||||||
public readonly liveData = new SimpleObservable<number[]>();
|
public readonly liveData = new SimpleObservable<number[]>();
|
||||||
|
private liveness: VoiceBroadcastLiveness = "not-live";
|
||||||
|
|
||||||
// set vial addInfoEvent() in constructor
|
// set vial addInfoEvent() in constructor
|
||||||
private infoState!: VoiceBroadcastInfoState;
|
private infoState!: VoiceBroadcastInfoState;
|
||||||
|
@ -143,6 +146,7 @@ export class VoiceBroadcastPlayback
|
||||||
|
|
||||||
if (this.getState() === VoiceBroadcastPlaybackState.Buffering) {
|
if (this.getState() === VoiceBroadcastPlaybackState.Buffering) {
|
||||||
await this.start();
|
await this.start();
|
||||||
|
this.updateLiveness();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -212,23 +216,19 @@ export class VoiceBroadcastPlayback
|
||||||
};
|
};
|
||||||
|
|
||||||
private setDuration(duration: number): void {
|
private setDuration(duration: number): void {
|
||||||
const shouldEmit = this.duration !== duration;
|
if (this.duration === duration) return;
|
||||||
this.duration = duration;
|
|
||||||
|
|
||||||
if (shouldEmit) {
|
this.duration = duration;
|
||||||
this.emit(VoiceBroadcastPlaybackEvent.LengthChanged, this.duration);
|
this.emit(VoiceBroadcastPlaybackEvent.LengthChanged, this.duration);
|
||||||
this.liveData.update([this.timeSeconds, this.durationSeconds]);
|
this.liveData.update([this.timeSeconds, this.durationSeconds]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setPosition(position: number): void {
|
private setPosition(position: number): void {
|
||||||
const shouldEmit = this.position !== position;
|
if (this.position === position) return;
|
||||||
this.position = position;
|
|
||||||
|
|
||||||
if (shouldEmit) {
|
this.position = position;
|
||||||
this.emit(VoiceBroadcastPlaybackEvent.PositionChanged, this.position);
|
this.emit(VoiceBroadcastPlaybackEvent.PositionChanged, this.position);
|
||||||
this.liveData.update([this.timeSeconds, this.durationSeconds]);
|
this.liveData.update([this.timeSeconds, this.durationSeconds]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPlaybackStateChange = async (event: MatrixEvent, newState: PlaybackState): Promise<void> => {
|
private onPlaybackStateChange = async (event: MatrixEvent, newState: PlaybackState): Promise<void> => {
|
||||||
|
@ -279,6 +279,42 @@ export class VoiceBroadcastPlayback
|
||||||
return playback;
|
return playback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getLiveness(): VoiceBroadcastLiveness {
|
||||||
|
return this.liveness;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setLiveness(liveness: VoiceBroadcastLiveness): void {
|
||||||
|
if (this.liveness === liveness) return;
|
||||||
|
|
||||||
|
this.liveness = liveness;
|
||||||
|
this.emit(VoiceBroadcastPlaybackEvent.LivenessChanged, liveness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateLiveness(): void {
|
||||||
|
if (this.infoState === VoiceBroadcastInfoState.Stopped) {
|
||||||
|
this.setLiveness("not-live");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.infoState === VoiceBroadcastInfoState.Paused) {
|
||||||
|
this.setLiveness("grey");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([VoiceBroadcastPlaybackState.Stopped, VoiceBroadcastPlaybackState.Paused].includes(this.state)) {
|
||||||
|
this.setLiveness("grey");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentlyPlaying && this.chunkEvents.isLast(this.currentlyPlaying)) {
|
||||||
|
this.setLiveness("live");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setLiveness("grey");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
public get currentState(): PlaybackState {
|
public get currentState(): PlaybackState {
|
||||||
return PlaybackState.Playing;
|
return PlaybackState.Playing;
|
||||||
}
|
}
|
||||||
|
@ -295,7 +331,10 @@ export class VoiceBroadcastPlayback
|
||||||
const time = timeSeconds * 1000;
|
const time = timeSeconds * 1000;
|
||||||
const event = this.chunkEvents.findByTime(time);
|
const event = this.chunkEvents.findByTime(time);
|
||||||
|
|
||||||
if (!event) return;
|
if (!event) {
|
||||||
|
logger.warn("voice broadcast chunk event to skip to not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentPlayback = this.currentlyPlaying
|
const currentPlayback = this.currentlyPlaying
|
||||||
? this.getPlaybackForEvent(this.currentlyPlaying)
|
? this.getPlaybackForEvent(this.currentlyPlaying)
|
||||||
|
@ -304,7 +343,7 @@ export class VoiceBroadcastPlayback
|
||||||
const skipToPlayback = this.getPlaybackForEvent(event);
|
const skipToPlayback = this.getPlaybackForEvent(event);
|
||||||
|
|
||||||
if (!skipToPlayback) {
|
if (!skipToPlayback) {
|
||||||
logger.error("voice broadcast chunk to skip to not found", event);
|
logger.warn("voice broadcast chunk to skip to not found", event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +363,7 @@ export class VoiceBroadcastPlayback
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setPosition(time);
|
this.setPosition(time);
|
||||||
|
this.updateLiveness();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
|
@ -398,6 +438,7 @@ export class VoiceBroadcastPlayback
|
||||||
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state, this);
|
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state, this);
|
||||||
|
this.updateLiveness();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getInfoState(): VoiceBroadcastInfoState {
|
public getInfoState(): VoiceBroadcastInfoState {
|
||||||
|
@ -411,6 +452,7 @@ export class VoiceBroadcastPlayback
|
||||||
|
|
||||||
this.infoState = state;
|
this.infoState = state;
|
||||||
this.emit(VoiceBroadcastPlaybackEvent.InfoStateChanged, state);
|
this.emit(VoiceBroadcastPlaybackEvent.InfoStateChanged, state);
|
||||||
|
this.updateLiveness();
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
|
|
|
@ -93,6 +93,10 @@ export class VoiceBroadcastChunkEvents {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isLast(event: MatrixEvent): boolean {
|
||||||
|
return this.events.indexOf(event) >= this.events.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
private calculateChunkLength(event: MatrixEvent): number {
|
private calculateChunkLength(event: MatrixEvent): number {
|
||||||
return event.getContent()?.["org.matrix.msc1767.audio"]?.duration
|
return event.getContent()?.["org.matrix.msc1767.audio"]?.duration
|
||||||
|| event.getContent()?.info?.duration
|
|| event.getContent()?.info?.duration
|
||||||
|
|
|
@ -20,8 +20,13 @@ import { render } from "@testing-library/react";
|
||||||
import { LiveBadge } from "../../../../src/voice-broadcast";
|
import { LiveBadge } from "../../../../src/voice-broadcast";
|
||||||
|
|
||||||
describe("LiveBadge", () => {
|
describe("LiveBadge", () => {
|
||||||
it("should render the expected HTML", () => {
|
it("should render as expected with default props", () => {
|
||||||
const { container } = render(<LiveBadge />);
|
const { container } = render(<LiveBadge />);
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render in grey as expected", () => {
|
||||||
|
const { container } = render(<LiveBadge grey={true} />);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Container } from "react-dom";
|
||||||
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
import { render, RenderResult } from "@testing-library/react";
|
import { render, RenderResult } from "@testing-library/react";
|
||||||
|
|
||||||
import { VoiceBroadcastHeader } from "../../../../src/voice-broadcast";
|
import { VoiceBroadcastHeader, VoiceBroadcastLiveness } from "../../../../src/voice-broadcast";
|
||||||
import { mkRoom, stubClient } from "../../../test-utils";
|
import { mkRoom, stubClient } from "../../../test-utils";
|
||||||
|
|
||||||
// mock RoomAvatar, because it is doing too much fancy stuff
|
// mock RoomAvatar, because it is doing too much fancy stuff
|
||||||
|
@ -35,7 +35,7 @@ describe("VoiceBroadcastHeader", () => {
|
||||||
const sender = new RoomMember(roomId, userId);
|
const sender = new RoomMember(roomId, userId);
|
||||||
let container: Container;
|
let container: Container;
|
||||||
|
|
||||||
const renderHeader = (live: boolean, showBroadcast: boolean = undefined): RenderResult => {
|
const renderHeader = (live: VoiceBroadcastLiveness, showBroadcast: boolean = undefined): RenderResult => {
|
||||||
return render(<VoiceBroadcastHeader
|
return render(<VoiceBroadcastHeader
|
||||||
live={live}
|
live={live}
|
||||||
microphoneLabel={sender.name}
|
microphoneLabel={sender.name}
|
||||||
|
@ -52,17 +52,27 @@ describe("VoiceBroadcastHeader", () => {
|
||||||
|
|
||||||
describe("when rendering a live broadcast header with broadcast info", () => {
|
describe("when rendering a live broadcast header with broadcast info", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
container = renderHeader(true, true).container;
|
container = renderHeader("live", true).container;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render the header with a live badge", () => {
|
it("should render the header with a red live badge", () => {
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when rendering a live (grey) broadcast header with broadcast info", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
container = renderHeader("grey", true).container;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the header with a grey live badge", () => {
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when rendering a non-live broadcast header", () => {
|
describe("when rendering a non-live broadcast header", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
container = renderHeader(false).container;
|
container = renderHeader("not-live").container;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render the header without a live badge", () => {
|
it("should render the header without a live badge", () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`LiveBadge should render the expected HTML 1`] = `
|
exports[`LiveBadge should render as expected with default props 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_LiveBadge"
|
class="mx_LiveBadge"
|
||||||
|
@ -12,3 +12,16 @@ exports[`LiveBadge should render the expected HTML 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`LiveBadge should render in grey as expected 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_LiveBadge mx_LiveBadge--grey"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Icon mx_Icon_16"
|
||||||
|
/>
|
||||||
|
Live
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
|
@ -1,6 +1,56 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadcast info should render the header with a live badge 1`] = `
|
exports[`VoiceBroadcastHeader when rendering a live (grey) broadcast header with broadcast info should render the header with a grey live badge 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="room-avatar"
|
||||||
|
>
|
||||||
|
room avatar:
|
||||||
|
!room:example.com
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_room"
|
||||||
|
>
|
||||||
|
!room:example.com
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_line"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Icon mx_Icon_16"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
test user
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_line"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Icon mx_Icon_16"
|
||||||
|
/>
|
||||||
|
Voice broadcast
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_LiveBadge mx_LiveBadge--grey"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Icon mx_Icon_16"
|
||||||
|
/>
|
||||||
|
Live
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadcast info should render the header with a red live badge 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_VoiceBroadcastHeader"
|
class="mx_VoiceBroadcastHeader"
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { mocked } from "jest-mock";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
VoiceBroadcastInfoState,
|
VoiceBroadcastInfoState,
|
||||||
|
VoiceBroadcastLiveness,
|
||||||
VoiceBroadcastPlayback,
|
VoiceBroadcastPlayback,
|
||||||
VoiceBroadcastPlaybackBody,
|
VoiceBroadcastPlaybackBody,
|
||||||
VoiceBroadcastPlaybackEvent,
|
VoiceBroadcastPlaybackEvent,
|
||||||
|
@ -62,6 +63,7 @@ describe("VoiceBroadcastPlaybackBody", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
playback = new VoiceBroadcastPlayback(infoEvent, client);
|
playback = new VoiceBroadcastPlayback(infoEvent, client);
|
||||||
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
|
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
|
||||||
|
jest.spyOn(playback, "getLiveness");
|
||||||
jest.spyOn(playback, "getState");
|
jest.spyOn(playback, "getState");
|
||||||
jest.spyOn(playback, "durationSeconds", "get").mockReturnValue(23 * 60 + 42); // 23:42
|
jest.spyOn(playback, "durationSeconds", "get").mockReturnValue(23 * 60 + 42); // 23:42
|
||||||
});
|
});
|
||||||
|
@ -69,6 +71,7 @@ describe("VoiceBroadcastPlaybackBody", () => {
|
||||||
describe("when rendering a buffering voice broadcast", () => {
|
describe("when rendering a buffering voice broadcast", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Buffering);
|
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Buffering);
|
||||||
|
mocked(playback.getLiveness).mockReturnValue("live");
|
||||||
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
|
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,6 +83,7 @@ describe("VoiceBroadcastPlaybackBody", () => {
|
||||||
describe(`when rendering a stopped broadcast`, () => {
|
describe(`when rendering a stopped broadcast`, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Stopped);
|
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Stopped);
|
||||||
|
mocked(playback.getLiveness).mockReturnValue("not-live");
|
||||||
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
|
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -107,11 +111,12 @@ describe("VoiceBroadcastPlaybackBody", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
VoiceBroadcastPlaybackState.Paused,
|
[VoiceBroadcastPlaybackState.Paused, "not-live"],
|
||||||
VoiceBroadcastPlaybackState.Playing,
|
[VoiceBroadcastPlaybackState.Playing, "live"],
|
||||||
])("when rendering a %s broadcast", (playbackState: VoiceBroadcastPlaybackState) => {
|
])("when rendering a %s/%s broadcast", (state: VoiceBroadcastPlaybackState, liveness: VoiceBroadcastLiveness) => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocked(playback.getState).mockReturnValue(playbackState);
|
mocked(playback.getState).mockReturnValue(state);
|
||||||
|
mocked(playback.getLiveness).mockReturnValue(liveness);
|
||||||
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
|
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -60,21 +60,21 @@ describe("VoiceBroadcastRecordingBody", () => {
|
||||||
renderResult = render(<VoiceBroadcastRecordingBody recording={recording} />);
|
renderResult = render(<VoiceBroadcastRecordingBody recording={recording} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render the expected HTML", () => {
|
it("should render with a red live badge", () => {
|
||||||
expect(renderResult.container).toMatchSnapshot();
|
expect(renderResult.container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when rendering a non-live broadcast", () => {
|
describe("when rendering a paused broadcast", () => {
|
||||||
let renderResult: RenderResult;
|
let renderResult: RenderResult;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
recording.stop();
|
await recording.pause();
|
||||||
renderResult = render(<VoiceBroadcastRecordingBody recording={recording} />);
|
renderResult = render(<VoiceBroadcastRecordingBody recording={recording} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not render the live badge", () => {
|
it("should render with a grey live badge", () => {
|
||||||
expect(renderResult.queryByText("Live")).toBeFalsy();
|
expect(renderResult.container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render as expected 1`] = `
|
exports[`VoiceBroadcastPlaybackBody when rendering a 0/not-live broadcast should render as expected 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_VoiceBroadcastBody"
|
class="mx_VoiceBroadcastBody"
|
||||||
|
@ -41,14 +41,6 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render a
|
||||||
Voice broadcast
|
Voice broadcast
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="mx_LiveBadge"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Icon mx_Icon_16"
|
|
||||||
/>
|
|
||||||
Live
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_VoiceBroadcastBody_controls"
|
class="mx_VoiceBroadcastBody_controls"
|
||||||
|
@ -87,7 +79,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render a
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render as expected 1`] = `
|
exports[`VoiceBroadcastPlaybackBody when rendering a 1/live broadcast should render as expected 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_VoiceBroadcastBody"
|
class="mx_VoiceBroadcastBody"
|
||||||
|
@ -303,14 +295,6 @@ exports[`VoiceBroadcastPlaybackBody when rendering a stopped broadcast and the l
|
||||||
Voice broadcast
|
Voice broadcast
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="mx_LiveBadge"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Icon mx_Icon_16"
|
|
||||||
/>
|
|
||||||
Live
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_VoiceBroadcastBody_controls"
|
class="mx_VoiceBroadcastBody_controls"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should render the expected HTML 1`] = `
|
exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should render with a red live badge 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_VoiceBroadcastBody"
|
class="mx_VoiceBroadcastBody"
|
||||||
|
@ -45,3 +45,49 @@ exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should rend
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`VoiceBroadcastRecordingBody when rendering a paused broadcast should render with a grey live badge 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastBody"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="room-avatar"
|
||||||
|
>
|
||||||
|
room avatar:
|
||||||
|
My room
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_room"
|
||||||
|
>
|
||||||
|
My room
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_VoiceBroadcastHeader_line"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Icon mx_Icon_16"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
@user:example.com
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_LiveBadge mx_LiveBadge--grey"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Icon mx_Icon_16"
|
||||||
|
/>
|
||||||
|
Live
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
|
@ -36,7 +36,7 @@ exports[`VoiceBroadcastRecordingPip when rendering a paused recording should ren
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_LiveBadge"
|
class="mx_LiveBadge mx_LiveBadge--grey"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Icon mx_Icon_16"
|
class="mx_Icon mx_Icon_16"
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { RelationsHelperEvent } from "../../../src/events/RelationsHelper";
|
||||||
import { MediaEventHelper } from "../../../src/utils/MediaEventHelper";
|
import { MediaEventHelper } from "../../../src/utils/MediaEventHelper";
|
||||||
import {
|
import {
|
||||||
VoiceBroadcastInfoState,
|
VoiceBroadcastInfoState,
|
||||||
|
VoiceBroadcastLiveness,
|
||||||
VoiceBroadcastPlayback,
|
VoiceBroadcastPlayback,
|
||||||
VoiceBroadcastPlaybackEvent,
|
VoiceBroadcastPlaybackEvent,
|
||||||
VoiceBroadcastPlaybackState,
|
VoiceBroadcastPlaybackState,
|
||||||
|
@ -76,6 +77,12 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const itShouldHaveLiveness = (liveness: VoiceBroadcastLiveness): void => {
|
||||||
|
it(`should have liveness ${liveness}`, () => {
|
||||||
|
expect(playback.getLiveness()).toBe(liveness);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const startPlayback = () => {
|
const startPlayback = () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await playback.start();
|
await playback.start();
|
||||||
|
@ -187,6 +194,8 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
describe("and calling start", () => {
|
describe("and calling start", () => {
|
||||||
startPlayback();
|
startPlayback();
|
||||||
|
|
||||||
|
itShouldHaveLiveness("grey");
|
||||||
|
|
||||||
it("should be in buffering state", () => {
|
it("should be in buffering state", () => {
|
||||||
expect(playback.getState()).toBe(VoiceBroadcastPlaybackState.Buffering);
|
expect(playback.getState()).toBe(VoiceBroadcastPlaybackState.Buffering);
|
||||||
});
|
});
|
||||||
|
@ -223,6 +232,7 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Playing);
|
itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Playing);
|
||||||
|
itShouldHaveLiveness("live");
|
||||||
|
|
||||||
it("should update the duration", () => {
|
it("should update the duration", () => {
|
||||||
expect(playback.durationSeconds).toBe(2.3);
|
expect(playback.durationSeconds).toBe(2.3);
|
||||||
|
|
Loading…
Reference in a new issue