diff --git a/src/app/slices/voice.ts b/src/app/slices/voice.ts index 762ebab1..34a6567c 100644 --- a/src/app/slices/voice.ts +++ b/src/app/slices/voice.ts @@ -8,8 +8,9 @@ export type VoiceBasicInfo = { export type VoicingInfo = { downlinkNetworkQuality?: number, - muted: boolean, - deafen: boolean + muted?: boolean, + deafen?: boolean, + joining?: boolean } & VoiceBasicInfo export type VoicingMemberInfo = { diff --git a/src/common/component/Voice.tsx b/src/common/component/Voice.tsx index e39b75df..54528c13 100644 --- a/src/common/component/Voice.tsx +++ b/src/common/component/Voice.tsx @@ -1,7 +1,8 @@ import AgoraRTC, { IMicrophoneAudioTrack } from 'agora-rtc-sdk-ng'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { useGetAgoraConfigQuery, useGetAgoraVoicingListQuery, useLazyGetAgoraTokenQuery } from '../../app/services/server'; +import { updateChannelVisibleAside } from '../../app/slices/channels'; import { addVoiceMember, removeVoiceMember, updateDeafenStatus, updateMuteStatus, updateVoicingInfo, updateVoicingMember, updateVoicingNetworkQuality } from '../../app/slices/voice'; import { useAppSelector } from '../../app/store'; @@ -136,9 +137,14 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => { }; }); const [generateToken] = useLazyGetAgoraTokenQuery(); - const [joining, setJoining] = useState(false); + // const [joining, setJoining] = useState(false); const joinVoice = async () => { - setJoining(true); + // setJoining(true); + dispatch(updateVoicingInfo({ + id, + context, + joining: true + })); const { isError, data } = await generateToken(id); if (!isError && data) { const { channel_name, app_id, agora_token, uid } = data; @@ -153,14 +159,23 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => { dispatch(updateVoicingInfo({ deafen: false, muted: false, + joining: false, id, context, })); // 把自己加进去 dispatch(addVoiceMember(uid)); } + } else { + console.error("generate agora token error"); + dispatch(updateVoicingInfo({ + joining: false, + id, + context, + })); + } - setJoining(false); + // setJoining(false); }; const leave = async () => { if (window.VOICE_CLIENT && localTrack) { @@ -168,6 +183,11 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => { localTrack = null; await window.VOICE_CLIENT.leave(); dispatch(updateVoicingInfo(null)); + if (context == "channel") { + dispatch(updateChannelVisibleAside({ + id, aside: null + })); + } } }; const setMute = (mute: boolean) => { @@ -194,13 +214,15 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => { dispatch(updateDeafenStatus(deafen)); } }; + const joinedAtThisContext = voicingInfo ? (voicingInfo.id == id && voicingInfo.context == context) : false; return { setMute, setDeafen, leave, // canVoice, voicingInfo, - joining, + joining: voicingInfo ? voicingInfo.joining : undefined, + joinedAtThisContext, joined: !!voicingInfo, joinVoice }; diff --git a/src/routes/chat/VoiceChat/Dashboard.tsx b/src/routes/chat/VoiceChat/Dashboard.tsx index 0c101a6a..b40ff239 100644 --- a/src/routes/chat/VoiceChat/Dashboard.tsx +++ b/src/routes/chat/VoiceChat/Dashboard.tsx @@ -1,7 +1,6 @@ // import { useEffect } from 'react'; // import { useDispatch } from 'react-redux'; import VoiceManagement from './VoiceManagement'; -import JoinVoice from './JoinVoice'; import { useVoice } from '../../../common/component/Voice'; type Props = { @@ -11,12 +10,12 @@ type Props = { } const Dashboard = ({ context = "channel", id, visible }: Props) => { - const { joinVoice, joined, joining, voicingInfo, setMute, setDeafen, leave } = useVoice({ id, context }); + const { voicingInfo, setMute, setDeafen, leave } = useVoice({ id, context }); // const dispatch = useDispatch(); - return
- {joined ? : } + return
+
; }; diff --git a/src/routes/chat/VoiceChat/JoinVoice.tsx b/src/routes/chat/VoiceChat/JoinVoice.tsx deleted file mode 100644 index 61e056e3..00000000 --- a/src/routes/chat/VoiceChat/JoinVoice.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import StyledButton from '../../../common/component/styled/Button'; - -type Props = { - join: () => void, - joining: boolean -} - -const JoinVoice = ({ joining, join }: Props) => { - if (joining) return
- Connecting to voice channel... -
; - return ( -
- Start Audio Chat -
- ); -}; - -export default JoinVoice; \ No newline at end of file diff --git a/src/routes/chat/VoiceChat/VoiceManagement.tsx b/src/routes/chat/VoiceChat/VoiceManagement.tsx index f8d8fe60..e4b6720e 100644 --- a/src/routes/chat/VoiceChat/VoiceManagement.tsx +++ b/src/routes/chat/VoiceChat/VoiceManagement.tsx @@ -30,15 +30,20 @@ const VoiceManagement = ({ info, setMute, setDeafen, leave }: Props) => { const nameClass = clsx(`text-sm text-gray-500 max-w-[120px] truncate font-semibold dark:text-white`); const members = voicingMembers.ids; const membersData = voicingMembers.byId; + if (info.joining) { + return
+ Connecting to voice channel... +
; + } return (
-
    +
      {members.map((uid) => { const curr = userData[uid]; if (!curr) return null; - const { muted, deafen, speakingVolume = 0 } = membersData[uid]; + const { muted, speakingVolume = 0 } = membersData[uid]; const speaking = speakingVolume > 50; - return
    • + return
    • {
      - {deafen ? : } + {/* {deafen ? : } */} {muted ? : }
      {/* */} diff --git a/src/routes/chat/VoiceChat/index.tsx b/src/routes/chat/VoiceChat/index.tsx index 229b0b53..992dd390 100644 --- a/src/routes/chat/VoiceChat/index.tsx +++ b/src/routes/chat/VoiceChat/index.tsx @@ -7,7 +7,7 @@ import { updateChannelVisibleAside } from '../../../app/slices/channels'; import { useAppSelector } from '../../../app/store'; import IconHeadphone from '../../../assets/icons/headphone.svg'; import Tooltip from '../../../common/component/Tooltip'; -// import Dashboard from './Dashboard'; +import { useVoice } from '../../../common/component/Voice'; type Props = { context?: "channel" | "dm" @@ -15,8 +15,15 @@ type Props = { } const VoiceChat = ({ id, context = "channel" }: Props) => { + const { joinVoice, joined, joining = false, joinedAtThisContext } = useVoice({ id, context }); const dispatch = useDispatch(); - const { loginUser, contextData } = useAppSelector(store => { return { loginUser: store.authData.user, contextData: context == "channel" ? store.channels.byId[id] : store.users.byId[id] }; }); + const { loginUser, contextData, voiceList } = useAppSelector(store => { + return { + loginUser: store.authData.user, + contextData: context == "channel" ? store.channels.byId[id] : store.users.byId[id], + voiceList: store.voice.list + }; + }); const { t } = useTranslation("chat"); const toggleDashboard = () => { if (context == "channel") { @@ -27,12 +34,30 @@ const VoiceChat = ({ id, context = "channel" }: Props) => { } // todo DM }; + const handleJoin = () => { + if (joining || joined) { + alert("You have joined another channel, please leave first!"); + return; + } + joinVoice(); + if (context == "channel") { + dispatch(updateChannelVisibleAside({ + id, + aside: "voice" + })); + } + }; if (!loginUser) return null; const visible = contextData.visibleAside == "voice"; + const memberCount = voiceList.find((v) => v.context == context && v.id == id)?.memberCount ?? 0; + const badgeClass = `absolute -top-1 -right-1 w-4 h-4 rounded-full bg-primary-400 text-white `; return ( -
    • - +
    • + + {visible ? null : memberCount > 0 ? {memberCount} : }
    • );