From b42fc7f4626da3913897594b8524697280977a47 Mon Sep 17 00:00:00 2001 From: yj <1336058017@qq.com> Date: Tue, 15 Oct 2024 10:37:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=A7=86=E9=A2=91=E5=B0=8F?= =?UTF-8?q?=E7=AA=97=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 12 ++- src/App.tsx | 8 +- src/components/UserVideo/index.tsx | 2 +- .../CurrentSpeakUserWindow/index.module.scss | 19 +++++ .../Meeting/CurrentSpeakUserWindow/index.tsx | 33 ++++++++ src/page/Meeting/index.module.scss | 11 +++ src/page/Meeting/index.tsx | 83 ++++++++++++++++--- src/utils/package/agora.ts | 6 +- src/utils/styles/App.scss | 5 ++ 9 files changed, 156 insertions(+), 23 deletions(-) create mode 100644 src/page/Meeting/CurrentSpeakUserWindow/index.module.scss create mode 100644 src/page/Meeting/CurrentSpeakUserWindow/index.tsx diff --git a/main.js b/main.js index 7079b02..b0670dc 100644 --- a/main.js +++ b/main.js @@ -161,7 +161,6 @@ app.on('ready', () => { mainWindow.on('move', () => { // 如果是全屏自动恢复到上次窗口大小 if (isMaximized) { - mainWindow.setResizable(true) mainWindow.unmaximize() isMaximized = false; } @@ -177,10 +176,8 @@ app.on('ready', () => { break; case 'maximize': mainWindow.maximize() - mainWindow.setResizable(false) break; case 'unmaximize': - mainWindow.setResizable(true) mainWindow.unmaximize() break; case 'minimize': @@ -438,6 +435,10 @@ function windowOperation(config) { y = height - child.getSize()[1]; child.setPosition(40, y - 200); break; + case 'currentSpeakUserWindow': + x = width - child.getSize()[0]; + child.setPosition(x - 40, 250); + break; } } // 主窗口居中 @@ -449,5 +450,8 @@ function mainWindowCenter() { } // 主窗口隐藏 function mainWindowHide() { - mainWindow.setPosition(-999999, -999999); + const display = screen.getDisplayMatching({ ...mainWindow.getBounds() }); + const { width, height } = display.size + x = width - mainWindow.getSize()[0]; + mainWindow.setPosition(x - 40, 40); } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 0e5632a..641852f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,8 +20,9 @@ import { GetLeave } from "@/api/Meeting"; import path from "path"; import ShareScreenWindow from "@/page/Meeting/ShareScreenWindow"; import UserListWindow from "@/page/Meeting/UserListWindow"; -import ChatSmallWindow from "./page/Meeting/ChatSmallWindow"; -import ChatBigWindow from "./page/Meeting/ChatBigWindow"; +import ChatSmallWindow from "@/page/Meeting/ChatSmallWindow"; +import ChatBigWindow from "@/page/Meeting/ChatBigWindow"; +import CurrentSpeakUserWindow from "@/page/Meeting/CurrentSpeakUserWindow"; const fs = require('fs').promises; const { exec } = require('child_process'); const App: React.FC = () => { @@ -36,7 +37,7 @@ const App: React.FC = () => { }); const [spinning, setSpinning] = useState(false); const [isState, setIsState] = useState(true); - const urlHashArr = ['#/userListWindow', '#/shareScreenWindow', '#/chatSmallWindow', '#/chatBigWindow'] + const urlHashArr = ['#/userListWindow', '#/shareScreenWindow', '#/chatSmallWindow', '#/chatBigWindow', '#/currentSpeakUserWindow'] if (urlHashArr.indexOf(location.hash) == -1) { useEffect(() => { let userInfo = JSON.parse(storage.getItem('user') as string) @@ -253,6 +254,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> } /> diff --git a/src/components/UserVideo/index.tsx b/src/components/UserVideo/index.tsx index 3a5cfc1..de403fe 100644 --- a/src/components/UserVideo/index.tsx +++ b/src/components/UserVideo/index.tsx @@ -60,7 +60,7 @@ const UserVideo: React.FC = () => { useEffect(() => { userList.forEach(async (item: any) => { - await agora.destroyRendererByConfig(Number('1' + item.screenShareId)) + await agora.destroyRendererByConfig(Number('1' + item.screenShareId), state.channelId + 'a') await agora.setupRemoteVideoEx({ uid: Number('1' + item.screenShareId), view: document.getElementById(`video-${item.screenShareId}`), diff --git a/src/page/Meeting/CurrentSpeakUserWindow/index.module.scss b/src/page/Meeting/CurrentSpeakUserWindow/index.module.scss new file mode 100644 index 0000000..4b7bf32 --- /dev/null +++ b/src/page/Meeting/CurrentSpeakUserWindow/index.module.scss @@ -0,0 +1,19 @@ +.currentSpeakUserWindow { + color: white; + width: 100%; + display: flex; + flex-direction: column; + height: 100%; + background-color: #16191E; + padding: 4px; + box-sizing: border-box; + + >div { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-width: 100%; + width: fit-content; + font-size: 14px; + } +} \ No newline at end of file diff --git a/src/page/Meeting/CurrentSpeakUserWindow/index.tsx b/src/page/Meeting/CurrentSpeakUserWindow/index.tsx new file mode 100644 index 0000000..ca34566 --- /dev/null +++ b/src/page/Meeting/CurrentSpeakUserWindow/index.tsx @@ -0,0 +1,33 @@ +import styles from '@/page/Meeting/CurrentSpeakUserWindow/index.module.scss' +import { useEffect, useState } from "react"; +const CurrentSpeakUserWindow: React.FC = () => { + const [inputValue, setInputValue] = useState('') + const channel = new BroadcastChannel('meeting_channel'); + useEffect(() => { + channel.onmessage = function (event) { + const { type, currentSpeakUser } = event.data; + switch (type) { + case 'currentSpeakUser': + if (currentSpeakUser.length) { + setInputValue(currentSpeakUser.join(',')) + } else { + setInputValue('') + } + break; + } + } + }, []); + + + return ( + <> +
+
+ {inputValue ? `${inputValue}正在说话` : '无人说话'} +
+
+ + ) +} + +export default CurrentSpeakUserWindow diff --git a/src/page/Meeting/index.module.scss b/src/page/Meeting/index.module.scss index 262c256..69586e3 100644 --- a/src/page/Meeting/index.module.scss +++ b/src/page/Meeting/index.module.scss @@ -94,6 +94,17 @@ background-color: #1F2022; display: flex; flex-direction: column; + position: relative; + + .meetingAbsolute { + position: absolute; + width: 100%; + height: 100%; + background-color: #1F2022; + left: 0; + top: 0; + z-index: 3000; + } .meetingHeader { display: flex; diff --git a/src/page/Meeting/index.tsx b/src/page/Meeting/index.tsx index 60825df..e92c961 100644 --- a/src/page/Meeting/index.tsx +++ b/src/page/Meeting/index.tsx @@ -844,18 +844,43 @@ const Meeting: React.FC = () => { } }, onAudioVolumeIndication: async (speakers: AudioVolumeInfo[]) => { - speakers.forEach((item: any) => { - let domMe = document.getElementById(`micr-item-${userInfo.uid}`); - let dom = document.getElementById(`micr-${item.uid ? item.uid : userInfo.uid}`); - if (dom) { - const percentage = (item.volume / 255) * 100 - dom.style.height = `${percentage}%` - } - if (domMe && !item.uid) { - const percentage = (item.volume / 255) * 100 - domMe.style.height = `${percentage}%` - } - }); + async function checkUidsInUsers(uids: number[]): Promise { + return new Promise(resolve => { + const usernames: string[] = []; + uids.forEach(uid => { + setRoomUserList((res: any) => { + const user = res.find((item: any) => item.uid == uid); + if (user) { + usernames.push(user.userName); + } + return res + }) + }); + if (uids.length === usernames.length) { + resolve(usernames) + } + }) + } + if (speakers.length) { + speakers.forEach((item: any) => { + let domMe = document.getElementById(`micr-item-${userInfo.uid}`); + let dom = document.getElementById(`micr-${item.uid ? item.uid : userInfo.uid}`); + if (dom) { + const percentage = (item.volume / 255) * 100 + dom.style.height = `${percentage}%` + } + if (domMe && !item.uid) { + const percentage = (item.volume / 255) * 100 + domMe.style.height = `${percentage}%` + } + }); + const uidArr = (speakers.filter((item: any) => item.volume)).map(item => item.uid || userInfo.uid) as number[]; + const currentSpeakUser = await checkUidsInUsers(uidArr) + channel.postMessage({ + type: 'currentSpeakUser', + currentSpeakUser, + }); + } }, onNetworkQuality: async (_connection: RtcConnection, remoteUid: number, _txQuality: QualityType, rxQuality: QualityType) => { if (remoteUid === 0) { @@ -1416,7 +1441,26 @@ const Meeting: React.FC = () => { height: 300, key: 'chatSmallWindow', }) + window.electron.createChildWindow({ + url: location.origin + `/#/currentSpeakUserWindow`, + width: 350, + height: 30, + key: 'currentSpeakUserWindow', + }) setKeyOpenChildWindow('shareScreenWindow', true) + window.electron.setMainWindowSize({ + width: 350, + height: 200, + }) + setTimeout(() => { + agora.setupLocalVideo({ + uid: Number(user.screenShareId), + view: document.getElementById(`meetingAbsoluteVideo`) as HTMLElement, + channelId: state.channelId, + sourceType: VideoSourceType.VideoSourceScreen, + type: true + }) + }, 1500); } } else { message.error('请选择应用!') @@ -1457,11 +1501,23 @@ const Meeting: React.FC = () => { const stopScreenCapture = async (): Promise => { const footerListTemplate = [...footerList] await agora.leaveChannelEx(userInfo.screenShareId) + await agora.destroyRendererByConfig(Number(userInfo.screenShareId), state.channelId) agora.stopScreenCapture() footerListTemplate[1][0].title = '共享屏幕' setFooterList(footerListTemplate) window.electron.closeChildWindow('shareScreenWindow') setKeyOpenChildWindow('shareScreenWindow', false) + window.electron.getWindowSize().then((res: any) => { + window.electron.setMainWindowSize({ + width: Math.ceil(res.width / 1.5), + height: Math.ceil(res.height / 1.3), + }) + window.electron.getIsMaximized().then((b: boolean) => { + if (!b) { + window.electron.setViewStatus('maximize') + } + }) + }) } // 获取房间用户 const getRoomUser = async (): Promise => { @@ -1796,6 +1852,9 @@ const Meeting: React.FC = () => { return ( <>
+ {isShare ?
+ +
: null} {contextHolder}
diff --git a/src/utils/package/agora.ts b/src/utils/package/agora.ts index 7819346..6f857ce 100644 --- a/src/utils/package/agora.ts +++ b/src/utils/package/agora.ts @@ -165,7 +165,7 @@ export const agora = { }, // 本地加入 setupLocalVideo: async (item: any) => { - if (item.view?.childNodes.length === 1) { + if (item.view?.childNodes.length === 1 || item.type) { await rtcEngine.setupLocalVideo({ renderMode: agora.getRrenderMode(item.uid), sourceType: item.sourceType, @@ -323,8 +323,8 @@ export const agora = { rtcEngine.muteRemoteVideoStream(uid, mute) }, // 销毁视频渲染dom - destroyRendererByConfig: async (uid: number) => { - await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, option.channelId + 'a', uid); + destroyRendererByConfig: async (uid: number, channelId: string) => { + await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid); }, // ai降噪 setAINSMode: async (enabled: boolean, mode: AudioAinsMode) => { diff --git a/src/utils/styles/App.scss b/src/utils/styles/App.scss index 3b5551f..1c1cf96 100644 --- a/src/utils/styles/App.scss +++ b/src/utils/styles/App.scss @@ -376,6 +376,7 @@ $pagination-hover-background-color: #5575F2; .ant-message { -webkit-app-region: no-drag; } + // ant-spin-fullscreen .ant-spin-fullscreen { z-index: 10000; @@ -383,4 +384,8 @@ $pagination-hover-background-color: #5575F2; .ant-picker-dropdown { -webkit-app-region: no-drag; +} + +.ant-tabs { + -webkit-app-region: no-drag; } \ No newline at end of file