зеркало из https://github.com/mozilla/hubs.git
Add VoiceButtonContainer logic
This commit is contained in:
Родитель
adfc22bc34
Коммит
8ecd1a2ac2
|
@ -35,7 +35,10 @@ module.exports = {
|
|||
template: require("../src/react-components/icons/IconTemplate"),
|
||||
svgoConfig: {
|
||||
plugins: {
|
||||
removeViewBox: false
|
||||
removeViewBox: false,
|
||||
mergePaths: false,
|
||||
convertShapeToPath: false,
|
||||
removeHiddenElems: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ AFRAME.registerComponent("mute-mic", {
|
|||
if (!NAF.connection.adapter) return;
|
||||
if (!this.el.sceneEl.is("entered")) return;
|
||||
|
||||
this.el.sceneEl.systems["hubs-systems"].soundEffectsSystem.playSoundOneShot(SOUND_TOGGLE_MIC);
|
||||
if (this.el.is("muted")) {
|
||||
NAF.connection.adapter.enableMicrophone(true);
|
||||
this.el.removeState("muted");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 17.5006H12.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="8" y="8.69995" width="4" height="0" fill="#7ED320"/>
|
||||
<path d="M15 8.12573V9.37573C15 12.1257 12.75 14.3757 10 14.3757C7.25 14.3757 5 12.1257 5 9.37573V8.12573" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.99976 14.3761V17.5011" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 2.50028C9.67126 2.49862 9.34543 2.56215 9.04137 2.6872C8.7373 2.81225 8.46105 2.99634 8.22857 3.22882C7.99609 3.46129 7.81201 3.73755 7.68696 4.04161C7.56191 4.34567 7.49838 4.67151 7.50003 5.00028V9.33621C7.50003 10.7112 8.63284 11.8753 10 11.8753C11.3672 11.8753 12.5 10.7425 12.5 9.33621V5.00028C12.5 3.59403 11.4063 2.50028 10 2.50028Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
|
До Ширина: | Высота: | Размер: 959 B После Ширина: | Высота: | Размер: 1021 B |
|
@ -1,5 +1,6 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 17.5006H12.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="8" y="8.69995" width="4" height="0" fill="#7ED320"/>
|
||||
<path d="M15 8.12573V9.37573C15 12.1257 12.75 14.3757 10 14.3757C7.25 14.3757 5 12.1257 5 9.37573V8.12573" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.99976 14.3761V17.5011" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 2.50028C9.67126 2.49862 9.34543 2.56215 9.04137 2.6872C8.7373 2.81225 8.46105 2.99634 8.22857 3.22882C7.99609 3.46129 7.81201 3.73755 7.68696 4.04161C7.56191 4.34567 7.49838 4.67151 7.50003 5.00028V9.33621C7.50003 10.7112 8.63284 11.8753 10 11.8753C11.3672 11.8753 12.5 10.7425 12.5 9.33621V5.00028C12.5 3.59403 11.4063 2.50028 10 2.50028Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { MicSetupModal } from "./MicSetupModal";
|
||||
import { useMicrophoneVolume } from "./useMicrophoneVolume";
|
||||
import { useMicrophone } from "./useMicrophone";
|
||||
import { useSound } from "./useSound";
|
||||
import webmSrc from "../../assets/sfx/tone.webm";
|
||||
import mp3Src from "../../assets/sfx/tone.mp3";
|
||||
|
@ -9,7 +9,7 @@ import oggSrc from "../../assets/sfx/tone.ogg";
|
|||
import wavSrc from "../../assets/sfx/tone.wav";
|
||||
|
||||
export function MicSetupModalContainer({ scene, ...rest }) {
|
||||
const volume = useMicrophoneVolume(scene);
|
||||
const { volume } = useMicrophone(scene);
|
||||
const [soundPlaying, playSound] = useSound({ webmSrc, mp3Src, oggSrc, wavSrc });
|
||||
return <MicSetupModal micLevel={volume} soundPlaying={soundPlaying} onPlaySound={playSound} {...rest} />;
|
||||
}
|
||||
|
|
|
@ -54,10 +54,10 @@ function getVoiceLabel(micPresence) {
|
|||
|
||||
function getVoiceIconComponent(micPresence) {
|
||||
if (micPresence) {
|
||||
if (micPresence.talking) {
|
||||
return VolumeHighIcon;
|
||||
} else if (micPresence.muted) {
|
||||
if (micPresence.muted) {
|
||||
return VolumeMutedIcon;
|
||||
} else if (micPresence.talking) {
|
||||
return VolumeHighIcon;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import React, { useEffect, useRef } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ReactComponent as MicrophoneIcon } from "../icons/Microphone.svg";
|
||||
import { ReactComponent as MicrophoneMutedIcon } from "../icons/MicrophoneMuted.svg";
|
||||
import { ToolbarButton } from "../input/ToolbarButton";
|
||||
import { useMicrophone } from "./useMicrophone";
|
||||
|
||||
export function VoiceButtonContainer({ scene, microphoneEnabled }) {
|
||||
const buttonRef = useRef();
|
||||
|
||||
const { isMuted, volume, toggleMute } = useMicrophone(scene);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const rect = buttonRef.current.querySelector("rect");
|
||||
|
||||
if (volume < 0.05) {
|
||||
rect.setAttribute("height", 0);
|
||||
} else if (volume < 0.3) {
|
||||
rect.setAttribute("y", 8);
|
||||
rect.setAttribute("height", 4);
|
||||
} else {
|
||||
rect.setAttribute("y", 4);
|
||||
rect.setAttribute("height", 8);
|
||||
}
|
||||
},
|
||||
[volume, isMuted]
|
||||
);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
ref={buttonRef}
|
||||
icon={isMuted || !microphoneEnabled ? <MicrophoneMutedIcon /> : <MicrophoneIcon />}
|
||||
label="Voice"
|
||||
preset="basic"
|
||||
onClick={toggleMute}
|
||||
statusColor={microphoneEnabled ? "green" : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
VoiceButtonContainer.propTypes = {
|
||||
scene: PropTypes.object.isRequired,
|
||||
microphoneEnabled: PropTypes.bool
|
||||
};
|
|
@ -1,17 +1,17 @@
|
|||
import { useState, useEffect, useRef } from "react";
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import MovingAverage from "moving-average";
|
||||
|
||||
const MOVING_AVG_TIMESPAN = 100;
|
||||
const UPDATE_RATE = 50;
|
||||
|
||||
export function useMicrophoneVolume(scene) {
|
||||
export function useMicrophone(scene, updateRate = UPDATE_RATE) {
|
||||
const movingAvgRef = useRef();
|
||||
const [isMuted, setIsMuted] = useState(scene.is("muted"));
|
||||
const [volume, setVolume] = useState(0);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!movingAvgRef.current) {
|
||||
movingAvgRef.current = MovingAverage(MOVING_AVG_TIMESPAN);
|
||||
movingAvgRef.current = MovingAverage(updateRate * 2);
|
||||
}
|
||||
|
||||
let max = 0;
|
||||
|
@ -31,12 +31,30 @@ export function useMicrophoneVolume(scene) {
|
|||
|
||||
updateMicVolume();
|
||||
|
||||
function onSceneStateChange(event) {
|
||||
if (event.detail === "muted") {
|
||||
setIsMuted(scene.is("muted"));
|
||||
}
|
||||
}
|
||||
|
||||
scene.addEventListener("stateadded", onSceneStateChange);
|
||||
scene.addEventListener("stateremoved", onSceneStateChange);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
scene.removeEventListener("stateadded", onSceneStateChange);
|
||||
scene.removeEventListener("stateremoved", onSceneStateChange);
|
||||
};
|
||||
},
|
||||
[setVolume, scene]
|
||||
[setVolume, scene, updateRate]
|
||||
);
|
||||
|
||||
return volume;
|
||||
const toggleMute = useCallback(
|
||||
() => {
|
||||
scene.emit("action_mute");
|
||||
},
|
||||
[scene]
|
||||
);
|
||||
|
||||
return { isMuted, volume, toggleMute };
|
||||
}
|
|
@ -91,7 +91,6 @@ import { ReactComponent as DiscordIcon } from "./icons/Discord.svg";
|
|||
import { ReactComponent as VRIcon } from "./icons/VR.svg";
|
||||
import { ReactComponent as PeopleIcon } from "./icons/People.svg";
|
||||
import { ReactComponent as ObjectsIcon } from "./icons/Objects.svg";
|
||||
import { ReactComponent as MicrophoneIcon } from "./icons/Microphone.svg";
|
||||
import { ReactComponent as ReactionIcon } from "./icons/Reaction.svg";
|
||||
import { ReactComponent as LeaveIcon } from "./icons/Leave.svg";
|
||||
import { PeopleSidebarContainer, userFromPresence } from "./room/PeopleSidebarContainer";
|
||||
|
@ -101,6 +100,7 @@ import { ObjectMenuContainer } from "./room/ObjectMenuContainer";
|
|||
import { useCssBreakpoints } from "react-use-css-breakpoints";
|
||||
import { PlacePopoverContainer } from "./room/PlacePopoverContainer";
|
||||
import { SharePopoverContainer } from "./room/SharePopoverContainer";
|
||||
import { VoiceButtonContainer } from "./room/VoiceButtonContainer";
|
||||
|
||||
const avatarEditorDebug = qsTruthy("avatarEditorDebug");
|
||||
|
||||
|
@ -1894,7 +1894,7 @@ class UIRoot extends Component {
|
|||
<>
|
||||
{entered && (
|
||||
<>
|
||||
<ToolbarButton icon={<MicrophoneIcon />} label="Voice" preset="basic" />
|
||||
<VoiceButtonContainer scene={this.props.scene} microphoneEnabled={!!this.state.audioTrack} />
|
||||
<SharePopoverContainer scene={this.props.scene} hubChannel={this.props.hubChannel} />
|
||||
<PlacePopoverContainer
|
||||
scene={this.props.scene}
|
||||
|
|
|
@ -396,7 +396,10 @@ module.exports = async (env, argv) => {
|
|||
template: require("./src/react-components/icons/IconTemplate"),
|
||||
svgoConfig: {
|
||||
plugins: {
|
||||
removeViewBox: false
|
||||
removeViewBox: false,
|
||||
mergePaths: false,
|
||||
convertShapeToPath: false,
|
||||
removeHiddenElems: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче