Merge pull request #367 from robintown/vu-animation

Add a VU meter-style animation to radio mode
This commit is contained in:
Robin 2022-06-01 10:42:07 -04:00 committed by GitHub
commit bab5c9aa42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 20 deletions

View file

@ -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;

View file

@ -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);
}

View file

@ -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>
);
};

View file

@ -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}

View file

@ -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 {
setState((prevState) => ({
...prevState,
activeSpeakerUserId: activeSpeakerFeed
? activeSpeakerFeed.userId
: null,
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,