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

Commit cf3c899

Browse files
authored
Tweak voice broadcast live icon (#9576)
1 parent 973513c commit cf3c899

21 files changed

+261
-72
lines changed

res/css/voice-broadcast/atoms/_LiveBadge.pcss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ limitations under the License.
2525
gap: $spacing-4;
2626
padding: 2px 4px;
2727
}
28+
29+
.mx_LiveBadge--grey {
30+
background-color: $quaternary-content;
31+
}

src/voice-broadcast/components/atoms/LiveBadge.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,27 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
import classNames from "classnames";
1718
import React from "react";
1819

1920
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
2021
import { _t } from "../../../languageHandler";
2122

22-
export const LiveBadge: React.FC = () => {
23-
return <div className="mx_LiveBadge">
23+
interface Props {
24+
grey?: boolean;
25+
}
26+
27+
export const LiveBadge: React.FC<Props> = ({
28+
grey = false,
29+
}) => {
30+
const liveBadgeClasses = classNames(
31+
"mx_LiveBadge",
32+
{
33+
"mx_LiveBadge--grey": grey,
34+
},
35+
);
36+
37+
return <div className={liveBadgeClasses}>
2438
<LiveIcon className="mx_Icon mx_Icon_16" />
2539
{ _t("Live") }
2640
</div>;

src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import React from "react";
1515
import { Room } from "matrix-js-sdk/src/matrix";
1616
import classNames from "classnames";
1717

18-
import { LiveBadge } from "../..";
18+
import { LiveBadge, VoiceBroadcastLiveness } from "../..";
1919
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
2020
import { Icon as MicrophoneIcon } from "../../../../res/img/voip/call-view/mic-on.svg";
2121
import { Icon as TimerIcon } from "../../../../res/img/element-icons/Timer.svg";
@@ -27,7 +27,7 @@ import Clock from "../../../components/views/audio_messages/Clock";
2727
import { formatTimeLeft } from "../../../DateUtils";
2828

2929
interface VoiceBroadcastHeaderProps {
30-
live?: boolean;
30+
live?: VoiceBroadcastLiveness;
3131
onCloseClick?: () => void;
3232
onMicrophoneLineClick?: () => void;
3333
room: Room;
@@ -38,7 +38,7 @@ interface VoiceBroadcastHeaderProps {
3838
}
3939

4040
export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
41-
live = false,
41+
live = "not-live",
4242
onCloseClick = () => {},
4343
onMicrophoneLineClick,
4444
room,
@@ -54,7 +54,9 @@ export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
5454
</div>
5555
: null;
5656

57-
const liveBadge = live ? <LiveBadge /> : null;
57+
const liveBadge = live === "not-live"
58+
? null
59+
: <LiveBadge grey={live === "grey"} />;
5860

5961
const closeButton = showClose
6062
? <AccessibleButton onClick={onCloseClick}>

src/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
3939
}) => {
4040
const {
4141
duration,
42-
live,
42+
liveness,
4343
room,
4444
sender,
4545
toggle,
@@ -79,7 +79,7 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
7979
return (
8080
<div className="mx_VoiceBroadcastBody">
8181
<VoiceBroadcastHeader
82-
live={live}
82+
live={liveness}
8383
microphoneLabel={sender?.name}
8484
room={room}
8585
showBroadcast={true}

src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const VoiceBroadcastRecordingBody: React.FC<VoiceBroadcastRecordingBodyPr
2929
return (
3030
<div className="mx_VoiceBroadcastBody">
3131
<VoiceBroadcastHeader
32-
live={live}
32+
live={live ? "live" : "grey"}
3333
microphoneLabel={sender?.name}
3434
room={room}
3535
/>

src/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const VoiceBroadcastRecordingPip: React.FC<VoiceBroadcastRecordingPipProp
5555
className="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
5656
>
5757
<VoiceBroadcastHeader
58-
live={live}
58+
live={live ? "live" : "grey"}
5959
room={room}
6060
timeLeft={timeLeft}
6161
/>

src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { useState } from "react";
1919
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
2020
import { MatrixClientPeg } from "../../MatrixClientPeg";
2121
import {
22-
VoiceBroadcastInfoState,
2322
VoiceBroadcastPlayback,
2423
VoiceBroadcastPlaybackEvent,
2524
VoiceBroadcastPlaybackState,
@@ -41,23 +40,23 @@ export const useVoiceBroadcastPlayback = (playback: VoiceBroadcastPlayback) => {
4140
},
4241
);
4342

44-
const [playbackInfoState, setPlaybackInfoState] = useState(playback.getInfoState());
43+
const [duration, setDuration] = useState(playback.durationSeconds);
4544
useTypedEventEmitter(
4645
playback,
47-
VoiceBroadcastPlaybackEvent.InfoStateChanged,
48-
setPlaybackInfoState,
46+
VoiceBroadcastPlaybackEvent.LengthChanged,
47+
d => setDuration(d / 1000),
4948
);
5049

51-
const [duration, setDuration] = useState(playback.durationSeconds);
50+
const [liveness, setLiveness] = useState(playback.getLiveness());
5251
useTypedEventEmitter(
5352
playback,
54-
VoiceBroadcastPlaybackEvent.LengthChanged,
55-
d => setDuration(d / 1000),
53+
VoiceBroadcastPlaybackEvent.LivenessChanged,
54+
l => setLiveness(l),
5655
);
5756

5857
return {
5958
duration,
60-
live: playbackInfoState !== VoiceBroadcastInfoState.Stopped,
59+
liveness: liveness,
6160
room: room,
6261
sender: playback.infoEvent.sender,
6362
toggle: playbackToggle,

src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export const useVoiceBroadcastRecording = (recording: VoiceBroadcastRecording) =
7474

7575
const live = [
7676
VoiceBroadcastInfoState.Started,
77-
VoiceBroadcastInfoState.Paused,
7877
VoiceBroadcastInfoState.Resumed,
7978
].includes(recordingState);
8079

src/voice-broadcast/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export * from "./utils/VoiceBroadcastResumer";
5252
export const VoiceBroadcastInfoEventType = "io.element.voice_broadcast_info";
5353
export const VoiceBroadcastChunkEventType = "io.element.voice_broadcast_chunk";
5454

55+
export type VoiceBroadcastLiveness = "live" | "not-live" | "grey";
56+
5557
export enum VoiceBroadcastInfoState {
5658
Started = "started",
5759
Paused = "paused",

src/voice-broadcast/models/VoiceBroadcastPlayback.ts

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { PlaybackManager } from "../../audio/PlaybackManager";
3030
import { UPDATE_EVENT } from "../../stores/AsyncStore";
3131
import { MediaEventHelper } from "../../utils/MediaEventHelper";
3232
import { IDestroyable } from "../../utils/IDestroyable";
33-
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
33+
import { VoiceBroadcastLiveness, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
3434
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
3535
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
3636

@@ -44,13 +44,15 @@ export enum VoiceBroadcastPlaybackState {
4444
export enum VoiceBroadcastPlaybackEvent {
4545
PositionChanged = "position_changed",
4646
LengthChanged = "length_changed",
47+
LivenessChanged = "liveness_changed",
4748
StateChanged = "state_changed",
4849
InfoStateChanged = "info_state_changed",
4950
}
5051

5152
interface EventMap {
5253
[VoiceBroadcastPlaybackEvent.PositionChanged]: (position: number) => void;
5354
[VoiceBroadcastPlaybackEvent.LengthChanged]: (length: number) => void;
55+
[VoiceBroadcastPlaybackEvent.LivenessChanged]: (liveness: VoiceBroadcastLiveness) => void;
5456
[VoiceBroadcastPlaybackEvent.StateChanged]: (
5557
state: VoiceBroadcastPlaybackState,
5658
playback: VoiceBroadcastPlayback
@@ -70,6 +72,7 @@ export class VoiceBroadcastPlayback
7072
/** @var current playback position in milliseconds */
7173
private position = 0;
7274
public readonly liveData = new SimpleObservable<number[]>();
75+
private liveness: VoiceBroadcastLiveness = "not-live";
7376

7477
// set vial addInfoEvent() in constructor
7578
private infoState!: VoiceBroadcastInfoState;
@@ -143,6 +146,7 @@ export class VoiceBroadcastPlayback
143146

144147
if (this.getState() === VoiceBroadcastPlaybackState.Buffering) {
145148
await this.start();
149+
this.updateLiveness();
146150
}
147151

148152
return true;
@@ -212,23 +216,19 @@ export class VoiceBroadcastPlayback
212216
};
213217

214218
private setDuration(duration: number): void {
215-
const shouldEmit = this.duration !== duration;
216-
this.duration = duration;
219+
if (this.duration === duration) return;
217220

218-
if (shouldEmit) {
219-
this.emit(VoiceBroadcastPlaybackEvent.LengthChanged, this.duration);
220-
this.liveData.update([this.timeSeconds, this.durationSeconds]);
221-
}
221+
this.duration = duration;
222+
this.emit(VoiceBroadcastPlaybackEvent.LengthChanged, this.duration);
223+
this.liveData.update([this.timeSeconds, this.durationSeconds]);
222224
}
223225

224226
private setPosition(position: number): void {
225-
const shouldEmit = this.position !== position;
226-
this.position = position;
227+
if (this.position === position) return;
227228

228-
if (shouldEmit) {
229-
this.emit(VoiceBroadcastPlaybackEvent.PositionChanged, this.position);
230-
this.liveData.update([this.timeSeconds, this.durationSeconds]);
231-
}
229+
this.position = position;
230+
this.emit(VoiceBroadcastPlaybackEvent.PositionChanged, this.position);
231+
this.liveData.update([this.timeSeconds, this.durationSeconds]);
232232
}
233233

234234
private onPlaybackStateChange = async (event: MatrixEvent, newState: PlaybackState): Promise<void> => {
@@ -279,6 +279,42 @@ export class VoiceBroadcastPlayback
279279
return playback;
280280
}
281281

282+
public getLiveness(): VoiceBroadcastLiveness {
283+
return this.liveness;
284+
}
285+
286+
private setLiveness(liveness: VoiceBroadcastLiveness): void {
287+
if (this.liveness === liveness) return;
288+
289+
this.liveness = liveness;
290+
this.emit(VoiceBroadcastPlaybackEvent.LivenessChanged, liveness);
291+
}
292+
293+
private updateLiveness(): void {
294+
if (this.infoState === VoiceBroadcastInfoState.Stopped) {
295+
this.setLiveness("not-live");
296+
return;
297+
}
298+
299+
if (this.infoState === VoiceBroadcastInfoState.Paused) {
300+
this.setLiveness("grey");
301+
return;
302+
}
303+
304+
if ([VoiceBroadcastPlaybackState.Stopped, VoiceBroadcastPlaybackState.Paused].includes(this.state)) {
305+
this.setLiveness("grey");
306+
return;
307+
}
308+
309+
if (this.currentlyPlaying && this.chunkEvents.isLast(this.currentlyPlaying)) {
310+
this.setLiveness("live");
311+
return;
312+
}
313+
314+
this.setLiveness("grey");
315+
return;
316+
}
317+
282318
public get currentState(): PlaybackState {
283319
return PlaybackState.Playing;
284320
}
@@ -295,7 +331,10 @@ export class VoiceBroadcastPlayback
295331
const time = timeSeconds * 1000;
296332
const event = this.chunkEvents.findByTime(time);
297333

298-
if (!event) return;
334+
if (!event) {
335+
logger.warn("voice broadcast chunk event to skip to not found");
336+
return;
337+
}
299338

300339
const currentPlayback = this.currentlyPlaying
301340
? this.getPlaybackForEvent(this.currentlyPlaying)
@@ -304,7 +343,7 @@ export class VoiceBroadcastPlayback
304343
const skipToPlayback = this.getPlaybackForEvent(event);
305344

306345
if (!skipToPlayback) {
307-
logger.error("voice broadcast chunk to skip to not found", event);
346+
logger.warn("voice broadcast chunk to skip to not found", event);
308347
return;
309348
}
310349

@@ -324,6 +363,7 @@ export class VoiceBroadcastPlayback
324363
}
325364

326365
this.setPosition(time);
366+
this.updateLiveness();
327367
}
328368

329369
public async start(): Promise<void> {
@@ -398,6 +438,7 @@ export class VoiceBroadcastPlayback
398438

399439
this.state = state;
400440
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state, this);
441+
this.updateLiveness();
401442
}
402443

403444
public getInfoState(): VoiceBroadcastInfoState {
@@ -411,6 +452,7 @@ export class VoiceBroadcastPlayback
411452

412453
this.infoState = state;
413454
this.emit(VoiceBroadcastPlaybackEvent.InfoStateChanged, state);
455+
this.updateLiveness();
414456
}
415457

416458
public destroy(): void {

0 commit comments

Comments
 (0)