Мобилка
This commit is contained in:
@@ -1574,14 +1574,15 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
animate={{ scale: 1, opacity: 1, y: 0 }}
|
animate={{ scale: 1, opacity: 1, y: 0 }}
|
||||||
exit={{ scale: 0.9, opacity: 0, y: 20 }}
|
exit={{ scale: 0.9, opacity: 0, y: 20 }}
|
||||||
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
|
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
|
||||||
className={`relative w-full mx-4 rounded-[2.5rem] glass-strong shadow-2xl shadow-black/50 overflow-hidden border border-white/5 ${showVideoArea ? 'max-w-5xl' : 'max-w-md'
|
className={`relative w-full h-full sm:h-auto sm:mx-4 sm:rounded-[2.5rem] glass-strong shadow-2xl shadow-black/50 overflow-hidden border border-white/5 flex flex-col ${
|
||||||
|
showVideoArea ? 'sm:max-w-5xl' : 'sm:max-w-md'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Mic selector popup */}
|
{/* Mic selector popup */}
|
||||||
{showMicMenu && microphones.length > 0 && (
|
{showMicMenu && microphones.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 z-[200]" onClick={() => setShowMicMenu(false)} />
|
<div className="fixed inset-0 z-[200]" onClick={() => setShowMicMenu(false)} />
|
||||||
<div className="fixed z-[201] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[320px] rounded-xl bg-zinc-800/95 backdrop-blur-md border border-zinc-600 shadow-2xl py-2">
|
<div className="fixed z-[201] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] sm:w-[320px] rounded-xl bg-zinc-800/95 backdrop-blur-md border border-zinc-600 shadow-2xl py-2">
|
||||||
<div className="px-3 py-1.5 text-xs text-zinc-400 uppercase tracking-wider border-b border-zinc-700 mb-1">{t('selectMicrophone')}</div>
|
<div className="px-3 py-1.5 text-xs text-zinc-400 uppercase tracking-wider border-b border-zinc-700 mb-1">{t('selectMicrophone')}</div>
|
||||||
{microphones.map((mic, i) => (
|
{microphones.map((mic, i) => (
|
||||||
<button
|
<button
|
||||||
@@ -1603,7 +1604,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
<>
|
<>
|
||||||
{/* Backdrop to close on click outside */}
|
{/* Backdrop to close on click outside */}
|
||||||
<div className="fixed inset-0 z-[200]" onClick={() => setShowCameraMenu(false)} />
|
<div className="fixed inset-0 z-[200]" onClick={() => setShowCameraMenu(false)} />
|
||||||
<div className="fixed z-[201] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[300px] rounded-xl bg-zinc-800/95 backdrop-blur-md border border-zinc-600 shadow-2xl py-2">
|
<div className="fixed z-[201] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] sm:w-[300px] rounded-xl bg-zinc-800/95 backdrop-blur-md border border-zinc-600 shadow-2xl py-2">
|
||||||
<div className="px-3 py-1.5 text-xs text-zinc-400 uppercase tracking-wider border-b border-zinc-700 mb-1">{t('switchCamera')}</div>
|
<div className="px-3 py-1.5 text-xs text-zinc-400 uppercase tracking-wider border-b border-zinc-700 mb-1">{t('switchCamera')}</div>
|
||||||
{cameras.map((cam, i) => (
|
{cameras.map((cam, i) => (
|
||||||
<button
|
<button
|
||||||
@@ -1624,7 +1625,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
{showVolumeSlider && (
|
{showVolumeSlider && (
|
||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 z-[200]" onClick={() => setShowVolumeSlider(false)} />
|
<div className="fixed inset-0 z-[200]" onClick={() => setShowVolumeSlider(false)} />
|
||||||
<div className="fixed z-[201] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[250px] rounded-xl bg-zinc-800/95 backdrop-blur-md border border-zinc-600 shadow-2xl p-4" onClick={(e) => e.stopPropagation()}>
|
<div className="fixed z-[201] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] sm:w-[250px] rounded-xl bg-zinc-800/95 backdrop-blur-md border border-zinc-600 shadow-2xl p-4" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="text-xs text-zinc-400 uppercase tracking-wider mb-3">{t('volume')}</div>
|
<div className="text-xs text-zinc-400 uppercase tracking-wider mb-3">{t('volume')}</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Volume2 size={16} className="text-zinc-400 shrink-0" />
|
<Volume2 size={16} className="text-zinc-400 shrink-0" />
|
||||||
@@ -1644,7 +1645,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* === Main Content Area === */}
|
{/* === Main Content Area === */}
|
||||||
<div className="flex-1 flex flex-col relative overflow-hidden bg-black min-h-[400px]">
|
<div className="flex-1 flex flex-col relative overflow-hidden bg-black min-h-[300px] sm:min-h-[400px]">
|
||||||
{showVideoArea ? (
|
{showVideoArea ? (
|
||||||
<div
|
<div
|
||||||
ref={videoContainerRef}
|
ref={videoContainerRef}
|
||||||
@@ -1664,9 +1665,9 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
<div className="relative mb-6">
|
<div className="relative mb-6">
|
||||||
<div className="absolute inset-0 rounded-full bg-vortex-500/20 animate-ping" />
|
<div className="absolute inset-0 rounded-full bg-vortex-500/20 animate-ping" />
|
||||||
{displayAvatar ? (
|
{displayAvatar ? (
|
||||||
<img src={displayAvatar} alt="" className="relative w-32 h-32 rounded-full object-cover border-4 border-vortex-500/30" />
|
<img src={displayAvatar} alt="" className="relative w-24 h-24 sm:w-32 sm: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">
|
<div className="relative w-24 h-24 sm:w-32 sm:h-32 rounded-full bg-gradient-to-br from-vortex-500 to-purple-600 flex items-center justify-center text-white text-3xl sm:text-4xl font-bold border-4 border-vortex-500/30">
|
||||||
{initials}
|
{initials}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1709,7 +1710,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
|
|
||||||
{/* Local video PIP (bottom-right) */}
|
{/* Local video PIP (bottom-right) */}
|
||||||
{hasLocalVideo && (
|
{hasLocalVideo && (
|
||||||
<div className="absolute bottom-3 right-3 w-48 rounded-xl overflow-hidden border-2 border-white/20 shadow-lg bg-black z-20"
|
<div className="absolute bottom-4 right-4 w-28 sm:w-48 rounded-xl overflow-hidden border-2 border-white/20 shadow-lg bg-black z-20"
|
||||||
style={{ aspectRatio: '16 / 9' }}
|
style={{ aspectRatio: '16 / 9' }}
|
||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
@@ -1772,9 +1773,9 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
)}
|
)}
|
||||||
<div className="relative z-10 p-1.5 rounded-full bg-gradient-to-br from-white/10 to-transparent backdrop-blur-md border border-white/10 shadow-2xl cursor-pointer">
|
<div className="relative z-10 p-1.5 rounded-full bg-gradient-to-br from-white/10 to-transparent backdrop-blur-md border border-white/10 shadow-2xl cursor-pointer">
|
||||||
{displayAvatar ? (
|
{displayAvatar ? (
|
||||||
<img src={displayAvatar} alt="" className="w-32 h-32 rounded-full object-cover shadow-inner" />
|
<img src={displayAvatar} alt="" className="w-24 h-24 sm:w-32 sm:h-32 rounded-full object-cover shadow-inner" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-32 h-32 rounded-full bg-gradient-to-br from-vortex-500 to-purple-600 flex items-center justify-center text-white font-bold text-4xl shadow-inner">
|
<div className="w-24 h-24 sm:w-32 sm:h-32 rounded-full bg-gradient-to-br from-vortex-500 to-purple-600 flex items-center justify-center text-white font-bold text-3xl sm:text-4xl shadow-inner">
|
||||||
{initials}
|
{initials}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1806,7 +1807,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* === Controls === */}
|
{/* === Controls === */}
|
||||||
<div className="px-8 pb-10 flex items-center justify-center gap-4 relative z-10 flex-wrap">
|
<div className="px-4 py-6 sm:px-8 sm:pb-10 flex items-center justify-center gap-3 sm:gap-4 relative z-10 flex-wrap">
|
||||||
{callState === 'incoming' && (
|
{callState === 'incoming' && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
@@ -1887,7 +1888,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={endCallSafe}
|
onClick={endCallSafe}
|
||||||
className="w-14 h-14 rounded-full bg-red-500 hover:bg-red-600 flex items-center justify-center text-white shadow-xl shadow-red-500/30 transition-all hover:scale-105 ml-2"
|
className="w-12 h-12 sm:w-14 sm:h-14 rounded-full bg-red-500 hover:bg-red-600 flex items-center justify-center text-white shadow-xl shadow-red-500/30 transition-all hover:scale-105"
|
||||||
>
|
>
|
||||||
<PhoneOff size={22} />
|
<PhoneOff size={22} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
Pin,
|
Pin,
|
||||||
Forward,
|
Forward,
|
||||||
Bookmark,
|
Bookmark,
|
||||||
|
ArrowLeft,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useChatStore } from '../stores/chatStore';
|
import { useChatStore } from '../stores/chatStore';
|
||||||
import { useAuthStore } from '../stores/authStore';
|
import { useAuthStore } from '../stores/authStore';
|
||||||
@@ -421,19 +422,17 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
|
|||||||
<div
|
<div
|
||||||
ref={chatViewRef}
|
ref={chatViewRef}
|
||||||
onMouseMove={handleMouseMove}
|
onMouseMove={handleMouseMove}
|
||||||
className={`flex-1 flex flex-col h-full rounded-3xl overflow-hidden shadow-[0_0_120px_-20px_rgba(0,0,0,0.5)] border border-border/50 relative z-0 chat-theme-${chatTheme} transition-colors duration-500`}
|
className={`flex-1 flex flex-col h-full sm:rounded-[2rem] overflow-hidden shadow-[0_0_120px_-20px_rgba(0,0,0,0.5)] border-x sm:border border-border/50 relative z-0 chat-theme-${chatTheme} transition-colors duration-500`}
|
||||||
>
|
>
|
||||||
{/* Шапка чата */}
|
|
||||||
{selectionMode ? (
|
{selectionMode ? (
|
||||||
<div className="h-[76px] flex items-center justify-between px-6 border-b border-border/40 bg-surface-secondary/80 backdrop-blur-xl z-20 flex-shrink-0 animate-in slide-in-from-top-2">
|
<div className="h-[76px] flex items-center justify-between px-4 sm:px-6 border-b border-border/40 bg-surface-secondary/80 backdrop-blur-xl z-20 flex-shrink-0 animate-in slide-in-from-top-2">
|
||||||
<div className="flex items-center gap-4 text-white">
|
<div className="flex items-center gap-2 sm:gap-4 text-white">
|
||||||
<button onClick={() => { setSelectionMode(false); setSelectedMessages(new Set()); }} className="p-2 -ml-2 rounded-full hover:bg-white/10 transition">
|
<button onClick={() => { setSelectionMode(false); setSelectedMessages(new Set()); }} className="p-2 -ml-2 rounded-full hover:bg-white/10 transition">
|
||||||
<X size={20} className="text-zinc-300" />
|
<X size={20} className="text-zinc-300" />
|
||||||
</button>
|
</button>
|
||||||
<span className="font-medium text-[15px]">{selectedMessages.size} {t('selected') || 'выбрано'}</span>
|
<span className="font-medium text-[15px]">{selectedMessages.size} {t('selected') || 'выбрано'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{/* Кнопка удаления с выпадающим меню */}
|
|
||||||
<div className="relative" ref={deleteMenuRef}>
|
<div className="relative" ref={deleteMenuRef}>
|
||||||
<button
|
<button
|
||||||
disabled={selectedMessages.size === 0}
|
disabled={selectedMessages.size === 0}
|
||||||
@@ -482,7 +481,14 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-[76px] flex items-center justify-between px-6 border-b border-border/40 bg-surface-secondary/80 backdrop-blur-xl z-20 flex-shrink-0">
|
<div className="h-[76px] flex items-center justify-between px-3 sm:px-6 border-b border-border/40 bg-surface-secondary/80 backdrop-blur-xl z-20 flex-shrink-0 gap-2">
|
||||||
|
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveChat(null)}
|
||||||
|
className="sm:hidden p-2 -ml-1 rounded-xl hover:bg-surface-hover text-zinc-400 transition-colors"
|
||||||
|
>
|
||||||
|
<ArrowLeft size={22} />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="flex items-center gap-3 min-w-0 flex-1 group transition-all"
|
className="flex items-center gap-3 min-w-0 flex-1 group transition-all"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -525,9 +531,9 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-1.5 ml-4">
|
<div className="flex items-center gap-1.5 ml-4">
|
||||||
{/* Поиск */}
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{showSearch && (
|
{showSearch && (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -590,7 +596,6 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Меню */}
|
|
||||||
<div className="relative" ref={topMenuRef}>
|
<div className="relative" ref={topMenuRef}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTopMenu(!showTopMenu)}
|
onClick={() => setShowTopMenu(!showTopMenu)}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default function Sidebar() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="w-[340px] h-full flex flex-col bg-surface-secondary rounded-3xl overflow-hidden border border-border/50 shadow-2xl relative z-10">
|
<div className="w-full h-full flex flex-col bg-surface-secondary sm:rounded-[2rem] overflow-hidden border-x sm:border border-border/50 shadow-2xl relative z-10">
|
||||||
{/* Шапка */}
|
{/* Шапка */}
|
||||||
<div className="h-[76px] px-4 flex items-center gap-3 border-b border-border/40 bg-surface-secondary flex-shrink-0">
|
<div className="h-[76px] px-4 flex items-center gap-3 border-b border-border/40 bg-surface-secondary flex-shrink-0">
|
||||||
<button
|
<button
|
||||||
@@ -121,10 +121,10 @@ export default function Sidebar() {
|
|||||||
onClick={() => setShowCreateStory(true)}
|
onClick={() => setShowCreateStory(true)}
|
||||||
className="flex flex-col items-center gap-1 flex-shrink-0 group"
|
className="flex flex-col items-center gap-1 flex-shrink-0 group"
|
||||||
>
|
>
|
||||||
<div className="w-14 h-14 rounded-full border-2 border-dashed border-zinc-600 flex items-center justify-center group-hover:border-vortex-400 transition-colors">
|
<div className="w-12 h-12 sm:w-14 sm:h-14 rounded-full border-2 border-dashed border-zinc-600 flex items-center justify-center group-hover:border-vortex-400 transition-colors">
|
||||||
<Plus size={20} className="text-zinc-400 group-hover:text-vortex-400 transition-colors" />
|
<Plus size={18} className="text-zinc-400 group-hover:text-vortex-400 transition-colors" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] text-zinc-500 truncate w-14 text-center">{t('newStory')}</span>
|
<span className="text-[10px] text-zinc-500 truncate w-12 sm:w-14 text-center">{t('newStory')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{storyGroups.map((group, idx) => {
|
{storyGroups.map((group, idx) => {
|
||||||
|
|||||||
@@ -257,15 +257,21 @@ export default function ChatPage() {
|
|||||||
setGroupCallOpen(false);
|
setGroupCallOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const activeChat = useChatStore((state) => state.activeChat);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
className="h-full flex bg-surface p-3 gap-3 overflow-hidden"
|
className="h-full flex bg-surface sm:p-3 sm:gap-3 overflow-hidden"
|
||||||
>
|
>
|
||||||
|
<div className={`${activeChat ? 'hidden sm:block' : 'block'} w-full sm:w-[340px] flex-shrink-0`}>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
<div className={`${activeChat ? 'block' : 'hidden sm:block'} flex-1 h-full min-w-0`}>
|
||||||
<ChatView onStartCall={handleStartCall} onStartGroupCall={handleStartGroupCall} />
|
<ChatView onStartCall={handleStartCall} onStartGroupCall={handleStartGroupCall} />
|
||||||
|
</div>
|
||||||
<CallModal
|
<CallModal
|
||||||
key={callSessionId}
|
key={callSessionId}
|
||||||
isOpen={callOpen}
|
isOpen={callOpen}
|
||||||
|
|||||||
Reference in New Issue
Block a user