feat: audio quality and mute
This commit is contained in:
@@ -3,9 +3,11 @@ import { useAppSelector } from '../../app/store';
|
||||
import User from '../../common/component/User';
|
||||
import IconHeadphone from '../../assets/icons/headphone.svg';
|
||||
import IconMic from '../../assets/icons/mic.on.svg';
|
||||
import IconSoundOn from '../../assets/icons/sound.on.svg';
|
||||
import IconSignal from '../../assets/icons/signal.svg';
|
||||
import IconMicOff from '../../assets/icons/mic.off.svg';
|
||||
// import IconSoundOn from '../../assets/icons/sound.on.svg';
|
||||
// import IconSignal from '../../assets/icons/signal.svg';
|
||||
import { useVoice } from '../../common/component/Voice';
|
||||
import Signal from '../../common/component/Signal';
|
||||
|
||||
type Props = {
|
||||
id: number,
|
||||
@@ -13,30 +15,31 @@ type Props = {
|
||||
}
|
||||
|
||||
const RTCWidget = ({ id, context = "channel" }: Props) => {
|
||||
const { leave } = useVoice({ context, id });
|
||||
const { loginUser, voicingInfo, channelData, userData } = useAppSelector(store => {
|
||||
const { leave, voicingInfo, setMute } = useVoice({ context, id });
|
||||
const { loginUser, channelData, userData } = useAppSelector(store => {
|
||||
return {
|
||||
userData: store.users.byId,
|
||||
channelData: store.channels.byId,
|
||||
loginUser: store.authData.user,
|
||||
voicingInfo: store.voice.voicing
|
||||
};
|
||||
});
|
||||
if (!loginUser) return null;
|
||||
if (!loginUser || !voicingInfo) return null;
|
||||
const name = voicingInfo.context == "channel" ? channelData[voicingInfo.id]?.name : userData[voicingInfo.id]?.name;
|
||||
if (!name) return null;
|
||||
return (
|
||||
<div className='bg-gray-100 dark:bg-gray-900 flex flex-col p-2 rounded-3xl m-3 text-sm'>
|
||||
{voicingInfo &&
|
||||
<div className="flex justify-between items-center border-b border-gray-100 dark:border-gray-800 pb-3 mb-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<IconSignal />
|
||||
<div className="flex flex-col">
|
||||
<span className='text-green-800'>Voice Connected</span>
|
||||
<span className='text-gray-600 dark:text-gray-400 text-xs'>{voicingInfo.context == "channel" ? "Channel" : "DM"} / {voicingInfo.context == "channel" ? channelData[voicingInfo.id].name : userData[voicingInfo.id].name}</span>
|
||||
</div>
|
||||
{/* {voicingInfo && */}
|
||||
<div className="flex justify-between items-center border-b border-gray-100 dark:border-gray-800 pb-3 mb-2">
|
||||
<div className="flex flex-1 items-center gap-1">
|
||||
<Signal strength={voicingInfo.downlinkNetworkQuality} />
|
||||
<div className="flex flex-col">
|
||||
<span className='text-green-800'>Voice Connected</span>
|
||||
<span className='text-gray-600 dark:text-gray-400 text-xs truncate max-w-[170px]' >{voicingInfo.context == "channel" ? "Channel" : "DM"} / {voicingInfo.context == "channel" ? channelData[voicingInfo.id].name : userData[voicingInfo.id].name}</span>
|
||||
</div>
|
||||
<IconHeadphone onClick={leave} role="button" className="fill-red-600" />
|
||||
</div>
|
||||
}
|
||||
<IconHeadphone onClick={leave} role="button" className="fill-red-600" />
|
||||
</div>
|
||||
{/* } */}
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center gap-3">
|
||||
<User uid={loginUser.uid} compact />
|
||||
@@ -45,10 +48,15 @@ const RTCWidget = ({ id, context = "channel" }: Props) => {
|
||||
<span className='text-gray-400 text-xs'>#{loginUser.uid}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<IconSoundOn role="button" />
|
||||
<IconMic role="button" />
|
||||
{/* {voicingInfo && */}
|
||||
<div className="flex gap-2 px-1">
|
||||
{/* <IconSoundOn role="button" /> */}
|
||||
{voicingInfo.muted ?
|
||||
<IconMicOff className="w-5" onClick={setMute.bind(null, false)} role="button" />
|
||||
:
|
||||
<IconMic className="w-5" onClick={setMute.bind(null, true)} role="button" />}
|
||||
</div>
|
||||
{/* } */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -10,11 +10,13 @@ type Props = {
|
||||
}
|
||||
|
||||
const Dashboard = ({ context = "channel", id }: Props) => {
|
||||
const { joinVoice, joined, joining, voiceInfo } = useVoice({ id, context });
|
||||
const { joinVoice, joined, joining, voicingInfo } = useVoice({ id, context });
|
||||
// const dispatch = useDispatch();
|
||||
|
||||
|
||||
return <div className='absolute -left-full -translate-x-full -top-1 z-50 shadow rounded p-2 bg-white dark:bg-black'>{joined ? <VoiceManagement info={voiceInfo} /> : <JoinVoice join={joinVoice} joining={joining} />}</div>;
|
||||
return <div className='absolute -left-full -translate-x-full -top-1 z-50 shadow rounded p-2 bg-white dark:bg-black px-2 py-4'>
|
||||
{joined ? <VoiceManagement info={voicingInfo} /> : <JoinVoice join={joinVoice} joining={joining} />}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
@@ -11,7 +11,7 @@ const JoinVoice = ({ joining, join }: Props) => {
|
||||
Joining
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
<div className='w-full h-full flex-center p-4'>
|
||||
<StyledButton className='mini' onClick={join}>Join</StyledButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,22 +1,49 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { VoiceInfo } from '../../../app/slices/voice';
|
||||
import { VoicingInfo } from '../../../app/slices/voice';
|
||||
import { useAppSelector } from '../../../app/store';
|
||||
import User from '../../../common/component/User';
|
||||
import Avatar from '../../../common/component/Avatar';
|
||||
import IconMic from '../../../assets/icons/mic.on.svg';
|
||||
import IconMicOff from '../../../assets/icons/mic.off.svg';
|
||||
// import User from '../../../common/component/User';
|
||||
|
||||
type Props = {
|
||||
info: VoiceInfo | null
|
||||
info: VoicingInfo | null
|
||||
}
|
||||
|
||||
const VoiceManagement = ({ info }: Props) => {
|
||||
const userData = useAppSelector(store => store.users.byId);
|
||||
if (!info) return null;
|
||||
const { context, id, members } = info;
|
||||
const nameClass = clsx(`text-sm text-gray-500 max-w-[190px] truncate font-semibold dark:text-white`);
|
||||
return (
|
||||
<div>
|
||||
<ul>
|
||||
<div className='w-full h-full py-2'>
|
||||
<ul className='flex flex-col gap-2'>
|
||||
{members.map((uid) => {
|
||||
const curr = userData[uid];
|
||||
if (!curr) return null;
|
||||
return <li key={uid}>
|
||||
<User uid={uid} interactive={false} />
|
||||
<div className="flex items-center justify-between gap-6 ">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 flex shrink-0">
|
||||
<Avatar
|
||||
width={32}
|
||||
height={32}
|
||||
className="w-full h-full rounded-full object-cover"
|
||||
src={curr.avatar}
|
||||
name={curr.name}
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
<span className={nameClass} title={curr?.name}>
|
||||
{curr?.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<IconMic className="w-4" role="button" />
|
||||
</div>
|
||||
</div>
|
||||
{/* <User uid={uid} interactive={false} /> */}
|
||||
{/* {userData[uid]?.name} */}
|
||||
</li>;
|
||||
})}
|
||||
|
||||
@@ -23,7 +23,7 @@ const VoiceChat = ({ id, context = "channel" }: Props) => {
|
||||
return (
|
||||
<Tooltip disabled={dashboardVisible} tip={t("voice")} placement="left">
|
||||
<li className={`relative`} >
|
||||
<IconHeadphone className="fill-gray-500" role="button" onClick={toggleDashboard} />
|
||||
<IconHeadphone className={dashboardVisible ? "fill-gray-600" : "fill-gray-500"} role="button" onClick={toggleDashboard} />
|
||||
{dashboardVisible && <Dashboard id={id} context={context} />}
|
||||
</li>
|
||||
</Tooltip>
|
||||
|
||||
Reference in New Issue
Block a user