From 1c1f281789ab0b00007019f51b0273117a386b5e Mon Sep 17 00:00:00 2001 From: xjs Date: Fri, 11 Apr 2025 16:43:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=8E=89=E7=BA=BF?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- src/config/config.ts | 2 +- src/lib/signal.ts | 69 ++++++++++++++++++ src/lib/useCommon.ts | 1 - .../MainArea/Room/AudioController.tsx | 15 +++- .../MainPage/MainArea/Room/index.module.less | 14 ++++ yarn.lock | 71 +++++++++++++++++++ 7 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 src/lib/signal.ts diff --git a/package.json b/package.json index b71efdd..17db6e6 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "private": true, "dependencies": { "@arco-design/web-react": "^2.65.0", + "@microsoft/signalr": "^8.0.7", "@reduxjs/toolkit": "^1.8.3", "@volcengine/rtc": "4.66.1", "autolinker": "^4.0.0", @@ -25,7 +26,7 @@ "server:start": "node Server/app.js", "build": "craco build", "upload": "bash ./upload.sh", - "build-and-upload": "pnpm run build && pnpm run upload", + "build-and-upload": "pnpm run build && pnpm run upload", "test": "craco test", "eject": "react-scripts eject", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", diff --git a/src/config/config.ts b/src/config/config.ts index cad3041..8dd41c8 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -184,7 +184,7 @@ export class ConfigFactory { }, IgnoreBracketText: [1, 2, 3, 4, 5], BidirectionAdditions:{ - disable_markdown_filter:false, + disable_markdown_filter:true, } }; } diff --git a/src/lib/signal.ts b/src/lib/signal.ts new file mode 100644 index 0000000..7ed63bf --- /dev/null +++ b/src/lib/signal.ts @@ -0,0 +1,69 @@ +import * as signalR from '@microsoft/signalr'; +import { useEffect, useRef } from 'react'; +import { Message } from '@arco-design/web-react'; +import { useLeave } from './useCommon'; + + +export const useSignalRConnection = (params: { access_token: string; roomId: string }) => { + const connectionRef = useRef(null); + const leaveRoom = useLeave(); + + useEffect(() => { + if (!params.access_token || !params.roomId) { + return; + } + + const connection = new signalR.HubConnectionBuilder() + .withServerTimeout(30000) + .withAutomaticReconnect() + .withUrl(`https://api.v3.ycymedu.com/hubs/weminpro?access_token=${params.access_token}&roomId=${params.roomId}`) + .configureLogging(signalR.LogLevel.Information) + .build(); + + connectionRef.current = connection; + + connection.on('connected', () => { + console.log('Connected to SignalR hub'); + }); + + connection.on('disconnected', () => { + console.log('Disconnected from SignalR hub'); + }); + + connection.on("ForceOffline", function (msg) { + // 可加逻辑:注销用户、跳转登录页等 + leaveRoom() + }); + + connection.on("SendWarn", function (msg) { + console.warn(`下线提醒:${msg}"`); + // 也可以弹窗提醒用户保存数据 + Message.warning(msg) + }); + + connection.start() + .then(() => { + console.log("SignalR连接已建立"); + setInterval(() => { + connection.invoke("Ping"); + }, 5000); + }) + .catch(err => { + console.error("SignalR连接失败:", err); + }); + + return () => { + if (connectionRef.current) { + connectionRef.current.stop() + .then(() => { + console.log("SignalR连接已关闭"); + }) + .catch(err => { + console.error("关闭SignalR连接失败:", err); + }); + } + }; + }, [params.access_token, params.roomId]); + + return connectionRef.current; +}; diff --git a/src/lib/useCommon.ts b/src/lib/useCommon.ts index a184486..e670e26 100644 --- a/src/lib/useCommon.ts +++ b/src/lib/useCommon.ts @@ -102,7 +102,6 @@ export const useJoin = (): [ const token = room.aiConfig.user_token; const initMsg = room.initMsg - /** 1. Create RTC Engine */ await RtcClient.createEngine({ diff --git a/src/pages/MainPage/MainArea/Room/AudioController.tsx b/src/pages/MainPage/MainArea/Room/AudioController.tsx index bea26e8..7f9a6ab 100644 --- a/src/pages/MainPage/MainArea/Room/AudioController.tsx +++ b/src/pages/MainPage/MainArea/Room/AudioController.tsx @@ -5,6 +5,9 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom'; +import {useSignalRConnection} from "@/lib/signal" + import { RootState } from '@/store'; import RtcClient from '@/lib/RtcClient'; import { setInterruptMsg } from '@/store/slices/room'; @@ -13,18 +16,24 @@ import { COMMAND } from '@/utils/handler'; import style from './index.module.less'; import LockMicroIcon from '@/assets/img/lockmicrophone.png'; import MicroIcon from '@/assets/img/microphone.png'; -import HandleOffIcon from "@/assets/img/handoff.png" +import HandleOffIcon from '@/assets/img/handoff.png'; + const THRESHOLD_VOLUME = 18; + function AudioController(props: React.HTMLAttributes) { const { className, ...rest } = props; const dispatch = useDispatch(); const room = useSelector((state: RootState) => state.room); const volume = room.localUser.audioPropertiesInfo?.linearVolume || 0; - const { isAudioPublished,switchMic } = useDeviceState(); + const { isAudioPublished, switchMic } = useDeviceState(); - const { isAITalking} = useSelector((state: RootState) => state.room); + const [searchParams] = useSearchParams(); + + useSignalRConnection({access_token:searchParams.get('token') ||'',roomId:room.roomId || ''}) + + const { isAITalking } = useSelector((state: RootState) => state.room); const isLoading = volume >= THRESHOLD_VOLUME && isAudioPublished; const leaveRoom = useLeave(); diff --git a/src/pages/MainPage/MainArea/Room/index.module.less b/src/pages/MainPage/MainArea/Room/index.module.less index ebd7548..28085bb 100644 --- a/src/pages/MainPage/MainArea/Room/index.module.less +++ b/src/pages/MainPage/MainArea/Room/index.module.less @@ -137,6 +137,7 @@ display: flex; align-items: center; justify-content: center; + flex-wrap: wrap; gap: 16px; background-color: #fff; padding: 8px 15px; @@ -218,6 +219,19 @@ padding: 10px 15px; border-radius: 400px; } + + .timerWrapper { + display: flex; + align-items: center; + justify-content: center; + background-color: #f6f6f6; + padding: 8px 14px; + border-radius: 400px; + color: #0078ff; + font-size: 14px; + font-weight: 500; + width:100%; + } } } diff --git a/yarn.lock b/yarn.lock index 2f1e555..0a0785d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1536,6 +1536,17 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@microsoft/signalr@^8.0.7": + version "8.0.7" + resolved "https://registry.yarnpkg.com/@microsoft/signalr/-/signalr-8.0.7.tgz#94419ddbf9418753e493f4ae4c13990316ec2ea5" + integrity sha512-PHcdMv8v5hJlBkRHAuKG5trGViQEkPYee36LnJQx4xHOQ5LL4X0nEWIxOp5cCtZ7tu+30quz5V3k0b1YNuc6lw== + dependencies: + abort-controller "^3.0.0" + eventsource "^2.0.2" + fetch-cookie "^2.0.3" + node-fetch "^2.6.7" + ws "^7.4.5" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2353,6 +2364,13 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -4350,6 +4368,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^4.0.0, eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -4360,6 +4383,11 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -4491,6 +4519,14 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fetch-cookie@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-2.2.0.tgz#01086b6b5b1c3e08f15ffd8647b02ca100377365" + integrity sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ== + dependencies: + set-cookie-parser "^2.4.8" + tough-cookie "^4.0.0" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -6519,6 +6555,13 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -8364,6 +8407,11 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" +set-cookie-parser@^2.4.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -9035,6 +9083,11 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" @@ -9368,6 +9421,11 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -9526,6 +9584,14 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -9775,6 +9841,11 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" +ws@^7.4.5: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + ws@^7.4.6: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"