From 4b81b22a1d56eed25bcfba8e2b477c10a67c779e Mon Sep 17 00:00:00 2001 From: inmake Date: Tue, 28 Oct 2025 16:58:38 +0500 Subject: [PATCH] Update environment configurations for local development, add socket.io and uuid dependencies, and refactor session management to support guest IDs for unauthorized users. Enhance ParticipantsPopup and UserCamera components to handle local media permissions and improve user session handling. Update optional authentication middleware to manage guest IDs and session validation. --- client/.env | 8 +- client/bun.lock | 27 ++ client/src/components/SessionUsersPanel.tsx | 1 + .../components/popups/ParticipantsPopup.tsx | 70 +++-- client/src/components/ui/UserCamera.tsx | 37 +-- client/src/hooks/useVoiceActivity.ts | 18 +- client/src/lib/api.ts | 5 + client/src/lib/guestId.ts | 42 +++ client/src/lib/webrtc.ts | 6 +- client/src/pages/SessionPage.tsx | 2 +- client/src/types/Session.ts | 1 + server/.env | 4 +- server/bun.lock | 39 ++- server/src/controllers/session.ts | 15 +- server/src/db/schema/serverSessions.ts | 4 +- server/src/middlewares/optionalAuth.ts | 285 ++++++++++-------- server/src/services/serverSession/index.ts | 14 +- 17 files changed, 375 insertions(+), 203 deletions(-) create mode 100644 client/src/lib/guestId.ts diff --git a/client/.env b/client/.env index 325974f..0f22f67 100644 --- a/client/.env +++ b/client/.env @@ -1,4 +1,4 @@ -# VITE_API_URL=http://192.168.1.23:3000 -# VITE_API_URL=http://192.168.1.224:3000 -VITE_API_URL=https://stream.graff.estate/api -VITE_WEBRTC_URL=https://stream.graff.estate \ No newline at end of file +VITE_API_URL=http://localhost:3000 +VITE_WEBRTC_URL=http://localhost:3001 +# VITE_API_URL=https://stream.graff.estate/api +# VITE_WEBRTC_URL=https://stream.graff.estate \ No newline at end of file diff --git a/client/bun.lock b/client/bun.lock index 9ad6317..cb64456 100644 --- a/client/bun.lock +++ b/client/bun.lock @@ -14,6 +14,8 @@ "react-dom": "^19.1.1", "react-qr-code": "^2.0.18", "react-router": "^7.9.3", + "socket.io-client": "^4.8.1", + "uuid": "^13.0.0", "zustand": "^5.0.8", }, "devDependencies": { @@ -21,6 +23,7 @@ "@types/node": "^24.6.0", "@types/react": "^19.1.16", "@types/react-dom": "^19.1.9", + "@types/uuid": "^11.0.0", "@vitejs/plugin-react-swc": "^4.1.0", "autoprefixer": "^10.4.21", "eslint": "^9.36.0", @@ -186,6 +189,8 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.3", "", { "os": "win32", "cpu": "x64" }, "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA=="], + "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], + "@swc/core": ["@swc/core@1.13.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.5", "@swc/core-darwin-x64": "1.13.5", "@swc/core-linux-arm-gnueabihf": "1.13.5", "@swc/core-linux-arm64-gnu": "1.13.5", "@swc/core-linux-arm64-musl": "1.13.5", "@swc/core-linux-x64-gnu": "1.13.5", "@swc/core-linux-x64-musl": "1.13.5", "@swc/core-win32-arm64-msvc": "1.13.5", "@swc/core-win32-ia32-msvc": "1.13.5", "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ=="], "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.13.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ=="], @@ -226,6 +231,8 @@ "@types/react-dom": ["@types/react-dom@19.2.0", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg=="], + "@types/uuid": ["@types/uuid@11.0.0", "", { "dependencies": { "uuid": "*" } }, "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.45.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/type-utils": "8.45.0", "@typescript-eslint/utils": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg=="], @@ -326,6 +333,10 @@ "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "engine.io-client": ["engine.io-client@6.6.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w=="], + + "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -568,6 +579,10 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "socket.io-client": ["socket.io-client@4.8.1", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ=="], + + "socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -616,6 +631,8 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + "vite": ["vite@7.1.8", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -628,6 +645,8 @@ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "zustand": ["zustand@5.0.8", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw=="], @@ -644,6 +663,10 @@ "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -652,6 +675,10 @@ "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "socket.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], diff --git a/client/src/components/SessionUsersPanel.tsx b/client/src/components/SessionUsersPanel.tsx index 4b91ca3..3790fb7 100644 --- a/client/src/components/SessionUsersPanel.tsx +++ b/client/src/components/SessionUsersPanel.tsx @@ -72,6 +72,7 @@ function SessionUsersPanel({ isControlDisabled={true} isAdmin={true} // Локальный пользователь - админ своей сессии mediaStream={participant.stream} + hasLocalMediaPermission={hasLocalStream} onMute={() => console.log(`Mute user ${participant.id}`)} onVideoOff={() => console.log(`Video off user ${participant.id}`)} onCanControl={() => diff --git a/client/src/components/popups/ParticipantsPopup.tsx b/client/src/components/popups/ParticipantsPopup.tsx index 2c687c6..7c0558c 100644 --- a/client/src/components/popups/ParticipantsPopup.tsx +++ b/client/src/components/popups/ParticipantsPopup.tsx @@ -12,11 +12,24 @@ import { Fragment, useRef } from "react"; import DraggableContainer from "../DraggableContainer"; import { useWebRTC } from "../../hooks/useWebRTC"; import type { Participant } from "../../lib/webrtc"; +import type { Session } from "../../types/Session"; +import { getGuestId } from "../../lib/guestId"; +import { useMe } from "../../hooks/useAuth"; -export default function ParticipantsPopup() { +interface ParticipantsPopupProps { + session: Session; +} + +export default function ParticipantsPopup({ session }: ParticipantsPopupProps) { const { participants, currentUserId, localStream } = useWebRTC(); + const { data: user } = useMe(); const headerRef = useRef(null); - + + // Определяем, является ли текущий пользователь организатором + const isOrganizer = + !!(session.userId && user?.id === session.userId) || + !!(session.guestId && getGuestId() === session.guestId); + // Добавляем локального пользователя в начало списка const allParticipants: (Participant & { isLocal?: boolean })[] = [ { @@ -39,15 +52,17 @@ export default function ParticipantsPopup() {
{allParticipants.length === 0 ? ( -
+
Нет участников
) : ( allParticipants.map((participant) => ( -
@@ -79,23 +94,38 @@ export default function ParticipantsPopup() { ); } -function ParticipantItem({ - participant, - isLocal -}: { - participant: Participant & { isLocal?: boolean }; +function ParticipantItem({ + participant, + isLocal, + isOrganizer, + session, +}: { + participant: Participant & { isLocal?: boolean }; isLocal: boolean; + isOrganizer: boolean; + session: Session; }) { const parentRef = useRef(null); - + // Проверяем наличие аудио/видео треков - const hasAudio = participant.stream?.getAudioTracks().some(track => track.enabled) ?? false; - const hasVideo = participant.stream?.getVideoTracks().some(track => track.enabled) ?? false; + const hasAudio = + participant.stream?.getAudioTracks().some((track) => track.enabled) ?? + false; + const hasVideo = + participant.stream?.getVideoTracks().some((track) => track.enabled) ?? + false; const isMuted = !hasAudio; const isVideoOff = !hasVideo; - // Определяем статус участника - const status: "admin" | "caution" | undefined = participant.stream ? "admin" : "caution"; + // Определяем, является ли этот конкретный участник организатором сессии + const isThisParticipantOrganizer = + (session.userId && participant.id === session.userId) || + (session.guestId && participant.id === session.guestId); + + // Определяем статус участника для аватара + const status: "admin" | "caution" | undefined = isThisParticipantOrganizer + ? "admin" + : undefined; return (
@@ -103,10 +133,12 @@ function ParticipantItem({
- {isLocal ? "Вы" : participant.name || `Участник ${participant.id.slice(0, 8)}`} + {isLocal + ? "Вы" + : participant.name || `Участник ${participant.id.slice(0, 8)}`} - {isLocal ? "Организатор" : "Участник"} + {isThisParticipantOrganizer ? "Организатор" : "Участник"}
@@ -123,8 +155,8 @@ function ParticipantItem({
)} - {/* Действия только для удаленных участников и только для администратора */} - {!isLocal && ( + {/* Действия только для удаленных участников и только для организатора */} + {!isLocal && isOrganizer && ( void; // Для локального - отправляем изменения + hasLocalMediaPermission?: boolean; // Есть ли у локального пользователя разрешение на медиа } export default function UserCamera({ @@ -50,9 +51,11 @@ export default function UserCamera({ isLocal = false, isSpeaking: remoteSpeaking, onSpeakingChange, + hasLocalMediaPermission = false, }: UserCameraProps) { const ref = useRef(null); - const [isAudioMuted, setIsAudioMuted] = useState(true); // Для удаленных участников - начинаем с muted + // Для удаленных участников: если у локального пользователя есть разрешение на медиа - unmute, иначе mute (для autoplay) + const [isAudioMuted, setIsAudioMuted] = useState(!hasLocalMediaPermission); // Детекция голосовой активности (только для локального пользователя) const { isSpeaking: isVoiceActive } = useVoiceActivity( @@ -62,7 +65,7 @@ export default function UserCamera({ // Для локального - используем локальную детекцию // Для удаленных - используем полученное состояние через Socket.IO const localSpeaking = !isMuted && isVoiceActive; - const isSpeaking = isLocal ? localSpeaking : (remoteSpeaking || false); + const isSpeaking = isLocal ? localSpeaking : remoteSpeaking || false; // Отправляем изменения состояния для локального пользователя useEffect(() => { @@ -78,7 +81,11 @@ export default function UserCamera({ // Логируем для отладки useEffect(() => { console.log( - `[${name}${isLocal ? " (local)" : ""}] isSpeaking: ${isSpeaking}, ringOpacity: ${ringOpacity.toFixed(2)}, isMuted: ${isMuted}` + `[${name}${ + isLocal ? " (local)" : "" + }] isSpeaking: ${isSpeaking}, ringOpacity: ${ringOpacity.toFixed( + 2 + )}, isMuted: ${isMuted}` ); }, [isSpeaking, ringOpacity, name, isMuted, isLocal]); @@ -90,12 +97,6 @@ export default function UserCamera({ ); ref.current.srcObject = mediaStream; - // Убеждаемся что видео muted для autoplay - if (!isLocal) { - ref.current.muted = true; - console.log(`[UserCamera] Set muted=true for remote video ${name}`); - } - // Принудительно запускаем воспроизведение ref.current.play().catch((error) => { console.error(`[UserCamera] Failed to play video for ${name}:`, error); @@ -237,14 +238,13 @@ export default function UserCamera({ }, [name]); const toggleRemoteAudio = () => { - if (!isLocal && ref.current) { + if (!isLocal) { const newMutedState = !isAudioMuted; - ref.current.muted = newMutedState; setIsAudioMuted(newMutedState); console.log( `[UserCamera] ${name} audio ${ newMutedState ? "muted" : "unmuted" - }, video element muted: ${ref.current.muted}` + }` ); } }; @@ -305,15 +305,6 @@ export default function UserCamera({
)} - {/* Подсказка для запуска видео */} - {!isLocal && mediaStream && !isVideoOff && ( -
-
- Кликните для запуска видео -
-
- )} -