Merge pull request #367 from robintown/vu-animation
Add a VU meter-style animation to radio mode
This commit is contained in:
commit
bab5c9aa42
5 changed files with 59 additions and 20 deletions
|
@ -26,6 +26,8 @@ limitations under the License.
|
|||
--inter-unicode-range: U+0000-20e2, U+20e4-23ce, U+23d0-24c1, U+24c3-259f,
|
||||
U+25c2-2664, U+2666-2763, U+2765-2b05, U+2b07-2b1b, U+2b1d-10FFFF;
|
||||
--primaryColor: #0dbd8b;
|
||||
--primaryColor-20: #0dbd8b33;
|
||||
--alert-20: #ff5b5533;
|
||||
--bgColor1: #15191e;
|
||||
--bgColor2: #21262c;
|
||||
--bgColor3: #444;
|
||||
|
|
|
@ -14,14 +14,10 @@
|
|||
|
||||
.talking {
|
||||
background-color: #0dbd8b;
|
||||
box-shadow: 0px 0px 0px 17px rgba(13, 189, 139, 0.2),
|
||||
0px 0px 0px 34px rgba(13, 189, 139, 0.2);
|
||||
cursor: unset;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #ff5b55;
|
||||
border-color: #ff5b55;
|
||||
box-shadow: 0px 0px 0px 17px rgba(255, 91, 85, 0.2),
|
||||
0px 0px 0px 34px rgba(255, 91, 85, 0.2);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React, { useCallback, useEffect, useState, createRef } from "react";
|
||||
import classNames from "classnames";
|
||||
import { useSpring, animated } from "@react-spring/web";
|
||||
|
||||
import styles from "./PTTButton.module.css";
|
||||
import { ReactComponent as MicIcon } from "../icons/Mic.svg";
|
||||
|
@ -27,6 +28,7 @@ interface Props {
|
|||
activeSpeakerDisplayName: string;
|
||||
activeSpeakerAvatarUrl: string;
|
||||
activeSpeakerIsLocalUser: boolean;
|
||||
activeSpeakerVolume: number;
|
||||
size: number;
|
||||
startTalking: () => void;
|
||||
stopTalking: () => void;
|
||||
|
@ -44,6 +46,7 @@ export const PTTButton: React.FC<Props> = ({
|
|||
activeSpeakerDisplayName,
|
||||
activeSpeakerAvatarUrl,
|
||||
activeSpeakerIsLocalUser,
|
||||
activeSpeakerVolume,
|
||||
size,
|
||||
startTalking,
|
||||
stopTalking,
|
||||
|
@ -130,12 +133,32 @@ export const PTTButton: React.FC<Props> = ({
|
|||
);
|
||||
};
|
||||
}, [onWindowMouseUp, onWindowTouchEnd, onButtonTouchStart, buttonRef]);
|
||||
|
||||
const { shadow } = useSpring({
|
||||
shadow: (Math.max(activeSpeakerVolume, -70) + 70) * 0.6,
|
||||
config: {
|
||||
clamp: true,
|
||||
tension: 300,
|
||||
},
|
||||
});
|
||||
const shadowColor = showTalkOverError
|
||||
? "var(--alert-20)"
|
||||
: "var(--primaryColor-20)";
|
||||
|
||||
return (
|
||||
<button
|
||||
<animated.button
|
||||
className={classNames(styles.pttButton, {
|
||||
[styles.talking]: activeSpeakerUserId,
|
||||
[styles.error]: showTalkOverError,
|
||||
})}
|
||||
style={{
|
||||
boxShadow: shadow.to(
|
||||
(s) =>
|
||||
`0px 0px 0px ${s}px ${shadowColor}, 0px 0px 0px ${
|
||||
2 * s
|
||||
}px ${shadowColor}`
|
||||
),
|
||||
}}
|
||||
onMouseDown={onButtonMouseDown}
|
||||
ref={buttonRef}
|
||||
>
|
||||
|
@ -154,6 +177,6 @@ export const PTTButton: React.FC<Props> = ({
|
|||
className={styles.avatar}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</animated.button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -123,6 +123,7 @@ export const PTTCallView: React.FC<Props> = ({
|
|||
talkOverEnabled,
|
||||
setTalkOverEnabled,
|
||||
activeSpeakerUserId,
|
||||
activeSpeakerVolume,
|
||||
startTalking,
|
||||
stopTalking,
|
||||
transmitBlocked,
|
||||
|
@ -221,6 +222,7 @@ export const PTTCallView: React.FC<Props> = ({
|
|||
activeSpeakerDisplayName={activeSpeakerDisplayName}
|
||||
activeSpeakerAvatarUrl={activeSpeakerAvatarUrl}
|
||||
activeSpeakerIsLocalUser={activeSpeakerIsLocalUser}
|
||||
activeSpeakerVolume={activeSpeakerVolume}
|
||||
size={pttButtonSize}
|
||||
startTalking={startTalking}
|
||||
stopTalking={stopTalking}
|
||||
|
|
|
@ -65,6 +65,7 @@ export interface PTTState {
|
|||
talkOverEnabled: boolean;
|
||||
setTalkOverEnabled: (boolean) => void;
|
||||
activeSpeakerUserId: string;
|
||||
activeSpeakerVolume: number;
|
||||
startTalking: () => void;
|
||||
stopTalking: () => void;
|
||||
transmitBlocked: boolean;
|
||||
|
@ -108,6 +109,7 @@ export const usePTT = (
|
|||
isAdmin,
|
||||
talkOverEnabled,
|
||||
activeSpeakerUserId,
|
||||
activeSpeakerVolume,
|
||||
transmitBlocked,
|
||||
},
|
||||
setState,
|
||||
|
@ -121,6 +123,7 @@ export const usePTT = (
|
|||
talkOverEnabled: false,
|
||||
pttButtonHeld: false,
|
||||
activeSpeakerUserId: activeSpeakerFeed ? activeSpeakerFeed.userId : null,
|
||||
activeSpeakerVolume: -Infinity,
|
||||
transmitBlocked: false,
|
||||
};
|
||||
});
|
||||
|
@ -152,15 +155,11 @@ export const usePTT = (
|
|||
playClip(PTTClipID.BLOCKED);
|
||||
}
|
||||
|
||||
setState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
activeSpeakerUserId: activeSpeakerFeed
|
||||
? activeSpeakerFeed.userId
|
||||
: null,
|
||||
transmitBlocked: blocked,
|
||||
};
|
||||
});
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
activeSpeakerUserId: activeSpeakerFeed ? activeSpeakerFeed.userId : null,
|
||||
transmitBlocked: blocked,
|
||||
}));
|
||||
}, [
|
||||
playClip,
|
||||
groupCall,
|
||||
|
@ -173,7 +172,7 @@ export const usePTT = (
|
|||
|
||||
useEffect(() => {
|
||||
for (const callFeed of userMediaFeeds) {
|
||||
callFeed.addListener(CallFeedEvent.MuteStateChanged, onMuteStateChanged);
|
||||
callFeed.on(CallFeedEvent.MuteStateChanged, onMuteStateChanged);
|
||||
}
|
||||
|
||||
const activeSpeakerFeed = getActiveSpeakerFeed(userMediaFeeds, groupCall);
|
||||
|
@ -185,14 +184,30 @@ export const usePTT = (
|
|||
|
||||
return () => {
|
||||
for (const callFeed of userMediaFeeds) {
|
||||
callFeed.removeListener(
|
||||
CallFeedEvent.MuteStateChanged,
|
||||
onMuteStateChanged
|
||||
);
|
||||
callFeed.off(CallFeedEvent.MuteStateChanged, onMuteStateChanged);
|
||||
}
|
||||
};
|
||||
}, [userMediaFeeds, onMuteStateChanged, groupCall]);
|
||||
|
||||
const onVolumeChanged = useCallback((volume: number) => {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
activeSpeakerVolume: volume,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const activeSpeakerFeed = getActiveSpeakerFeed(userMediaFeeds, groupCall);
|
||||
activeSpeakerFeed?.on(CallFeedEvent.VolumeChanged, onVolumeChanged);
|
||||
return () => {
|
||||
activeSpeakerFeed?.off(CallFeedEvent.VolumeChanged, onVolumeChanged);
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
activeSpeakerVolume: -Infinity,
|
||||
}));
|
||||
};
|
||||
}, [activeSpeakerUserId, onVolumeChanged, userMediaFeeds, groupCall]);
|
||||
|
||||
const startTalking = useCallback(async () => {
|
||||
if (pttButtonHeld) return;
|
||||
|
||||
|
@ -317,6 +332,7 @@ export const usePTT = (
|
|||
talkOverEnabled,
|
||||
setTalkOverEnabled,
|
||||
activeSpeakerUserId,
|
||||
activeSpeakerVolume,
|
||||
startTalking,
|
||||
stopTalking,
|
||||
transmitBlocked,
|
||||
|
|
Loading…
Reference in a new issue