diff --git a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Controls.tsx b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Controls.tsx
index 934f74e3..56136f7f 100644
--- a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Controls.tsx
+++ b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Controls.tsx
@@ -1,45 +1,46 @@
import { IconBtn } from '@capital/component';
-import React, { useState } from 'react';
+import React from 'react';
import { useClient, useMicrophoneAndCameraTracks } from './client';
import { useMeetingStore } from './store';
+import { getClientLocalTrack } from './utils';
export const Controls: React.FC<{
onClose: () => void;
}> = React.memo((props) => {
const client = useClient();
- const [trackState, setTrackState] = useState({ video: false, audio: false });
const { ready, tracks } = useMicrophoneAndCameraTracks();
+ const mediaPerm = useMeetingStore((state) => state.mediaPerm);
const mute = async (type: 'audio' | 'video') => {
if (type === 'audio') {
- if (trackState.audio === true) {
- // await tracks[0].setEnabled(false);
- await client.unpublish(tracks[0]);
+ if (mediaPerm.audio === true) {
+ const track = getClientLocalTrack(client, 'audio');
+ if (track) {
+ await client.unpublish(track);
+ }
} else {
- // await tracks[0].setEnabled(true);
await client.publish(tracks[0]);
}
- setTrackState((ps) => {
- return { ...ps, audio: !ps.audio };
- });
+
+ useMeetingStore.getState().setMediaPerm({ audio: !mediaPerm.audio });
} else if (type === 'video') {
- if (trackState.video === true) {
- // await tracks[1].setEnabled(false);
- await client.unpublish(tracks[1]);
+ if (mediaPerm.video === true) {
+ const track = getClientLocalTrack(client, 'video');
+ if (track) {
+ await client.unpublish(track);
+ }
} else {
- // await tracks[1].setEnabled(true);
await client.publish(tracks[1]);
}
- setTrackState((ps) => {
- return { ...ps, video: !ps.video };
- });
+
+ useMeetingStore.getState().setMediaPerm({ video: !mediaPerm.video });
}
};
const leaveChannel = async () => {
await client.leave();
client.removeAllListeners();
- useMeetingStore.getState().clearUser();
+ useMeetingStore.getState().reset();
// we close the tracks to perform cleanup
tracks[0].close();
tracks[1].close();
@@ -49,16 +50,16 @@ export const Controls: React.FC<{
return (
mute('video')}
/>
mute('audio')}
diff --git a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/VideoView.tsx b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/VideoView.tsx
index fb368b9d..6543e1d7 100644
--- a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/VideoView.tsx
+++ b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/VideoView.tsx
@@ -2,6 +2,8 @@ import { UserName } from '@capital/component';
import { AgoraVideoPlayer, IAgoraRTCRemoteUser } from 'agora-rtc-react';
import React from 'react';
import styled from 'styled-components';
+import { useClient, useMicrophoneAndCameraTracks } from './client';
+import { useMeetingStore } from './store';
const Root = styled.div`
width: 95%;
@@ -12,6 +14,7 @@ const Root = styled.div`
aspect-ratio: 16/9;
justify-self: center;
align-self: center;
+ overflow: hidden;
.player {
width: 100%;
@@ -42,3 +45,20 @@ export const VideoView: React.FC<{
);
};
VideoView.displayName = 'VideoView';
+
+export const OwnVideoView: React.FC<{}> = React.memo(() => {
+ const { ready, tracks } = useMicrophoneAndCameraTracks();
+ const client = useClient();
+ const mediaPerm = useMeetingStore((state) => state.mediaPerm);
+
+ return (
+
+ {ready && mediaPerm.video && (
+
+ )}
+
+
+
+ );
+});
+OwnVideoView.displayName = 'OwnVideoView';
diff --git a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Videos.tsx b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Videos.tsx
index 8df06b2f..27f17298 100644
--- a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Videos.tsx
+++ b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/Videos.tsx
@@ -1,26 +1,20 @@
-import { AgoraVideoPlayer } from 'agora-rtc-react';
import React from 'react';
import styled from 'styled-components';
-import { useMicrophoneAndCameraTracks } from './client';
import { useMeetingStore } from './store';
-import { VideoView } from './VideoView';
+import { OwnVideoView, VideoView } from './VideoView';
const Root = styled.div`
height: 70vh;
- /* align-self: flex-start; */
display: grid;
grid-template-columns: repeat(auto-fit, minmax(440px, 1fr));
`;
export const Videos: React.FC = React.memo(() => {
const users = useMeetingStore((state) => state.users);
- const { ready, tracks } = useMicrophoneAndCameraTracks();
return (
- {/* AgoraVideoPlayer component takes in the video track to render the stream,
- you can pass in other props that get passed to the rendered div */}
- {ready && }
+
{users.length > 0 &&
users.map((user) => {
diff --git a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/store.ts b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/store.ts
index 5b8a279d..5e23194d 100644
--- a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/store.ts
+++ b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/store.ts
@@ -1,22 +1,34 @@
import type { IAgoraRTCRemoteUser } from 'agora-rtc-react';
import create from 'zustand';
+interface MediaPerm {
+ video: boolean;
+ audio: boolean;
+}
+
interface MeetingState {
/**
* 本次会议用户列表
*/
users: IAgoraRTCRemoteUser[];
+ /**
+ * 本地媒体权限
+ */
+ mediaPerm: MediaPerm;
appendUser: (user: IAgoraRTCRemoteUser) => void;
removeUser: (user: IAgoraRTCRemoteUser) => void;
- clearUser: () => void;
/**
* 更新用户信息
*/
updateUserInfo: (user: IAgoraRTCRemoteUser) => void;
+ setMediaPerm: (perm: Partial) => void;
+
+ reset: () => void;
}
export const useMeetingStore = create((set) => ({
users: [],
+ mediaPerm: { video: false, audio: false },
appendUser: (user: IAgoraRTCRemoteUser) => {
set((state) => ({
users: [...state.users, user],
@@ -29,9 +41,6 @@ export const useMeetingStore = create((set) => ({
};
});
},
- clearUser: () => {
- set({ users: [] });
- },
updateUserInfo: (user: IAgoraRTCRemoteUser) => {
set((state) => {
const users = [...state.users];
@@ -47,4 +56,19 @@ export const useMeetingStore = create((set) => ({
};
});
},
+ setMediaPerm: (perm: Partial) => {
+ set((state) => ({
+ mediaPerm: {
+ ...state.mediaPerm,
+ ...perm,
+ },
+ }));
+ },
+
+ reset: () => {
+ set({
+ users: [],
+ mediaPerm: { video: false, audio: false },
+ });
+ },
}));
diff --git a/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/utils.ts b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/utils.ts
new file mode 100644
index 00000000..c98a24f3
--- /dev/null
+++ b/server/plugins/com.msgbyte.agora/web/plugins/com.msgbyte.agora/src/FloatWindow/utils.ts
@@ -0,0 +1,15 @@
+import type { IAgoraRTCClient, ILocalTrack } from 'agora-rtc-react';
+
+/**
+ * 获取客户端本地轨道
+ */
+export function getClientLocalTrack(
+ client: IAgoraRTCClient,
+ trackMediaType: 'audio' | 'video'
+): ILocalTrack | null {
+ return (
+ client.localTracks.find(
+ (track) => track.trackMediaType === trackMediaType
+ ) ?? null
+ );
+}