From 88a8f061b55845707fb5e92dcd167453076c881b Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Thu, 23 Mar 2023 10:33:23 +0800 Subject: [PATCH] feat: init agora --- package.json | 3 +- src/app/slices/auth.data.ts | 18 ++++--- src/app/slices/users.ts | 1 + src/assets/icons/headphone.svg | 4 +- src/routes/chat/ChannelChat/index.tsx | 2 + src/routes/chat/RTCWidget.tsx | 37 +++++++++++++ src/routes/chat/SessionList/index.tsx | 2 +- src/routes/chat/VoiceChat/Dashboard.tsx | 70 +++++++++++++++++++++++++ src/routes/chat/VoiceChat/index.tsx | 36 +++++++++++++ src/routes/chat/index.tsx | 6 ++- src/routes/setting/navs.tsx | 10 ++-- yarn.lock | 12 +++++ 12 files changed, 184 insertions(+), 17 deletions(-) create mode 100644 src/routes/chat/RTCWidget.tsx create mode 100644 src/routes/chat/VoiceChat/Dashboard.tsx create mode 100644 src/routes/chat/VoiceChat/index.tsx diff --git a/package.json b/package.json index ffaf53eb..43ee5956 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vocechat-web", - "version": "0.3.53", + "version": "0.3.54", "private": true, "homepage": "https://voce.chat", "dependencies": { @@ -19,6 +19,7 @@ "@types/react-dom": "^18.0.11", "@udecode/plate": "16.8", "@uiball/loaders": "^1.2.6", + "agora-rtc-sdk-ng": "^4.17.0", "bfj": "^7.0.2", "broadcast-channel": "^4.20.2", "browserslist": "^4.21.5", diff --git a/src/app/slices/auth.data.ts b/src/app/slices/auth.data.ts index 335aff18..34592bc2 100644 --- a/src/app/slices/auth.data.ts +++ b/src/app/slices/auth.data.ts @@ -9,17 +9,18 @@ import { KEY_UID } from "../config"; import { AuthData, RenewTokenResponse } from "../../types/auth"; -import { User } from "../../types/user"; +import { StoredUser } from "./users"; // import { updateUsersByLogs } from './users'; interface State { initialized: boolean; guest: boolean; - user: User | undefined; + user: StoredUser | undefined; token: string; expireTime: number; refreshToken: string; roleChanged: boolean; + voice: boolean; } const loginUser = localStorage.getItem(KEY_LOGIN_USER) || ""; const initialState: State = { @@ -29,7 +30,8 @@ const initialState: State = { token: localStorage.getItem(KEY_TOKEN) || "", expireTime: Number(localStorage.getItem(KEY_EXPIRE) || +new Date()), refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN) || "", - roleChanged: false + roleChanged: false, + voice: false }; const emptyState: State = { @@ -39,7 +41,8 @@ const emptyState: State = { token: "", expireTime: +new Date(), refreshToken: "", - roleChanged: false + roleChanged: false, + voice: false }; const authDataSlice = createSlice({ @@ -64,7 +67,7 @@ const authDataSlice = createSlice({ localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token); localStorage.setItem(KEY_UID, `${uid}`); }, - updateLoginUser(state, { payload }: PayloadAction>) { + updateLoginUser(state, { payload }: PayloadAction>) { if (!state.user) return; const obj = { ...state.user, ...payload }; @@ -82,6 +85,9 @@ const authDataSlice = createSlice({ updateRoleChanged(state, action: PayloadAction) { state.roleChanged = action.payload; }, + updateVoiceStatus(state, action: PayloadAction) { + state.voice = action.payload; + }, resetAuthData() { // remove local data localStorage.removeItem(KEY_EXPIRE); @@ -128,5 +134,5 @@ const authDataSlice = createSlice({ // } }); -export const { updateInitialized, updateLoginUser, setAuthData, resetAuthData, updateToken, updateRoleChanged } = authDataSlice.actions; +export const { updateInitialized, updateLoginUser, setAuthData, resetAuthData, updateToken, updateRoleChanged, updateVoiceStatus } = authDataSlice.actions; export default authDataSlice.reducer; diff --git a/src/app/slices/users.ts b/src/app/slices/users.ts index 6b0b845b..18664e25 100644 --- a/src/app/slices/users.ts +++ b/src/app/slices/users.ts @@ -6,6 +6,7 @@ import { UserLog, UserState } from "../../types/sse"; export interface StoredUser extends User { online?: boolean; + voice?: boolean; avatar?: string; } diff --git a/src/assets/icons/headphone.svg b/src/assets/icons/headphone.svg index 43033d6f..a0cfee72 100644 --- a/src/assets/icons/headphone.svg +++ b/src/assets/icons/headphone.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/routes/chat/ChannelChat/index.tsx b/src/routes/chat/ChannelChat/index.tsx index 8efd2bc2..9e9b5854 100644 --- a/src/routes/chat/ChannelChat/index.tsx +++ b/src/routes/chat/ChannelChat/index.tsx @@ -16,6 +16,7 @@ import IconPin from "../../../assets/icons/pin.svg"; import { useAppSelector } from "../../../app/store"; import GoBackNav from "../../../common/component/GoBackNav"; import Members from "./Members"; +import VoiceChat from "../VoiceChat"; type Props = { cid?: number; dropFiles?: File[]; @@ -83,6 +84,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) { + { + const { loginUser, voicing } = useAppSelector(store => { return { loginUser: store.authData.user, voicing: store.authData.voice }; }); + if (!loginUser) return null; + + return ( +
+ {voicing &&
+
+ Voice Connected +
+
} +
+
+ +
+ {loginUser.name} + #{loginUser.uid} +
+
+
+ + +
+
+
+ ); +}; + +export default RTCWidget; \ No newline at end of file diff --git a/src/routes/chat/SessionList/index.tsx b/src/routes/chat/SessionList/index.tsx index 4eda75bf..bfc3d061 100644 --- a/src/routes/chat/SessionList/index.tsx +++ b/src/routes/chat/SessionList/index.tsx @@ -74,7 +74,7 @@ const SessionList: FC = ({ tempSession }) => { return ( <> -
    +
      { + const dispatch = useDispatch(); + console.log("aaaaa"); + + const { agoraConfig } = useConfig("agora"); + useEffect(() => { + console.log("aaa", agoraConfig); + const startConnect = async () => { + // Create an instance of the Agora Engine + const agoraEngine = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }); + + // Listen for the "user-published" event to retrieve an AgoraRTCRemoteUser object. + agoraEngine.on("user-published", async (user, mediaType) => { + // Subscribe to the remote user when the SDK triggers the "user-published" event. + await agoraEngine.subscribe(user, mediaType); + console.log("subscribe success"); + + // // Subscribe and play the remote audio track. + // if (mediaType == "audio") + // { + // channelParameters.remoteUid=user.uid; + // // Get the RemoteAudioTrack object from the AgoraRTCRemoteUser object. + // channelParameters.remoteAudioTrack = user.audioTrack; + // // Play the remote audio track. + // channelParameters.remoteAudioTrack.play(); + // console.log("Remote user connected: " + user.uid); + + // } + + // Listen for the "user-unpublished" event. + agoraEngine.on("user-unpublished", user => { + console.log(user.uid + "has left the channel"); + console.log("Remote user has left the channel"); + }); + }); + // Join a channel. + console.log("join data ", agoraConfig); + await agoraEngine.join(agoraConfig!.app_id, agoraConfig!.rtm_key, agoraConfig!.rtm_secret, uid); + console.log("Joined channel: " + agoraConfig!.rtm_key); + // // Create a local audio track from the microphone audio. + // channelParameters.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack(); + // // Publish the local audio track in the channel. + // await agoraEngine.publish(channelParameters.localAudioTrack); + // console.log("Publish success!"); + dispatch(updateVoiceStatus(true)); + }; + + if (!voicing && agoraConfig) { + startConnect(); + } + }, [voicing, agoraConfig, uid, channel]); + + return ( +
      Dashboard
      + ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/src/routes/chat/VoiceChat/index.tsx b/src/routes/chat/VoiceChat/index.tsx new file mode 100644 index 00000000..450fee6a --- /dev/null +++ b/src/routes/chat/VoiceChat/index.tsx @@ -0,0 +1,36 @@ +// import React from 'react'; +import Tippy from '@tippyjs/react'; +import { useTranslation } from 'react-i18next'; +import { useAppSelector } from '../../../app/store'; +import IconHeadphone from '../../../assets/icons/headphone.svg'; +import Tooltip from '../../../common/component/Tooltip'; +import Dashboard from './Dashboard'; + +type Props = { + channel: string +} + +const VoiceChat = ({ channel }: Props) => { + const { loginUser, voice } = useAppSelector(store => { return { loginUser: store.authData.user, voice: store.authData.voice }; }); + const { t } = useTranslation("chat"); + const toolClass = `relative cursor-pointer`; + if (!loginUser) return null; + return ( + + } + > +
    • + +
    • +
      +
      + ); +}; + +export default VoiceChat; \ No newline at end of file diff --git a/src/routes/chat/index.tsx b/src/routes/chat/index.tsx index 5074c797..dc900738 100644 --- a/src/routes/chat/index.tsx +++ b/src/routes/chat/index.tsx @@ -14,6 +14,7 @@ import GuestBlankPlaceholder from "./GuestBlankPlaceholder"; import GuestChannelChat from "./GuestChannelChat"; import GuestSessionList from "./GuestSessionList"; import ErrorCatcher from "../../common/component/ErrorCatcher"; +import RTCWidget from "./RTCWidget"; function ChatPage() { const isHomePath = useMatch(`/`); @@ -62,9 +63,10 @@ function ChatPage() { )}> {isGuest ? : } - {isGuest &&
      + + {isGuest ? } +
      : }
      {placeholderVisible && (isGuest ? : )} diff --git a/src/routes/setting/navs.tsx b/src/routes/setting/navs.tsx index 261fe5b1..ef4b24a7 100644 --- a/src/routes/setting/navs.tsx +++ b/src/routes/setting/navs.tsx @@ -11,7 +11,7 @@ import BotConfig from "./BotConfig"; import APIDocument from "./APIDocument"; import ManageMembers from "../../common/component/ManageMembers"; import Version from "../../common/component/Version"; -// import ConfigAgora from "./config/Agora"; +import ConfigAgora from "./config/Agora"; import { useAppSelector } from "../../app/store"; import ServerVersionChecker from "../../common/component/ServerVersionChecker"; import useLicense from "../../common/hook/useLicense"; @@ -47,10 +47,10 @@ const navs = [ name: "firebase", component: }, - // { - // name: "agora", - // component: - // }, + { + name: "agora", + component: + }, { name: "smtp", component: diff --git a/yarn.lock b/yarn.lock index a0a0ef8e..1efc0800 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3879,6 +3879,18 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +agora-rtc-sdk-ng@^4.17.0: + version "4.17.0" + resolved "https://mirrors.cloud.tencent.com/npm/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.17.0.tgz#5824fcd2613a82ab9e946d451007d0f35b0b91a2" + integrity sha512-TmqnvWr7J9cVm62KzE3jhBjVcQpcvxj0GoSMHUylce5GWZrN6rkDLgcPNvhNmPPK5Slqwsx/d70FrWM7T3ngQw== + dependencies: + agora-rte-extension "^1.2.3" + +agora-rte-extension@^1.2.3: + version "1.2.3" + resolved "https://mirrors.cloud.tencent.com/npm/agora-rte-extension/-/agora-rte-extension-1.2.3.tgz#979b96df0146300296f9f37212ffa67656c698ef" + integrity sha512-k3yNrYVyzJRoQJjaJUktKUI1XRtf8J1XsW8OzYKFqGlS8WQRMsES1+Phj2rfuEriiLObfuyuCimG6KHQCt5tiw== + ajv-formats@^2.1.1: version "2.1.1" resolved "https://mirrors.cloud.tencent.com/npm/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"