refactor: calling

This commit is contained in:
Tristan Yang
2023-05-16 14:54:29 +08:00
parent 20b907aae4
commit 23191fd3bf
12 changed files with 99 additions and 69 deletions
+12 -3
View File
@@ -38,6 +38,7 @@ export type VoiceInfo = {
interface State {
callingFrom: number,
callingTo: number,
calling: boolean,
voicing: VoicingInfo | null,
voicingMembers: VoicingMembers,
list: VoiceInfo[]
@@ -48,6 +49,7 @@ interface State {
// members: []
// };
const initialState: State = {
calling: false,
callingFrom: 0,
callingTo: 0,
voicing: null,
@@ -64,10 +66,16 @@ const voiceSlice = createSlice({
name: "voice",
initialState,
reducers: {
updateCalling(state, { payload }: PayloadAction<{ from: number, to?: number }>) {
const { from, to = 0 } = payload;
updateCallInfo(state, { payload }: PayloadAction<{ from: number, to?: number, calling?: boolean }>) {
const { from, to = 0, calling } = payload;
state.callingFrom = from;
state.callingTo = to;
if (typeof calling === "boolean") {
state.calling = calling;
}
},
updateCalling(state, { payload }: PayloadAction<boolean>) {
state.calling = payload;
},
updateVoicingInfo(state, { payload }: PayloadAction<VoicingInfo | null>) {
if (payload) {
@@ -182,6 +190,7 @@ const voiceSlice = createSlice({
});
}
state.voicing = null;
state.calling = false;
state.voicingMembers = {
ids: [],
byId: {}
@@ -190,5 +199,5 @@ const voiceSlice = createSlice({
}
});
export const { updateCalling, updatePin, updateConnectionState, addVoiceMember, removeVoiceMember, upsertVoiceList, updateVoicingInfo, updateVoicingNetworkQuality, updateVoicingMember } = voiceSlice.actions;
export const { updateCalling, updateCallInfo, updatePin, updateConnectionState, addVoiceMember, removeVoiceMember, upsertVoiceList, updateVoicingInfo, updateVoicingNetworkQuality, updateVoicingMember } = voiceSlice.actions;
export default voiceSlice.reducer;
+6 -4
View File
@@ -26,9 +26,10 @@ const DMCalling = ({ from, to = 0 }: Props) => {
const dispatch = useDispatch();
const { leave, joinVoice, joining } = useVoice({ id: to, context: "dm" });
const containerRef = useRef(null);
const { voicingInfo, voicingMembers, fromUser, toUser, loginUser } = useAppSelector(store => {
const { calling, voicingMembers, fromUser, toUser, loginUser } = useAppSelector(store => {
return {
voicingInfo: store.voice.voicing ?? {},
calling: store.voice.calling,
// voicingInfo: store.voice.voicing ?? {},
voicingMembers: store.voice.voicingMembers,
fromUser: store.users.byId[from],
toUser: store.users.byId[to],
@@ -48,7 +49,8 @@ const DMCalling = ({ from, to = 0 }: Props) => {
if (sendByMe || voicingMembers.ids.length == 2) {
leave();
}
dispatch(updateCalling({ from: 0, to: 0 }));
// 拒绝
dispatch(updateCalling(false));
};
const handleAnswer = () => {
joinVoice();
@@ -58,7 +60,7 @@ const DMCalling = ({ from, to = 0 }: Props) => {
const connected = voicingMembers.ids.length == 2;
console.log("dm calling", !fromUser, !toUser, connected);
const atChatPath = sendByMe ? pathname == `/chat/dm/${to}` : pathname == `/chat/dm/${from}`;
if (!fromUser || !toUser || connected || atChatPath) return null;
if (!fromUser || !toUser || connected || atChatPath || !calling) return null;
return (
<div ref={containerRef} className="fixed top-0 left-0 w-full h-screen z-[999] pointer-events-none flex items-center justify-end pr-10">
+8 -3
View File
@@ -2,7 +2,7 @@ import AgoraRTC from 'agora-rtc-sdk-ng';
import { memo, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useGetAgoraChannelsQuery, useGetAgoraStatusQuery, useLazyGetAgoraUsersByChannelQuery } from '../../app/services/server';
import { addVoiceMember, removeVoiceMember, updateCalling, updateConnectionState, updateVoicingMember, updateVoicingNetworkQuality } from '../../app/slices/voice';
import { addVoiceMember, removeVoiceMember, updateCallInfo, updateConnectionState, updateVoicingMember, updateVoicingNetworkQuality } from '../../app/slices/voice';
import { useAppSelector } from '../../app/store';
import { playAgoraVideo } from '../../utils';
import useVoice from './useVoice';
@@ -86,7 +86,7 @@ const Voice = () => {
window.VIDEO_TRACK_MAP[+user.uid] = null;
if (voicingInfo && voicingInfo.context == "dm") {
// 有人离开,就断开
dispatch(updateCalling({ from: 0 }));
dispatch(updateCallInfo({ from: 0 }));
}
}
break;
@@ -161,6 +161,7 @@ const Voice = () => {
};
}, [voicingInfo]);
useEffect(() => {
// 有人呼叫我
const callMeList = voiceList.filter(item => item.context == "dm" && item.id == loginUid);
@@ -168,14 +169,18 @@ const Voice = () => {
const [firstCall] = callMeList;
const { memberCount, channelName, id } = firstCall;
if (memberCount == 1) {
// 呼叫的人在频道里
getUsersByChannel(channelName).then(resp => {
const [uid] = resp.data ?? [];
if (uid) {
dispatch(updateCalling({ from: uid, to: id }));
dispatch(updateCallInfo({ from: uid, to: id }));
}
});
}
}
// else {
// dispatch(updateCallInfo({ from: 0, to: 0, calling: false }));
// }
}, [voiceList, loginUid]);
// return <DMCalling uid={1} sendByMe={calling !== loginUid} />;
if (from !== 0) return <DMCalling from={from} to={to} />;
+5 -1
View File
@@ -52,7 +52,11 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => {
// Publish the local audio track in the channel.
await window.VOICE_CLIENT.publish(localAudioTrack);
// play the join audio
audioJoin.play();
try {
await audioJoin.play();
} catch (error) {
console.warn("play join sound failed!", error);
}
console.log("Publish success!,joined the channel");
dispatch(updateVoicingInfo({
deafen: false,
+6
View File
@@ -30,6 +30,7 @@ import { useRenewMutation } from "@/app/services/auth";
import { getLocalAuthData } from "@/utils";
import { removeUserSession } from "@/app/slices/message.user";
import { clearChannelMessage, removeChannelSession } from "@/app/slices/message.channel";
import { updateCallInfo } from "@/app/slices/voice";
const getQueryString = (params: { [key: string]: string }) => {
const sp = new URLSearchParams();
@@ -151,6 +152,11 @@ export default function useStreaming() {
dispatch(clearChannelMessage(gid));
break;
}
case "user_calling": {
const { target, uid } = data;
dispatch(updateCallInfo({ from: uid, to: target, calling: true }));
break;
}
case "pinned_chats": {
const { chats } = data;
dispatch(upsertPinChats({ pins: chats, override: true }));
+8 -8
View File
@@ -13,7 +13,7 @@ import IconMic from '@/assets/icons/mic.on.svg';
import IconCamera from '@/assets/icons/camera.svg';
import IconCameraOff from '@/assets/icons/camera.off.svg';
import IconScreen from '@/assets/icons/share.screen.svg';
import { VoicingInfo, updateCalling } from "@/app/slices/voice";
import { VoicingInfo, updateCallInfo } from "@/app/slices/voice";
import { useVoice } from "@/components/Voice";
import { playAgoraVideo } from "@/utils";
import Avatar from "@/components/Avatar";
@@ -32,13 +32,13 @@ type BlockProps = {
to: VoicingMember
}
const VoicingBlocks = ({ sendByMe, connected, from, to }: BlockProps) => {
const blocks = [from, to].map(({ id, speaking, name, avatar, video }, idx) => {
const showWaiting = idx == 1 && !connected && sendByMe;
const showWaiting = idx == 1 && !connected && !sendByMe;
const showToWaiting = idx == 1 && !connected && sendByMe;
return <div key={id} className={clsx("relative flex-center", video ? "w-80 h-60 overflow-hidden rounded" : "")}>
<div className={clsx("w-20 h-20 flex shrink-0 relative transition-opacity", showWaiting && "animate-pulse")}>
<div className={clsx("w-20 h-20 flex shrink-0 relative transition-opacity", showToWaiting && "animate-pulse", showWaiting && "opacity-40")}>
{speaking && <div className={clsx("z-10 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 rounded-full bg-green-500 animate-speaking", "w-[86px] h-[86px]")}></div>}
{showWaiting && <div className={clsx("z-10 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-gray-400", "w-[88px] h-[88px]")}></div>}
{showToWaiting && <div className={clsx("z-10 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-gray-400", "w-[88px] h-[88px]")}></div>}
<Avatar
width={80}
height={80}
@@ -88,7 +88,7 @@ const DMVoice = ({ uid }: Props) => {
if (sendByMe || voicingMembers.ids.length == 2) {
leave();
}
dispatch(updateCalling({ from: 0, to: 0 }));
dispatch(updateCallInfo({ from: 0, to: 0 }));
};
const handleAnswer = () => {
joinVoice();
@@ -117,11 +117,11 @@ const DMVoice = ({ uid }: Props) => {
}} />
</div>
<div className={clsx("flex gap-3", connected ? "h-full items-end" : "")}>
<Tooltip tip={"Disconnect"} placement="top">
{(sendByMe || connected) && <Tooltip tip={"Disconnect"} placement="top">
<button onClick={handleCancel} className='flex-center bg-red-600 hover:bg-red-700 py-2 px-3 rounded-lg'>
<IconCallOff className="w-6 h-6" />
</button>
</Tooltip>
</Tooltip>}
{!sendByMe && !connected &&
<Tooltip tip={"Answer"} placement="top">
<button disabled={joining} onClick={handleAnswer} className='flex-center bg-green-600 hover:bg-green-700 py-2 px-3 rounded-lg'>
+2 -2
View File
@@ -10,7 +10,7 @@ import Tooltip from '../../../components/Tooltip';
import { useVoice } from '../../../components/Voice';
import { useGetAgoraStatusQuery } from '../../../app/services/server';
import { ChatContext } from '../../../types/common';
import { updateCalling } from '../../../app/slices/voice';
import { updateCallInfo } from '../../../app/slices/voice';
type Props = {
context?: ChatContext
@@ -50,7 +50,7 @@ const VoiceChat = ({ id, context = "channel" }: Props) => {
dispatch(context == "channel" ? updateChannelVisibleAside(data) : updateDMVisibleAside(data));
// 实时显示calling box
if (!joinedAtThisContext && context == "dm") {
dispatch(updateCalling({ from: loginUser?.uid ?? 0, to: id }));
dispatch(updateCallInfo({ from: loginUser?.uid ?? 0, to: id, calling: true }));
}
};
if (!loginUser || !enabled) return null;
+7 -1
View File
@@ -211,6 +211,11 @@ interface GroupClearEvent {
type: "group_message_cleared";
gid: number;
}
interface UserCallEvent {
type: "user_calling";
target: number;
uid: number
}
export type ServerEvent =
| ReadyEvent
@@ -231,4 +236,5 @@ export type ServerEvent =
| PinnedMessageUpdatedEvent
| HeartbeatEvent
| GroupClearEvent
| PinnedChatsEvent;
| PinnedChatsEvent
| UserCallEvent;