Попытка
This commit is contained in:
@@ -1,18 +0,0 @@
|
|||||||
# Server
|
|
||||||
PORT=3001
|
|
||||||
JWT_SECRET=your-secure-random-secret-key-here
|
|
||||||
CORS_ORIGINS=http://localhost:5173,http://localhost:3000
|
|
||||||
|
|
||||||
# Database (PostgreSQL)
|
|
||||||
DATABASE_URL=postgresql://user:password@localhost:5432/vortex
|
|
||||||
|
|
||||||
# Message encryption (AES-256-GCM) — 64-char hex key (32 bytes)
|
|
||||||
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
||||||
# WARNING: if you lose this key, all encrypted messages become unreadable!
|
|
||||||
ENCRYPTION_KEY=
|
|
||||||
|
|
||||||
# TURN server for voice/video calls (optional, recommended for production)
|
|
||||||
# Install coturn: sudo apt install coturn
|
|
||||||
# TURN_URL=turn:your-domain.com:3478
|
|
||||||
# TURN_SECRET=your-coturn-shared-secret
|
|
||||||
# STUN_URLS=stun:stun.l.google.com:19302,stun:stun1.l.google.com:19302
|
|
||||||
@@ -1576,37 +1576,38 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* === Main Content Area === */}
|
|
||||||
<div className="flex-1 flex flex-col relative overflow-hidden">
|
|
||||||
{/*
|
|
||||||
CRITICAL: Single stable remote video element.
|
|
||||||
We keep it always mounted so WebRTC srcObject is never lost
|
|
||||||
during transitions between voice and video modes.
|
|
||||||
*/}
|
|
||||||
<video
|
|
||||||
ref={remoteVideoRef}
|
|
||||||
autoPlay
|
|
||||||
playsInline
|
|
||||||
className={`
|
|
||||||
bg-black transition-all duration-500
|
|
||||||
${showVideoArea
|
|
||||||
? 'w-full h-full object-contain'
|
|
||||||
: 'w-0 h-0 opacity-0 absolute pointer-events-none'
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
onContextMenu={(e) => { e.preventDefault(); setShowVolumeSlider(true); }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{showVideoArea ? (
|
{/* === Main Content Area === */}
|
||||||
|
<div className="flex-1 flex flex-col relative overflow-hidden bg-black min-h-[400px]">
|
||||||
|
{showVideoArea && (
|
||||||
<div
|
<div
|
||||||
ref={videoContainerRef}
|
ref={videoContainerRef}
|
||||||
className="absolute inset-0 flex items-center justify-center bg-black"
|
className="absolute inset-0 flex items-center justify-center z-0"
|
||||||
>
|
>
|
||||||
{/* Placeholder shown when video track is not yet arrived or muted */}
|
<video
|
||||||
|
ref={remoteVideoRef}
|
||||||
|
autoPlay
|
||||||
|
playsInline
|
||||||
|
className="w-full h-full object-contain"
|
||||||
|
onContextMenu={(e) => { e.preventDefault(); setShowVolumeSlider(true); }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Placeholder shown ONLY when track is not yet ready */}
|
||||||
{!hasRemoteVideo && (
|
{!hasRemoteVideo && (
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center bg-zinc-900/50 backdrop-blur-sm z-10">
|
<div className="absolute inset-0 flex flex-col items-center justify-center bg-zinc-900 z-10">
|
||||||
|
<div className="relative mb-6">
|
||||||
|
<div className="absolute inset-0 rounded-full bg-vortex-500/20 animate-ping" />
|
||||||
|
{displayAvatar ? (
|
||||||
|
<img src={displayAvatar} alt="" className="relative w-32 h-32 rounded-full object-cover border-4 border-vortex-500/30" />
|
||||||
|
) : (
|
||||||
|
<div className="relative w-32 h-32 rounded-full bg-gradient-to-br from-vortex-500 to-purple-600 flex items-center justify-center text-white text-4xl font-bold border-4 border-vortex-500/30">
|
||||||
|
{initials}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<VideoOff size={48} className="text-zinc-500 mb-2" />
|
<VideoOff size={48} className="text-zinc-500 mb-2" />
|
||||||
<span className="text-sm text-zinc-500">{displayName}</span>
|
<span className="text-xl text-white font-medium mb-1">{displayName}</span>
|
||||||
|
<span className="text-sm text-zinc-500 animate-pulse">Соединение...</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user