refactor: voice data structure
This commit is contained in:
@@ -22,7 +22,7 @@ import {
|
|||||||
} from "../../types/server";
|
} from "../../types/server";
|
||||||
import { Channel } from "../../types/channel";
|
import { Channel } from "../../types/channel";
|
||||||
import { ContentTypeKey } from "../../types/message";
|
import { ContentTypeKey } from "../../types/message";
|
||||||
import { updateVoiceInfo } from "../slices/voice";
|
import { updateVoiceList } from "../slices/voice";
|
||||||
|
|
||||||
const defaultExpireDuration = 2 * 24 * 60 * 60;
|
const defaultExpireDuration = 2 * 24 * 60 * 60;
|
||||||
|
|
||||||
@@ -128,14 +128,17 @@ export const serverApi = createApi({
|
|||||||
try {
|
try {
|
||||||
const { data: resp } = await queryFulfilled;
|
const { data: resp } = await queryFulfilled;
|
||||||
const { success } = resp;
|
const { success } = resp;
|
||||||
if (success && resp.data.channels.length > 0) {
|
if (success) {
|
||||||
const [channel] = resp.data.channels;
|
const arr = resp.data.channels.map(data => {
|
||||||
const [id] = channel.channel_name.split(":").slice(-1);
|
const [id] = data.channel_name.split(":").slice(-1);
|
||||||
// todo
|
const count = data.user_count;
|
||||||
dispatch(updateVoiceInfo({
|
return {
|
||||||
id: +id,
|
id: +id,
|
||||||
context: "channel"
|
context: "channel" as const,
|
||||||
}));
|
memberCount: count
|
||||||
|
};
|
||||||
|
});
|
||||||
|
dispatch(updateVoiceList(arr));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
console.error("get voice list error");
|
console.error("get voice list error");
|
||||||
|
|||||||
+22
-27
@@ -1,22 +1,27 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
export type VoiceInfo = {
|
export type VoiceBasicInfo = {
|
||||||
context: "channel" | "dm"
|
context: "channel" | "dm"
|
||||||
id: number | null,
|
id: number,
|
||||||
|
}
|
||||||
|
export type VoicingInfo = {
|
||||||
members: number[]
|
members: number[]
|
||||||
}
|
} & VoiceBasicInfo
|
||||||
|
export type VoiceInfo = {
|
||||||
|
memberCount: number
|
||||||
|
} & VoiceBasicInfo
|
||||||
interface State {
|
interface State {
|
||||||
joined: boolean,
|
voicing: VoicingInfo | null,
|
||||||
voicing: VoiceInfo
|
list: VoiceInfo[]
|
||||||
}
|
}
|
||||||
const initialInfo = {
|
// const initialInfo = {
|
||||||
context: "channel" as const,
|
// context: "channel" as const,
|
||||||
id: null,
|
// id: 0,
|
||||||
members: []
|
// members: []
|
||||||
};
|
// };
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
joined: false,
|
voicing: null,
|
||||||
voicing: initialInfo
|
list: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -25,21 +30,11 @@ const voiceSlice = createSlice({
|
|||||||
name: "voice",
|
name: "voice",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateJoinStatus(state, { payload }: PayloadAction<boolean>) {
|
updateVoicingInfo(state, { payload }: PayloadAction<VoicingInfo | null>) {
|
||||||
state.joined = payload;
|
|
||||||
},
|
|
||||||
updateVoiceInfo(state, { payload }: PayloadAction<VoiceInfo | null>) {
|
|
||||||
if (!payload) {
|
|
||||||
// reset
|
|
||||||
state.voicing = initialInfo;
|
|
||||||
} else {
|
|
||||||
if (state.voicing) {
|
|
||||||
state.voicing = { ...state.voicing, ...payload };
|
|
||||||
} else {
|
|
||||||
state.voicing = payload;
|
state.voicing = payload;
|
||||||
}
|
},
|
||||||
|
updateVoiceList(state, { payload }: PayloadAction<VoiceInfo[]>) {
|
||||||
}
|
state.list = payload;
|
||||||
},
|
},
|
||||||
addVoiceMember(state, { payload }: PayloadAction<number>) {
|
addVoiceMember(state, { payload }: PayloadAction<number>) {
|
||||||
if (state.voicing) {
|
if (state.voicing) {
|
||||||
@@ -59,5 +54,5 @@ const voiceSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { updateJoinStatus, updateVoiceInfo, addVoiceMember, removeVoiceMember } = voiceSlice.actions;
|
export const { addVoiceMember, removeVoiceMember, updateVoiceList, updateVoicingInfo } = voiceSlice.actions;
|
||||||
export default voiceSlice.reducer;
|
export default voiceSlice.reducer;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import AgoraRTC from 'agora-rtc-sdk-ng';
|
import AgoraRTC, { IMicrophoneAudioTrack } from 'agora-rtc-sdk-ng';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useGetAgoraConfigQuery, useGetAgoraVoicingListQuery, useLazyGetAgoraTokenQuery } from '../../app/services/server';
|
import { useGetAgoraConfigQuery, useGetAgoraVoicingListQuery, useLazyGetAgoraTokenQuery } from '../../app/services/server';
|
||||||
import { addVoiceMember, removeVoiceMember, updateJoinStatus, updateVoiceInfo } from '../../app/slices/voice';
|
import { addVoiceMember, removeVoiceMember, updateVoicingInfo } from '../../app/slices/voice';
|
||||||
import { useAppSelector } from '../../app/store';
|
import { useAppSelector } from '../../app/store';
|
||||||
|
|
||||||
// type Props = {}
|
// type Props = {}
|
||||||
@@ -34,6 +34,7 @@ const Voice = () => {
|
|||||||
dispatch(addVoiceMember(+user.uid));
|
dispatch(addVoiceMember(+user.uid));
|
||||||
// console.log("subscribe success");
|
// console.log("subscribe success");
|
||||||
// Subscribe and play the remote audio track.
|
// Subscribe and play the remote audio track.
|
||||||
|
console.log(user.uid + "has joined the channel");
|
||||||
if (mediaType == "audio") {
|
if (mediaType == "audio") {
|
||||||
// Play the remote audio track.
|
// Play the remote audio track.
|
||||||
user.audioTrack?.play();
|
user.audioTrack?.play();
|
||||||
@@ -71,10 +72,11 @@ type VoiceProps = {
|
|||||||
}
|
}
|
||||||
const useVoice = ({ id, context = "channel" }: VoiceProps) => {
|
const useVoice = ({ id, context = "channel" }: VoiceProps) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { voiceInfo, joined } = useAppSelector(store => {
|
const { voiceInfo, joined, loginUid = 0 } = useAppSelector(store => {
|
||||||
return {
|
return {
|
||||||
|
loginUid: store.authData.user?.uid,
|
||||||
voiceInfo: store.voice.voicing,
|
voiceInfo: store.voice.voicing,
|
||||||
joined: store.voice.joined
|
joined: !!store.voice.voicing
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const [generateToken] = useLazyGetAgoraTokenQuery();
|
const [generateToken] = useLazyGetAgoraTokenQuery();
|
||||||
@@ -88,25 +90,27 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => {
|
|||||||
await window.VOICE_CLIENT.join(app_id, channel_name, agora_token, uid);
|
await window.VOICE_CLIENT.join(app_id, channel_name, agora_token, uid);
|
||||||
console.table(data);
|
console.table(data);
|
||||||
// Create a local audio track from the microphone audio.
|
// Create a local audio track from the microphone audio.
|
||||||
const localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
|
|
||||||
|
window.VOICE_TRACK_MAP[loginUid] = await AgoraRTC.createMicrophoneAudioTrack();
|
||||||
// Publish the local audio track in the channel.
|
// Publish the local audio track in the channel.
|
||||||
await window.VOICE_CLIENT.publish(localAudioTrack);
|
await window.VOICE_CLIENT.publish(window.VOICE_TRACK_MAP[loginUid]);
|
||||||
console.log("Publish success!");
|
console.log("Publish success!");
|
||||||
dispatch(updateVoiceInfo({
|
dispatch(updateVoicingInfo({
|
||||||
id,
|
id,
|
||||||
context,
|
context,
|
||||||
members: [uid]
|
members: [uid]
|
||||||
}));
|
}));
|
||||||
dispatch(updateJoinStatus(true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setJoining(false);
|
setJoining(false);
|
||||||
};
|
};
|
||||||
const leave = async () => {
|
const leave = async () => {
|
||||||
if (window.VOICE_CLIENT) {
|
if (window.VOICE_CLIENT) {
|
||||||
|
(window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack).close();
|
||||||
await window.VOICE_CLIENT.leave();
|
await window.VOICE_CLIENT.leave();
|
||||||
dispatch(updateJoinStatus(false));
|
dispatch(updateVoicingInfo(null));
|
||||||
// window.VOICE_TRACK_MAP
|
// window.VOICE_TRACK_MAP[loginUid]?.stop();
|
||||||
|
// window.VOICE_TRACK_MAP[loginUid] = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface Props
|
|||||||
| "pattern"
|
| "pattern"
|
||||||
| "disabled"
|
| "disabled"
|
||||||
| "minLength"
|
| "minLength"
|
||||||
|
| "spellCheck"
|
||||||
>,
|
>,
|
||||||
HTMLInputElement
|
HTMLInputElement
|
||||||
> {
|
> {
|
||||||
|
|||||||
@@ -14,17 +14,24 @@ type Props = {
|
|||||||
|
|
||||||
const RTCWidget = ({ id, context = "channel" }: Props) => {
|
const RTCWidget = ({ id, context = "channel" }: Props) => {
|
||||||
const { leave } = useVoice({ context, id });
|
const { leave } = useVoice({ context, id });
|
||||||
const { loginUser, joined } = useAppSelector(store => { return { loginUser: store.authData.user, joined: store.voice.joined }; });
|
const { loginUser, voicingInfo, 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) return null;
|
||||||
return (
|
return (
|
||||||
<div className='bg-gray-100 dark:bg-gray-900 flex flex-col p-2 rounded-3xl m-3 text-sm'>
|
<div className='bg-gray-100 dark:bg-gray-900 flex flex-col p-2 rounded-3xl m-3 text-sm'>
|
||||||
{joined &&
|
{voicingInfo &&
|
||||||
<div className="flex justify-between items-center border-b border-solid border-gray-100 dark:border-gray-800 pb-3 mb-2">
|
<div className="flex justify-between items-center border-b border-solid border-gray-100 dark:border-gray-800 pb-3 mb-2">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<IconSignal />
|
<IconSignal />
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className='text-green-800'>Voice Connected</span>
|
<span className='text-green-800'>Voice Connected</span>
|
||||||
<span className='text-gray-600 dark:text-gray-400 text-xs'>Channel / name</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<IconHeadphone onClick={leave} role="button" className="fill-red-600" />
|
<IconHeadphone onClick={leave} role="button" className="fill-red-600" />
|
||||||
|
|||||||
@@ -16,20 +16,20 @@ import { useAppSelector } from "../../../app/store";
|
|||||||
import { fromNowTime } from "../../../common/utils";
|
import { fromNowTime } from "../../../common/utils";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
type?: "user" | "channel";
|
type?: "dm" | "channel";
|
||||||
id: number;
|
id: number;
|
||||||
mid: number;
|
mid: number;
|
||||||
setDeleteChannelId: (param: number) => void;
|
setDeleteChannelId: (param: number) => void;
|
||||||
setInviteChannelId: (param: number) => void;
|
setInviteChannelId: (param: number) => void;
|
||||||
}
|
}
|
||||||
const Session: FC<IProps> = ({
|
const Session: FC<IProps> = ({
|
||||||
type = "user",
|
type = "dm",
|
||||||
id,
|
id,
|
||||||
mid,
|
mid,
|
||||||
setDeleteChannelId,
|
setDeleteChannelId,
|
||||||
setInviteChannelId
|
setInviteChannelId
|
||||||
}) => {
|
}) => {
|
||||||
const navPath = type == "user" ? `/chat/dm/${id}` : `/chat/channel/${id}`;
|
const navPath = type == "dm" ? `/chat/dm/${id}` : `/chat/channel/${id}`;
|
||||||
// const { pathname } = useLocation();
|
// const { pathname } = useLocation();
|
||||||
const isCurrentPath = useMatch(navPath);
|
const isCurrentPath = useMatch(navPath);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -46,7 +46,7 @@ const Session: FC<IProps> = ({
|
|||||||
return { size, type, name, url };
|
return { size, type, name, url };
|
||||||
});
|
});
|
||||||
addStageFile(filesData);
|
addStageFile(filesData);
|
||||||
navigate(type == "user" ? `/chat/dm/${id}` : `/chat/channel/${id}`);
|
navigate(type == "dm" ? `/chat/dm/${id}` : `/chat/channel/${id}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
collect: (monitor) => ({
|
collect: (monitor) => ({
|
||||||
@@ -62,24 +62,24 @@ const Session: FC<IProps> = ({
|
|||||||
mid: number;
|
mid: number;
|
||||||
is_public: boolean;
|
is_public: boolean;
|
||||||
}>();
|
}>();
|
||||||
const { messageData, userData, channelData, readIndex, loginUid, mids, muted, voiceInfo } = useAppSelector(
|
const { messageData, userData, channelData, readIndex, loginUid, mids, muted, voiceList } = useAppSelector(
|
||||||
(store) => {
|
(store) => {
|
||||||
return {
|
return {
|
||||||
voiceInfo: store.voice.voicing,
|
voiceList: store.voice.list,
|
||||||
mids: type == "user" ? store.userMessage.byId[id] : store.channelMessage[id],
|
mids: type == "dm" ? store.userMessage.byId[id] : store.channelMessage[id],
|
||||||
loginUid: store.authData.user?.uid || 0,
|
loginUid: store.authData.user?.uid || 0,
|
||||||
readIndex:
|
readIndex:
|
||||||
type == "user" ? store.footprint.readUsers[id] : store.footprint.readChannels[id],
|
type == "dm" ? store.footprint.readUsers[id] : store.footprint.readChannels[id],
|
||||||
messageData: store.message,
|
messageData: store.message,
|
||||||
userData: store.users.byId,
|
userData: store.users.byId,
|
||||||
channelData: store.channels.byId,
|
channelData: store.channels.byId,
|
||||||
muted: type == "user" ? store.footprint.muteUsers[id] : store.footprint.muteChannels[id]
|
muted: type == "dm" ? store.footprint.muteUsers[id] : store.footprint.muteChannels[id]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tmp = type == "user" ? userData[id] : channelData[id];
|
const tmp = type == "dm" ? userData[id] : channelData[id];
|
||||||
if (!tmp) return;
|
if (!tmp) return;
|
||||||
if ("avatar" in tmp) {
|
if ("avatar" in tmp) {
|
||||||
// user
|
// user
|
||||||
@@ -100,6 +100,7 @@ const Session: FC<IProps> = ({
|
|||||||
messageData,
|
messageData,
|
||||||
loginUid
|
loginUid
|
||||||
});
|
});
|
||||||
|
const isVoicing = type == "channel" && voiceList.findIndex(item => item.id == id) > -1;
|
||||||
return (
|
return (
|
||||||
<li className="session">
|
<li className="session">
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
@@ -118,7 +119,7 @@ const Session: FC<IProps> = ({
|
|||||||
onContextMenu={handleContextMenuEvent}
|
onContextMenu={handleContextMenuEvent}
|
||||||
>
|
>
|
||||||
<div className="flex shrink-0 relative">
|
<div className="flex shrink-0 relative">
|
||||||
{type == "user" ? (
|
{type == "dm" ? (
|
||||||
<User avatarSize={40} compact interactive={false} uid={id} />
|
<User avatarSize={40} compact interactive={false} uid={id} />
|
||||||
) : (
|
) : (
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -130,7 +131,7 @@ const Session: FC<IProps> = ({
|
|||||||
src={icon}
|
src={icon}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{type == "channel" && voiceInfo.id == id && <IconVoicing className="-top-0.5 -right-0.5 absolute w-[18px] h-[18px]" />}
|
{isVoicing && <IconVoicing className="-top-0.5 -right-0.5 absolute w-[18px] h-[18px]" />}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col justify-between overflow-hidden">
|
<div className="w-full flex flex-col justify-between overflow-hidden">
|
||||||
<div className="flex items-center justify-between ">
|
<div className="flex items-center justify-between ">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import DeleteChannelConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
|||||||
import InviteModal from "../../../common/component/InviteModal";
|
import InviteModal from "../../../common/component/InviteModal";
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
export interface ChatSession {
|
export interface ChatSession {
|
||||||
type: "user" | "channel";
|
type: "dm" | "channel";
|
||||||
id: number;
|
id: number;
|
||||||
mid: number;
|
mid: number;
|
||||||
unread: number;
|
unread: number;
|
||||||
@@ -46,11 +46,11 @@ const SessionList: FC<Props> = ({ tempSession }) => {
|
|||||||
// console.log("adddd", id);
|
// console.log("adddd", id);
|
||||||
const mids = userMessage[id];
|
const mids = userMessage[id];
|
||||||
if (!mids || mids.length == 0) {
|
if (!mids || mids.length == 0) {
|
||||||
return { unreads: 0, id, type: "user" };
|
return { unreads: 0, id, type: "dm" };
|
||||||
}
|
}
|
||||||
// 先转换成数字,再排序
|
// 先转换成数字,再排序
|
||||||
const mid = [...mids].sort((a, b) => +a - +b).pop();
|
const mid = [...mids].sort((a, b) => +a - +b).pop();
|
||||||
return { type: "user", id, mid };
|
return { type: "dm", id, mid };
|
||||||
});
|
});
|
||||||
const temps = [...(cSessions as ChatSession[]), ...(uSessions as ChatSession[])].sort((a, b) => {
|
const temps = [...(cSessions as ChatSession[]), ...(uSessions as ChatSession[])].sort((a, b) => {
|
||||||
const { mid: aMid = 0 } = a;
|
const { mid: aMid = 0 } = a;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function ChatPage() {
|
|||||||
mid: 0,
|
mid: 0,
|
||||||
unread: 0,
|
unread: 0,
|
||||||
id: +user_id,
|
id: +user_id,
|
||||||
type: "user" as const
|
type: "dm" as const
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
// console.log("temp uid", tmpUid);
|
// console.log("temp uid", tmpUid);
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ export default function ConfigAgora() {
|
|||||||
<div className="input">
|
<div className="input">
|
||||||
<Label htmlFor="project_id">Project ID</Label>
|
<Label htmlFor="project_id">Project ID</Label>
|
||||||
<Input
|
<Input
|
||||||
|
spellCheck={false}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
// type={"number"}
|
|
||||||
data-type="project_id"
|
data-type="project_id"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
value={project_id}
|
value={project_id}
|
||||||
@@ -66,6 +66,7 @@ export default function ConfigAgora() {
|
|||||||
<div className="input">
|
<div className="input">
|
||||||
<Label htmlFor="app_id">App ID</Label>
|
<Label htmlFor="app_id">App ID</Label>
|
||||||
<Input
|
<Input
|
||||||
|
spellCheck={false}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
data-type="app_id"
|
data-type="app_id"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -77,6 +78,7 @@ export default function ConfigAgora() {
|
|||||||
<div className="input">
|
<div className="input">
|
||||||
<Label htmlFor="app_certificate">APP Certificate</Label>
|
<Label htmlFor="app_certificate">APP Certificate</Label>
|
||||||
<Input
|
<Input
|
||||||
|
spellCheck={false}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
data-type="app_certificate"
|
data-type="app_certificate"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -88,6 +90,7 @@ export default function ConfigAgora() {
|
|||||||
<div className="input">
|
<div className="input">
|
||||||
<Label htmlFor="rtm_key">RTM Key</Label>
|
<Label htmlFor="rtm_key">RTM Key</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
spellCheck={false}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
data-type="rtm_key"
|
data-type="rtm_key"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -99,6 +102,7 @@ export default function ConfigAgora() {
|
|||||||
<div className="input">
|
<div className="input">
|
||||||
<Label htmlFor="rtm_secret">RTM Secret</Label>
|
<Label htmlFor="rtm_secret">RTM Secret</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
spellCheck={false}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
data-type="rtm_secret"
|
data-type="rtm_secret"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import { IAgoraRTCClient, IRemoteAudioTrack } from "agora-rtc-sdk-ng";
|
import { IAgoraRTCClient, IMicrophoneAudioTrack, IRemoteAudioTrack } from "agora-rtc-sdk-ng";
|
||||||
|
|
||||||
interface BeforeInstallPromptEvent extends Event {
|
interface BeforeInstallPromptEvent extends Event {
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +33,7 @@ export declare global {
|
|||||||
CACHE: { [key: string]: typeof localforage | undefined };
|
CACHE: { [key: string]: typeof localforage | undefined };
|
||||||
VOICE_CLIENT?: IAgoraRTCClient;
|
VOICE_CLIENT?: IAgoraRTCClient;
|
||||||
VOICE_TRACK_MAP: {
|
VOICE_TRACK_MAP: {
|
||||||
[key: number]: IRemoteAudioTrack | undefined
|
[key: number]: IRemoteAudioTrack | IMicrophoneAudioTrack | undefined | null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interface WindowEventMap {
|
interface WindowEventMap {
|
||||||
|
|||||||
Reference in New Issue
Block a user