Merge pull request #21 from lorsanstand/dev

Fix 2
This commit is contained in:
Станислав
2026-02-24 21:44:44 +03:00
committed by GitHub
3 changed files with 268 additions and 127 deletions
+83 -10
View File
@@ -38,25 +38,98 @@ function App() {
useEffect(() => { useEffect(() => {
// Prevent default iOS behaviors // Prevent default iOS behaviors
document.addEventListener('touchmove', (e) => { const preventBounce = (e: TouchEvent) => {
if ((e.target as any).closest('.overflow-y-auto') === null && if ((e.target as any).closest('.overflow-y-auto') === null &&
(e.target as any).nodeName !== 'INPUT' && (e.target as any).nodeName !== 'INPUT' &&
(e.target as any).nodeName !== 'TEXTAREA') { (e.target as any).nodeName !== 'TEXTAREA') {
e.preventDefault(); e.preventDefault();
} }
}, { passive: false });
// Handle viewport resize on mobile (keyboard appears)
const handleResize = () => {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}; };
handleResize(); document.addEventListener('touchmove', preventBounce, { passive: false });
window.addEventListener('resize', handleResize);
// Track focus state for input fields
let isInputFocused = false;
const handleInputFocus = () => {
isInputFocused = true;
document.body.style.position = 'fixed';
document.body.style.width = '100%';
};
const handleInputBlur = () => {
isInputFocused = false;
document.body.style.position = '';
document.body.style.width = '';
};
// Attach focus/blur listeners to all inputs
const inputs = document.querySelectorAll('input, textarea');
inputs.forEach(input => {
input.addEventListener('focus', handleInputFocus);
input.addEventListener('blur', handleInputBlur);
});
// Handle viewport resize and keyboard appearance using visualViewport API
const handleViewportChange = () => {
const viewport = window.visualViewport;
if (!viewport) {
// Fallback for older browsers
const windowHeight = window.innerHeight;
const vh = windowHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
return;
}
// Calculate keyboard height
const windowHeight = window.innerHeight;
const viewportHeight = viewport.height;
const keyboardHeight = Math.max(0, windowHeight - viewportHeight);
// Set CSS variables for keyboard height
document.documentElement.style.setProperty('--keyboard-height', `${keyboardHeight}px`);
// Set standard vh variable for better compatibility
const vh = viewport.height * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
// Prevent body scrolling issues
if (keyboardHeight > 0) {
document.body.style.overflow = 'hidden';
} else if (!isInputFocused) {
document.body.style.overflow = 'auto';
}
};
// Listen to visualViewport changes (modern browsers)
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', handleViewportChange);
window.visualViewport.addEventListener('scroll', handleViewportChange);
handleViewportChange();
}
// Fallback for older browsers - listen to window resize
window.addEventListener('resize', handleViewportChange);
window.addEventListener('orientationchange', () => {
setTimeout(handleViewportChange, 100);
});
// Initial setup
handleViewportChange();
return () => { return () => {
window.removeEventListener('resize', handleResize); document.removeEventListener('touchmove', preventBounce);
window.removeEventListener('resize', handleViewportChange);
window.removeEventListener('orientationchange', handleViewportChange);
if (window.visualViewport) {
window.visualViewport.removeEventListener('resize', handleViewportChange);
window.visualViewport.removeEventListener('scroll', handleViewportChange);
}
inputs.forEach(input => {
input.removeEventListener('focus', handleInputFocus);
input.removeEventListener('blur', handleInputBlur);
});
}; };
}, []); }, []);
+73 -3
View File
@@ -46,6 +46,8 @@ html {
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
--vh: 1vh;
--keyboard-height: 0px;
} }
html, html,
@@ -144,6 +146,19 @@ input, textarea, select {
outline: 2px solid var(--accent-primary); outline: 2px solid var(--accent-primary);
outline-offset: 2px; outline-offset: 2px;
} }
/* Fix for iOS input zoom and keyboard behavior */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
textarea {
font-size: 16px !important;
padding: 8px 12px;
-webkit-appearance: none;
appearance: none;
border-radius: 8px;
}
} }
/* Prevent text zoom when rotating */ /* Prevent text zoom when rotating */
@@ -163,7 +178,7 @@ body.modal-open {
position: fixed; position: fixed;
} }
/* Fix for iOS keyboard appearance */ /* Fix for iOS keyboard appearance - prevents scrolling under keyboard */
@supports (padding: max(0px)) { @supports (padding: max(0px)) {
body { body {
padding-bottom: max(env(safe-area-inset-bottom), 0px); padding-bottom: max(env(safe-area-inset-bottom), 0px);
@@ -191,7 +206,7 @@ button {
border: none; border: none;
} }
/* Input styling for mobile */ /* Input styling for mobile - prevent zoom and unwanted behavior */
input[type="text"], input[type="text"],
input[type="email"], input[type="email"],
input[type="password"], input[type="password"],
@@ -199,6 +214,7 @@ textarea {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
border-radius: 8px; border-radius: 8px;
font-size: 16px;
} }
/* Disable auto-zoom on iOS input focus */ /* Disable auto-zoom on iOS input focus */
@@ -210,7 +226,7 @@ select {
font-size: 16px !important; font-size: 16px !important;
} }
/* Smooth scrolling container */ /* Smooth scrolling container - support iOS momentum scrolling */
.overflow-y-auto { .overflow-y-auto {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
@@ -253,3 +269,57 @@ select {
} }
} }
/* iOS specific fixes for input fields */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
textarea {
/* Prevent iOS default styling */
-webkit-border-radius: 8px;
border-radius: 8px;
/* Prevent zooming on focus */
font-size: 16px !important;
/* Fix for iOS bug where input gets zoomed */
max-width: 100%;
/* Better input styling on iOS */
-webkit-padding-start: 12px;
padding-inline-start: 12px;
}
/* Ensure chat container doesn't overflow */
[role="region"] {
overflow: hidden;
max-width: 100vw;
}
/* Fix for messages container not respecting keyboard */
.message-container {
padding-bottom: var(--keyboard-height);
transition: padding-bottom 0.15s ease-out;
}
/* Sticky header support */
.sticky {
position: sticky;
z-index: 40;
}
/* Safe area support for notch and status bar */
@supports (padding: max(0px)) {
.sticky-top {
padding-top: max(1rem, env(safe-area-inset-top));
}
}
/* Smooth backdrop filter transitions */
@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
.sticky-header {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
}
+114 -116
View File
@@ -521,31 +521,31 @@ export default function ChatPage() {
return ( return (
<div className="w-screen h-screen flex flex-col md:flex-row overflow-hidden" style={{ backgroundColor: 'var(--bg-primary)' }}> <div className="min-h-screen h-screen flex" style={{ backgroundColor: 'var(--bg-primary)' }}>
{/* Sidebar */} {/* Sidebar */}
<div className={`w-screen md:w-80 flex flex-col h-screen shadow-soft ${ <div className={`w-full md:w-80 flex flex-col shadow-soft ${
selectedChat ? 'hidden md:flex' : 'flex' selectedChat ? 'hidden md:flex' : 'flex'
}`} style={{ backgroundColor: 'var(--bg-card)' }}> }`} style={{ backgroundColor: 'var(--bg-card)' }}>
{/* Header */} {/* Header */}
<div className="p-2.5 md:p-6 border-b flex-shrink-0" style={{ borderColor: 'var(--border-color)' }}> <div className="p-4 md:p-6 border-b" style={{ borderColor: 'var(--border-color)' }}>
<h1 className="text-xl md:text-3xl font-lora font-semibold text-center tracking-wider" style={{ color: 'var(--accent-primary)' }}> <h1 className="text-2xl md:text-3xl font-lora font-semibold text-center tracking-wider" style={{ color: 'var(--accent-primary)' }}>
AETHER AETHER
</h1> </h1>
</div> </div>
{/* Verification Banner */} {/* Verification Banner */}
{user && !user.is_verified && ( {user && !user.is_verified && (
<div className="px-3 py-2 md:p-4 flex-shrink-0"> <div className="p-4">
<VerificationBanner userEmail={user.email} /> <VerificationBanner userEmail={user.email} />
</div> </div>
)} )}
{/* User Profile Section */} {/* User Profile Section */}
<div className="px-3 py-3 md:p-6 border-b flex-shrink-0" style={{ borderColor: 'var(--border-color)' }}> <div className="p-4 md:p-6 border-b" style={{ borderColor: 'var(--border-color)' }}>
<div className="flex items-center gap-2 md:gap-4"> <div className="flex items-center gap-3 md:gap-4">
{/* Avatar */} {/* Avatar */}
<div <div
className="w-10 h-10 md:w-14 md:h-14 flex items-center justify-center flex-shrink-0 cursor-pointer hover:opacity-90 transition" className="w-12 h-12 md:w-14 md:h-14 flex items-center justify-center flex-shrink-0 cursor-pointer hover:opacity-90 transition"
style={{ style={{
backgroundImage: user?.avatar_url ? `url(${user.avatar_url})` : undefined, backgroundImage: user?.avatar_url ? `url(${user.avatar_url})` : undefined,
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -562,10 +562,10 @@ export default function ChatPage() {
{/* User Info */} {/* User Info */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h3 className="font-inter font-semibold text-sm md:text-base truncate" style={{ color: 'var(--text-primary)' }}> <h3 className="font-inter font-semibold truncate" style={{ color: 'var(--text-primary)' }}>
{user?.display_name || user?.username} {user?.display_name || user?.username}
</h3> </h3>
<p className="text-xs md:text-sm font-inter truncate" style={{ color: 'var(--text-secondary)' }}> <p className="text-sm font-inter truncate" style={{ color: 'var(--text-secondary)' }}>
@{user?.username} @{user?.username}
</p> </p>
</div> </div>
@@ -573,61 +573,61 @@ export default function ChatPage() {
{/* Settings Icon */} {/* Settings Icon */}
<button <button
onClick={() => navigate('/settings')} onClick={() => navigate('/settings')}
className="p-1.5 md:p-2 hover:bg-gray-100 rounded-full transition min-h-touch min-w-touch" className="p-2 hover:bg-gray-100 rounded-full transition"
title="Настройки" title="Настройки"
> >
<Settings size={18} style={{ color: 'var(--text-secondary)' }} /> <Settings size={20} style={{ color: 'var(--text-secondary)' }} />
</button> </button>
</div> </div>
{/* Action Button */} {/* Action Button */}
<div className="mt-2 md:mt-4"> <div className="mt-4">
<motion.button <motion.button
onClick={() => navigate('/profile')} onClick={() => navigate('/profile')}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
className="w-full flex items-center justify-center gap-2 py-2 px-3 rounded-xl md:rounded-2xl font-inter text-xs md:text-sm font-medium transition hover:opacity-80 min-h-touch" className="w-full flex items-center justify-center gap-2 py-2 px-3 rounded-xl font-inter text-sm font-medium transition hover:opacity-80"
style={{ backgroundColor: 'var(--bg-input)', color: 'var(--accent-primary)' }} style={{ backgroundColor: 'var(--bg-input)', color: 'var(--accent-primary)' }}
> >
<User size={14} className="md:w-4 md:h-4" /> <User size={16} />
Профиль Профиль
</motion.button> </motion.button>
</div> </div>
</div> </div>
{/* New Chat Button */} {/* New Chat Button */}
<div className="px-3 py-2 md:p-4 flex-shrink-0"> <div className="p-4">
<motion.button <motion.button
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={() => setShowNewChatModal(true)} onClick={() => setShowNewChatModal(true)}
className="w-full flex items-center justify-center gap-2 md:gap-3 py-2.5 md:py-3 px-3 md:px-4 rounded-xl md:rounded-2xl font-inter font-semibold text-sm md:text-base shadow-sm hover:shadow-md transition min-h-touch" className="w-full flex items-center justify-center gap-3 py-3 px-4 rounded-2xl font-inter font-semibold shadow-sm hover:shadow-md transition"
style={{ backgroundColor: 'var(--accent-primary)', color: 'white' }} style={{ backgroundColor: 'var(--accent-primary)', color: 'white' }}
> >
<MessageSquarePlus size={16} className="md:w-5 md:h-5" /> <MessageSquarePlus size={20} />
Новый чат Новый чат
</motion.button> </motion.button>
</div> </div>
{/* Chats List */} {/* Chats List */}
<div className="flex-1 overflow-y-auto px-2 md:px-4"> <div className="flex-1 overflow-y-auto px-4">
<div className="space-y-1 md:space-y-2"> <div className="space-y-2">
{loading ? ( {loading ? (
<div className="text-center py-12 px-4"> <div className="text-center py-12 px-4">
<div className="animate-spin w-8 h-8 mx-auto mb-4 border-2 border-t-transparent rounded-full" <div className="animate-spin w-8 h-8 mx-auto mb-4 border-2 border-t-transparent rounded-full"
style={{ borderColor: 'var(--accent-primary)', borderTopColor: 'transparent' }}></div> style={{ borderColor: 'var(--accent-primary)', borderTopColor: 'transparent' }}></div>
<p className="font-inter text-xs md:text-sm" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-sm" style={{ color: 'var(--text-secondary)' }}>
Загрузка чатов... Загрузка чатов...
</p> </p>
</div> </div>
) : error ? ( ) : error ? (
<div className="text-center py-12 px-4"> <div className="text-center py-12 px-4">
<p className="font-inter text-xs md:text-sm text-red-500">{error}</p> <p className="font-inter text-sm text-red-500">{error}</p>
</div> </div>
) : chats.length === 0 ? ( ) : chats.length === 0 ? (
<div className="text-center py-12 px-4"> <div className="text-center py-12 px-4">
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-gradient-to-br from-accent-terracotta/20 to-accent-olive/20 flex items-center justify-center"> <div className="w-16 h-16 mx-auto mb-4 rounded-full bg-gradient-to-br from-accent-terracotta/20 to-accent-olive/20 flex items-center justify-center">
<MessageSquarePlus size={32} style={{ color: 'var(--accent-primary)', opacity: 0.5 }} /> <MessageSquarePlus size={32} style={{ color: 'var(--accent-primary)', opacity: 0.5 }} />
</div> </div>
<p className="font-inter text-xs md:text-sm" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-sm" style={{ color: 'var(--text-secondary)' }}>
Пока нет чатов Пока нет чатов
</p> </p>
<p className="font-inter text-xs mt-1" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
@@ -641,17 +641,17 @@ export default function ChatPage() {
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={() => handleChatClick(chat)} onClick={() => handleChatClick(chat)}
className="p-2.5 md:p-4 rounded-lg md:rounded-2xl cursor-pointer transition-all relative overflow-hidden min-h-touch" className="p-4 rounded-2xl cursor-pointer transition-all relative overflow-hidden"
style={{ style={{
backgroundColor: selectedChat?.chat_id === chat.chat_id ? 'var(--accent-primary)' : 'var(--bg-input)', backgroundColor: selectedChat?.chat_id === chat.chat_id ? 'var(--accent-primary)' : 'var(--bg-input)',
boxShadow: selectedChat?.chat_id === chat.chat_id ? '0 4px 12px rgba(0,0,0,0.1)' : 'none', boxShadow: selectedChat?.chat_id === chat.chat_id ? '0 4px 12px rgba(0,0,0,0.1)' : 'none',
}} }}
> >
<div className="flex items-start gap-2.5 md:gap-4"> <div className="flex items-start gap-4">
{/* Chat Avatar with Online Indicator */} {/* Chat Avatar with Online Indicator */}
<div className="relative flex-shrink-0"> <div className="relative flex-shrink-0">
<div <div
className="w-10 h-10 md:w-12 md:h-12 flex items-center justify-center" className="w-12 h-12 flex items-center justify-center"
style={{ style={{
backgroundImage: chat.avatar_url ? `url(${chat.avatar_url})` : undefined, backgroundImage: chat.avatar_url ? `url(${chat.avatar_url})` : undefined,
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -665,7 +665,7 @@ export default function ChatPage() {
</div> </div>
{/* Online indicator */} {/* Online indicator */}
<div <div
className="absolute bottom-0 right-0 w-3 h-3 md:w-4 md:h-4 rounded-full border-2" className="absolute bottom-0 right-0 w-4 h-4 rounded-full border-2"
style={{ style={{
backgroundColor: '#10b981', backgroundColor: '#10b981',
borderColor: 'var(--bg-card)' borderColor: 'var(--bg-card)'
@@ -677,7 +677,7 @@ export default function ChatPage() {
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2 mb-1"> <div className="flex items-start justify-between gap-2 mb-1">
<h4 <h4
className="font-inter font-semibold text-sm md:text-base truncate" className="font-inter font-semibold text-base truncate"
style={{ color: selectedChat?.chat_id === chat.chat_id ? 'white' : 'var(--text-primary)' }} style={{ color: selectedChat?.chat_id === chat.chat_id ? 'white' : 'var(--text-primary)' }}
> >
{chat.display_name} {chat.display_name}
@@ -696,14 +696,14 @@ export default function ChatPage() {
{chat.last_message ? ( {chat.last_message ? (
<p <p
className="font-inter text-xs md:text-sm truncate" className="font-inter text-sm truncate"
style={{ color: selectedChat?.chat_id === chat.chat_id ? 'rgba(255,255,255,0.85)' : 'var(--text-secondary)' }} style={{ color: selectedChat?.chat_id === chat.chat_id ? 'rgba(255,255,255,0.85)' : 'var(--text-secondary)' }}
> >
{chat.last_message} {chat.last_message}
</p> </p>
) : ( ) : (
<p <p
className="font-inter text-xs md:text-sm italic truncate" className="font-inter text-sm italic truncate"
style={{ color: selectedChat?.chat_id === chat.chat_id ? 'rgba(255,255,255,0.6)' : 'var(--text-secondary)' }} style={{ color: selectedChat?.chat_id === chat.chat_id ? 'rgba(255,255,255,0.6)' : 'var(--text-secondary)' }}
> >
Нет сообщений Нет сообщений
@@ -718,7 +718,7 @@ export default function ChatPage() {
</div> </div>
{/* Footer Info */} {/* Footer Info */}
<div className="p-2 md:p-4 border-t text-center flex-shrink-0" style={{ borderColor: 'var(--border-color)' }}> <div className="p-4 border-t text-center" style={{ borderColor: 'var(--border-color)' }}>
<p className="text-xs font-inter" style={{ color: 'var(--text-secondary)' }}> <p className="text-xs font-inter" style={{ color: 'var(--text-secondary)' }}>
Aether Chat v1.0 Aether Chat v1.0
</p> </p>
@@ -726,13 +726,13 @@ export default function ChatPage() {
</div> </div>
{/* Main Chat Area */} {/* Main Chat Area */}
<div className={`flex-1 flex flex-col h-screen relative ${ <div className={`flex-1 flex flex-col relative ${
selectedChat ? 'flex' : 'hidden md:flex' selectedChat ? 'flex' : 'hidden md:flex'
}`}> }`}>
{selectedChat ? ( {selectedChat ? (
<> <>
{/* Chat Header */} {/* Chat Header */}
<div className="px-2 md:px-4 py-2 md:py-4 border-b flex items-center gap-2 md:gap-4 flex-shrink-0" style={{ <div className="p-3 md:p-4 border-b flex items-center gap-2 md:gap-4" style={{
backgroundColor: 'var(--bg-card)', backgroundColor: 'var(--bg-card)',
borderColor: 'var(--border-color)' borderColor: 'var(--border-color)'
}}> }}>
@@ -740,21 +740,21 @@ export default function ChatPage() {
whileHover={{ scale: 1.1 }} whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }} whileTap={{ scale: 0.9 }}
onClick={handleBackToChats} onClick={handleBackToChats}
className="p-2 rounded-full transition min-h-touch min-w-touch" className="p-2 rounded-full transition"
style={{ style={{
backgroundColor: 'var(--bg-input)', backgroundColor: 'var(--bg-input)',
color: 'var(--text-primary)' color: 'var(--text-primary)'
}} }}
title="Назад к чатам" title="Назад к чатам"
> >
<ArrowLeft size={18} className="md:w-5 md:h-5" /> <ArrowLeft size={20} />
</motion.button> </motion.button>
<motion.div <motion.div
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
onClick={() => handleViewUserProfile(selectedChat.user_id)} onClick={() => handleViewUserProfile(selectedChat.user_id)}
className="w-10 h-10 md:w-12 md:h-12 flex-shrink-0 flex items-center justify-center cursor-pointer" className="w-12 h-12 flex-shrink-0 flex items-center justify-center cursor-pointer"
style={{ style={{
backgroundImage: selectedChat.avatar_url ? `url(${selectedChat.avatar_url})` : undefined, backgroundImage: selectedChat.avatar_url ? `url(${selectedChat.avatar_url})` : undefined,
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -771,7 +771,7 @@ export default function ChatPage() {
className="flex-1 min-w-0 cursor-pointer" className="flex-1 min-w-0 cursor-pointer"
onClick={() => handleViewUserProfile(selectedChat.user_id)} onClick={() => handleViewUserProfile(selectedChat.user_id)}
> >
<h3 className="font-inter font-semibold text-sm md:text-base truncate hover:underline" style={{ color: 'var(--text-primary)' }}> <h3 className="font-inter font-semibold truncate hover:underline" style={{ color: 'var(--text-primary)' }}>
{selectedChat.display_name} {selectedChat.display_name}
</h3> </h3>
<p className="text-xs font-inter" style={{ color: 'var(--text-secondary)' }}> <p className="text-xs font-inter" style={{ color: 'var(--text-secondary)' }}>
@@ -784,7 +784,7 @@ export default function ChatPage() {
<div <div
ref={messagesContainerRef} ref={messagesContainerRef}
onScroll={handleScroll} onScroll={handleScroll}
className="flex-1 overflow-y-auto px-3 md:px-6 py-4 md:py-6 relative" className="flex-1 overflow-y-auto p-6 relative"
style={{ style={{
backgroundColor: 'var(--bg-primary)', backgroundColor: 'var(--bg-primary)',
backgroundImage: ` backgroundImage: `
@@ -848,8 +848,8 @@ export default function ChatPage() {
<div key={message.id}> <div key={message.id}>
{/* Date divider */} {/* Date divider */}
{showDateDivider && ( {showDateDivider && (
<div className="flex items-center justify-center my-4 md:my-6"> <div className="flex items-center justify-center my-6">
<div className="px-3 md:px-4 py-1.5 md:py-2 rounded-full font-inter text-xs font-medium shadow-sm" <div className="px-4 py-2 rounded-full font-inter text-xs font-medium shadow-sm"
style={{ style={{
backgroundColor: 'var(--bg-card)', backgroundColor: 'var(--bg-card)',
color: 'var(--text-secondary)', color: 'var(--text-secondary)',
@@ -864,7 +864,7 @@ export default function ChatPage() {
initial={{ opacity: 0, y: 20, scale: 0.95 }} initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }} animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ duration: 0.4, ease: "easeOut" }} transition={{ duration: 0.4, ease: "easeOut" }}
className={`flex gap-2 md:gap-3 mb-2 md:mb-3 ${isMyMessage ? 'justify-end' : 'justify-start'} ${!showAvatar && !isMyMessage ? 'ml-8 md:ml-11' : ''}`} className={`flex gap-3 mb-3 ${isMyMessage ? 'justify-end' : 'justify-start'} ${!showAvatar && !isMyMessage ? 'ml-11' : ''}`}
> >
{/* Avatar for incoming messages */} {/* Avatar for incoming messages */}
{!isMyMessage && showAvatar && ( {!isMyMessage && showAvatar && (
@@ -872,7 +872,7 @@ export default function ChatPage() {
initial={{ scale: 0 }} initial={{ scale: 0 }}
animate={{ scale: 1 }} animate={{ scale: 1 }}
transition={{ delay: 0.1 }} transition={{ delay: 0.1 }}
className="w-7 h-7 md:w-9 md:h-9 flex-shrink-0 flex items-center justify-center self-end shadow-md" className="w-9 h-9 flex-shrink-0 flex items-center justify-center self-end shadow-md"
style={{ style={{
backgroundImage: selectedChat.avatar_url ? `url(${selectedChat.avatar_url})` : undefined, backgroundImage: selectedChat.avatar_url ? `url(${selectedChat.avatar_url})` : undefined,
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -888,20 +888,20 @@ export default function ChatPage() {
)} )}
{/* Message bubble */} {/* Message bubble */}
<div className="flex flex-col max-w-xs md:max-w-lg"> <div className="flex flex-col max-w-lg">
{/* Sender name for incoming messages */} {/* Sender name for incoming messages */}
{!isMyMessage && showAvatar && ( {!isMyMessage && showAvatar && (
<span className="text-xs font-inter font-medium mb-0.5 md:mb-1 px-2" style={{ color: 'var(--accent-primary)' }}> <span className="text-xs font-inter font-medium mb-1 px-2" style={{ color: 'var(--accent-primary)' }}>
{selectedChat.display_name} {selectedChat.display_name}
</span> </span>
)} )}
<motion.div <motion.div
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
className={`px-3 md:px-5 py-2 md:py-3 font-inter text-sm md:text-[15px] leading-relaxed relative group ${ className={`px-5 py-3 font-inter text-[15px] leading-relaxed relative group ${
isMyMessage isMyMessage
? 'rounded-2xl md:rounded-[20px] rounded-br-sm' ? 'rounded-[20px] rounded-br-md'
: 'rounded-2xl md:rounded-[20px] rounded-bl-sm' : 'rounded-[20px] rounded-bl-md'
}`} }`}
style={{ style={{
backgroundColor: isMyMessage ? 'var(--accent-primary)' : 'var(--bg-card)', backgroundColor: isMyMessage ? 'var(--accent-primary)' : 'var(--bg-card)',
@@ -912,38 +912,38 @@ export default function ChatPage() {
border: isMyMessage ? 'none' : '1px solid var(--border-color)' border: isMyMessage ? 'none' : '1px solid var(--border-color)'
}} }}
> >
{/* Edit and Delete buttons - только для своих сообщений на десктопе */} {/* Edit and Delete buttons - показываем только для своих сообщений */}
{isMyMessage && editingMessageId !== message.id && ( {isMyMessage && editingMessageId !== message.id && (
<> <>
<button <button
onClick={() => handleStartEdit(message)} onClick={() => handleStartEdit(message)}
className="absolute -top-2 -right-10 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md hidden md:block" className="absolute -top-2 -right-10 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md"
style={{ backgroundColor: 'var(--bg-card)', color: 'var(--accent-primary)' }} style={{ backgroundColor: 'var(--bg-card)', color: 'var(--accent-primary)' }}
title="Редактировать" title="Редактировать"
> >
<Pencil size={12} className="md:w-3.5 md:h-3.5" /> <Pencil size={14} />
</button> </button>
<button <button
onClick={() => handleDeleteMessage(message.id)} onClick={() => handleDeleteMessage(message.id)}
className="absolute -top-2 -right-2 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md hidden md:block" className="absolute -top-2 -right-2 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md"
style={{ backgroundColor: 'var(--bg-card)', color: '#DC2626' }} style={{ backgroundColor: 'var(--bg-card)', color: '#DC2626' }}
title="Удалить" title="Удалить"
> >
<Trash2 size={12} className="md:w-3.5 md:h-3.5" /> <Trash2 size={14} />
</button> </button>
</> </>
)} )}
{/* Message content or edit input */} {/* Message content or edit input */}
{editingMessageId === message.id ? ( {editingMessageId === message.id ? (
<div className="flex items-center gap-1 md:gap-2"> <div className="flex items-center gap-2">
<input <input
type="text" type="text"
value={editingMessageText} value={editingMessageText}
onChange={(e) => setEditingMessageText(e.target.value)} onChange={(e) => setEditingMessageText(e.target.value)}
onKeyDown={(e) => handleEditKeyPress(e, message.id)} onKeyDown={(e) => handleEditKeyPress(e, message.id)}
autoFocus autoFocus
className="flex-1 bg-transparent border-b-2 outline-none text-sm md:text-base" className="flex-1 bg-transparent border-b-2 outline-none"
style={{ style={{
borderColor: isMyMessage ? 'rgba(255,255,255,0.5)' : 'var(--accent-primary)', borderColor: isMyMessage ? 'rgba(255,255,255,0.5)' : 'var(--accent-primary)',
color: isMyMessage ? 'white' : 'var(--text-primary)' color: isMyMessage ? 'white' : 'var(--text-primary)'
@@ -951,17 +951,17 @@ export default function ChatPage() {
/> />
<button <button
onClick={() => handleSaveEdit(message.id)} onClick={() => handleSaveEdit(message.id)}
className="p-1 hover:opacity-70 transition min-w-touch min-h-touch flex items-center justify-center" className="p-1 hover:opacity-70 transition"
title="Сохранить (Enter)" title="Сохранить (Enter)"
> >
<Check size={14} className="md:w-4 md:h-4" /> <Check size={16} />
</button> </button>
<button <button
onClick={handleCancelEdit} onClick={handleCancelEdit}
className="p-1 hover:opacity-70 transition min-w-touch min-h-touch flex items-center justify-center" className="p-1 hover:opacity-70 transition"
title="Отмена (Esc)" title="Отмена (Esc)"
> >
<X size={14} className="md:w-4 md:h-4" /> <X size={16} />
</button> </button>
</div> </div>
) : ( ) : (
@@ -969,11 +969,11 @@ export default function ChatPage() {
)} )}
{/* Message metadata */} {/* Message metadata */}
<div className={`flex items-center gap-1 md:gap-2 mt-0.5 md:mt-1 ${isMyMessage ? 'justify-end' : 'justify-start'}`}> <div className={`flex items-center gap-2 mt-1 ${isMyMessage ? 'justify-end' : 'justify-start'}`}>
{/* Edited indicator */} {/* Edited indicator */}
{message.is_edited && ( {message.is_edited && (
<span <span
className="text-[10px] md:text-[11px] font-inter font-medium italic" className="text-[11px] font-inter font-medium italic"
style={{ style={{
color: isMyMessage ? 'rgba(255,255,255,0.6)' : 'var(--text-secondary)', color: isMyMessage ? 'rgba(255,255,255,0.6)' : 'var(--text-secondary)',
}} }}
@@ -983,7 +983,7 @@ export default function ChatPage() {
)} )}
<span <span
className="text-[10px] md:text-[11px] font-inter font-medium" className="text-[11px] font-inter font-medium"
style={{ style={{
color: isMyMessage ? 'rgba(255,255,255,0.75)' : 'var(--text-secondary)', color: isMyMessage ? 'rgba(255,255,255,0.75)' : 'var(--text-secondary)',
}} }}
@@ -997,8 +997,8 @@ export default function ChatPage() {
{/* Read status for my messages */} {/* Read status for my messages */}
{isMyMessage && ( {isMyMessage && (
<svg <svg
width="14" width="16"
height="14" height="16"
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="none" fill="none"
style={{ opacity: 0.75 }} style={{ opacity: 0.75 }}
@@ -1022,7 +1022,7 @@ export default function ChatPage() {
</div> </div>
</motion.div> </motion.div>
{/* Reactions placeholder */} {/* Reactions placeholder - можно добавить позже */}
{isLastInGroup && false && ( {isLastInGroup && false && (
<div className="flex gap-1 mt-1 px-2"> <div className="flex gap-1 mt-1 px-2">
<span className="text-xs"></span> <span className="text-xs"></span>
@@ -1076,7 +1076,7 @@ export default function ChatPage() {
</AnimatePresence> </AnimatePresence>
{/* Message Input */} {/* Message Input */}
<div className="px-2 md:px-4 py-2 md:py-4 border-t flex-shrink-0" style={{ <div className="p-3 md:p-4 border-t" style={{
backgroundColor: 'var(--bg-card)', backgroundColor: 'var(--bg-card)',
borderColor: 'var(--border-color)' borderColor: 'var(--border-color)'
}}> }}>
@@ -1087,14 +1087,13 @@ export default function ChatPage() {
value={messageText} value={messageText}
onChange={(e) => setMessageText(e.target.value)} onChange={(e) => setMessageText(e.target.value)}
onKeyPress={handleKeyPress} onKeyPress={handleKeyPress}
placeholder="Сообщение..." placeholder="Написать сообщение..."
disabled={sendingMessage} disabled={sendingMessage}
className="flex-1 px-3 md:px-4 py-2 md:py-3 rounded-lg md:rounded-2xl font-inter text-sm md:text-base outline-none transition min-h-touch" className="flex-1 px-3 md:px-4 py-2.5 md:py-3 rounded-2xl font-inter text-sm outline-none transition"
style={{ style={{
backgroundColor: 'var(--bg-input)', backgroundColor: 'var(--bg-input)',
color: 'var(--text-primary)', color: 'var(--text-primary)',
borderBottom: '2px solid transparent', borderBottom: '2px solid transparent',
fontSize: '16px', // Prevent iOS zoom on input focus
}} }}
onFocus={(e) => e.target.style.borderBottomColor = 'var(--accent-primary)'} onFocus={(e) => e.target.style.borderBottomColor = 'var(--accent-primary)'}
onBlur={(e) => e.target.style.borderBottomColor = 'transparent'} onBlur={(e) => e.target.style.borderBottomColor = 'transparent'}
@@ -1103,13 +1102,13 @@ export default function ChatPage() {
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
onClick={handleSendMessage} onClick={handleSendMessage}
disabled={!messageText.trim() || sendingMessage} disabled={!messageText.trim() || sendingMessage}
className="px-3 md:px-6 py-2 md:py-3 rounded-lg md:rounded-2xl font-inter font-semibold flex items-center justify-center gap-1 md:gap-2 transition hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed min-h-touch min-w-touch" className="px-4 md:px-6 py-2.5 md:py-3 rounded-2xl font-inter font-semibold flex items-center gap-2 transition hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: 'var(--accent-primary)', color: 'white' }} style={{ backgroundColor: 'var(--accent-primary)', color: 'white' }}
> >
{sendingMessage ? ( {sendingMessage ? (
<div className="animate-spin w-4 h-4 md:w-5 md:h-5 border-2 border-white border-t-transparent rounded-full"></div> <div className="animate-spin w-5 h-5 border-2 border-white border-t-transparent rounded-full"></div>
) : ( ) : (
<Send size={16} className="md:w-4.5 md:h-4.5" /> <Send size={18} />
)} )}
</motion.button> </motion.button>
</div> </div>
@@ -1146,38 +1145,38 @@ export default function ChatPage() {
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50 flex items-end md:items-center justify-center z-50 p-4" className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
onClick={() => setViewingUser(null)} onClick={() => setViewingUser(null)}
> >
<motion.div <motion.div
initial={{ scale: 0.9, opacity: 0, y: 100 }} initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1, y: 0 }} animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0, y: 100 }} exit={{ scale: 0.9, opacity: 0 }}
transition={{ type: "spring", duration: 0.3 }} transition={{ type: "spring", duration: 0.3 }}
className="rounded-t-3xl md:rounded-3xl shadow-2xl w-full md:max-w-md overflow-hidden" className="rounded-3xl shadow-2xl max-w-md w-full overflow-hidden"
style={{ backgroundColor: 'var(--bg-card)' }} style={{ backgroundColor: 'var(--bg-card)' }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{/* Modal Header */} {/* Modal Header */}
<div className="relative p-4 md:p-6 border-b" style={{ borderColor: 'var(--border-color)' }}> <div className="relative p-6 border-b" style={{ borderColor: 'var(--border-color)' }}>
<button <button
onClick={() => setViewingUser(null)} onClick={() => setViewingUser(null)}
className="absolute top-3 md:top-4 right-3 md:right-4 p-2 rounded-full hover:bg-gray-100 transition min-h-touch min-w-touch" className="absolute top-4 right-4 p-2 rounded-full hover:bg-gray-100 transition"
style={{ color: 'var(--text-secondary)' }} style={{ color: 'var(--text-secondary)' }}
> >
<X size={18} className="md:w-5 md:h-5" /> <X size={20} />
</button> </button>
<h2 className="text-lg md:text-xl font-lora font-semibold" style={{ color: 'var(--text-primary)' }}> <h2 className="text-xl font-lora font-semibold" style={{ color: 'var(--text-primary)' }}>
Профиль пользователя Профиль пользователя
</h2> </h2>
</div> </div>
{/* Modal Content */} {/* Modal Content */}
<div className="p-4 md:p-6"> <div className="p-6">
{/* Avatar */} {/* Avatar */}
<div className="flex justify-center mb-4 md:mb-6"> <div className="flex justify-center mb-6">
<div <div
className="w-24 h-24 md:w-32 md:h-32 rounded-full flex items-center justify-center shadow-lg" className="w-32 h-32 rounded-full flex items-center justify-center shadow-lg"
style={{ style={{
backgroundImage: viewingUser.avatar_url ? `url(${viewingUser.avatar_url})` : undefined, backgroundImage: viewingUser.avatar_url ? `url(${viewingUser.avatar_url})` : undefined,
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -1186,32 +1185,32 @@ export default function ChatPage() {
}} }}
> >
{!viewingUser.avatar_url && ( {!viewingUser.avatar_url && (
<User size={36} className="md:w-12 md:h-12" style={{ color: 'var(--text-secondary)' }} /> <User size={48} style={{ color: 'var(--text-secondary)' }} />
)} )}
</div> </div>
</div> </div>
{/* User Info */} {/* User Info */}
<div className="space-y-3 md:space-y-4"> <div className="space-y-4">
<div className="text-center"> <div className="text-center">
<h3 className="text-lg md:text-2xl font-lora font-semibold mb-0.5 md:mb-1" style={{ color: 'var(--text-primary)' }}> <h3 className="text-2xl font-lora font-semibold mb-1" style={{ color: 'var(--text-primary)' }}>
{viewingUser.display_name} {viewingUser.display_name}
</h3> </h3>
<p className="text-xs md:text-sm font-inter" style={{ color: 'var(--text-secondary)' }}> <p className="text-sm font-inter" style={{ color: 'var(--text-secondary)' }}>
@{viewingUser.username} @{viewingUser.username}
</p> </p>
</div> </div>
{viewingUser.description && ( {viewingUser.description && (
<div className="p-3 md:p-4 rounded-lg md:rounded-2xl" style={{ backgroundColor: 'var(--bg-input)' }}> <div className="p-4 rounded-2xl" style={{ backgroundColor: 'var(--bg-input)' }}>
<p className="text-sm md:text-base font-inter" style={{ color: 'var(--text-primary)' }}> <p className="text-sm font-inter" style={{ color: 'var(--text-primary)' }}>
{viewingUser.description} {viewingUser.description}
</p> </p>
</div> </div>
)} )}
{viewingUser.birth_day && ( {viewingUser.birth_day && (
<div className="flex items-center gap-2 text-xs md:text-sm font-inter" style={{ color: 'var(--text-secondary)' }}> <div className="flex items-center gap-2 text-sm font-inter" style={{ color: 'var(--text-secondary)' }}>
<span>🎂</span> <span>🎂</span>
<span> <span>
{new Date(viewingUser.birth_day).toLocaleDateString('ru-RU', { {new Date(viewingUser.birth_day).toLocaleDateString('ru-RU', {
@@ -1223,18 +1222,18 @@ export default function ChatPage() {
</div> </div>
)} )}
<div className="flex items-center gap-2 text-xs md:text-sm font-inter" style={{ color: 'var(--text-secondary)' }}> <div className="flex items-center gap-2 text-sm font-inter" style={{ color: 'var(--text-secondary)' }}>
<span></span> <span></span>
<span className="truncate">{viewingUser.email}</span> <span>{viewingUser.email}</span>
</div> </div>
</div> </div>
{/* Actions */} {/* Actions */}
<div className="mt-4 md:mt-6 pt-4 md:pt-6 border-t" style={{ borderColor: 'var(--border-color)' }}> <div className="mt-6 pt-6 border-t" style={{ borderColor: 'var(--border-color)' }}>
<motion.button <motion.button
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
onClick={() => setViewingUser(null)} onClick={() => setViewingUser(null)}
className="w-full py-2.5 md:py-3 px-4 rounded-lg md:rounded-2xl font-inter font-semibold transition hover:opacity-90 min-h-touch" className="w-full py-3 px-4 rounded-2xl font-inter font-semibold transition hover:opacity-90"
style={{ backgroundColor: 'var(--accent-primary)', color: 'white' }} style={{ backgroundColor: 'var(--accent-primary)', color: 'white' }}
> >
Закрыть Закрыть
@@ -1253,7 +1252,7 @@ export default function ChatPage() {
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50 flex items-end md:items-center justify-center z-50 p-4" className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
onClick={() => { onClick={() => {
setShowNewChatModal(false); setShowNewChatModal(false);
setSearchQuery(''); setSearchQuery('');
@@ -1261,17 +1260,17 @@ export default function ChatPage() {
}} }}
> >
<motion.div <motion.div
initial={{ scale: 0.95, opacity: 0, y: 100 }} initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: 1, opacity: 1, y: 0 }} animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.95, opacity: 0, y: 100 }} exit={{ scale: 0.95, opacity: 0 }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="w-full md:max-w-md rounded-t-3xl md:rounded-3xl shadow-2xl overflow-hidden" className="w-full max-w-md rounded-3xl shadow-2xl overflow-hidden"
style={{ backgroundColor: 'var(--bg-card)' }} style={{ backgroundColor: 'var(--bg-card)' }}
> >
{/* Modal Header */} {/* Modal Header */}
<div className="p-4 md:p-6 border-b" style={{ borderColor: 'var(--border-color)' }}> <div className="p-6 border-b" style={{ borderColor: 'var(--border-color)' }}>
<div className="flex items-center justify-between mb-3 md:mb-4"> <div className="flex items-center justify-between mb-4">
<h2 className="text-lg md:text-2xl font-lora font-semibold" style={{ color: 'var(--text-primary)' }}> <h2 className="text-2xl font-lora font-semibold" style={{ color: 'var(--text-primary)' }}>
Новый чат Новый чат
</h2> </h2>
<button <button
@@ -1280,9 +1279,9 @@ export default function ChatPage() {
setSearchQuery(''); setSearchQuery('');
setSearchResults([]); setSearchResults([]);
}} }}
className="p-2 rounded-full hover:bg-gray-100 transition min-h-touch min-w-touch" className="p-2 rounded-full hover:bg-gray-100 transition"
> >
<X size={18} className="md:w-5 md:h-5" style={{ color: 'var(--text-secondary)' }} /> <X size={20} style={{ color: 'var(--text-secondary)' }} />
</button> </button>
</div> </div>
@@ -1293,52 +1292,51 @@ export default function ChatPage() {
onChange={(e) => handleSearchUsers(e.target.value)} onChange={(e) => handleSearchUsers(e.target.value)}
placeholder="Поиск пользователей..." placeholder="Поиск пользователей..."
autoFocus autoFocus
className="w-full px-3 md:px-4 py-2 md:py-3 rounded-lg md:rounded-2xl font-inter text-sm md:text-base outline-none transition" className="w-full px-4 py-3 rounded-2xl font-inter text-sm outline-none transition"
style={{ style={{
backgroundColor: 'var(--bg-input)', backgroundColor: 'var(--bg-input)',
color: 'var(--text-primary)', color: 'var(--text-primary)',
borderBottom: '2px solid transparent', borderBottom: '2px solid transparent',
fontSize: '16px', // Prevent iOS zoom
}} }}
/> />
</div> </div>
{/* Search Results */} {/* Search Results */}
<div className="max-h-96 overflow-y-auto p-3 md:p-4"> <div className="max-h-96 overflow-y-auto p-4">
{searchLoading ? ( {searchLoading ? (
<div className="text-center py-8"> <div className="text-center py-8">
<div className="animate-spin w-8 h-8 mx-auto mb-4 border-2 border-t-transparent rounded-full" <div className="animate-spin w-8 h-8 mx-auto mb-4 border-2 border-t-transparent rounded-full"
style={{ borderColor: 'var(--accent-primary)', borderTopColor: 'transparent' }}></div> style={{ borderColor: 'var(--accent-primary)', borderTopColor: 'transparent' }}></div>
<p className="font-inter text-xs md:text-sm" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-sm" style={{ color: 'var(--text-secondary)' }}>
Поиск... Поиск...
</p> </p>
</div> </div>
) : searchQuery.trim().length < 2 ? ( ) : searchQuery.trim().length < 2 ? (
<div className="text-center py-8"> <div className="text-center py-8">
<p className="font-inter text-xs md:text-sm" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-sm" style={{ color: 'var(--text-secondary)' }}>
Введите имя пользователя для поиска Введите имя пользователя для поиска
</p> </p>
</div> </div>
) : searchResults.length === 0 ? ( ) : searchResults.length === 0 ? (
<div className="text-center py-8"> <div className="text-center py-8">
<p className="font-inter text-xs md:text-sm" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-sm" style={{ color: 'var(--text-secondary)' }}>
Пользователи не найдены Пользователи не найдены
</p> </p>
</div> </div>
) : ( ) : (
<div className="space-y-1.5 md:space-y-2"> <div className="space-y-2">
{searchResults.map((foundUser) => ( {searchResults.map((foundUser) => (
<motion.button <motion.button
key={foundUser.id} key={foundUser.id}
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={() => handleStartChatWithUser(foundUser)} onClick={() => handleStartChatWithUser(foundUser)}
className="w-full flex items-center gap-2 md:gap-3 p-2.5 md:p-3 rounded-lg md:rounded-2xl transition hover:shadow-sm min-h-touch" className="w-full flex items-center gap-3 p-3 rounded-2xl transition hover:shadow-sm"
style={{ backgroundColor: 'var(--bg-input)' }} style={{ backgroundColor: 'var(--bg-input)' }}
> >
{/* Avatar */} {/* Avatar */}
<div <div
className="w-10 h-10 md:w-12 md:h-12 flex items-center justify-center flex-shrink-0" className="w-12 h-12 flex items-center justify-center flex-shrink-0"
style={{ style={{
backgroundImage: foundUser.avatar_url ? `url(${foundUser.avatar_url})` : undefined, backgroundImage: foundUser.avatar_url ? `url(${foundUser.avatar_url})` : undefined,
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -1353,11 +1351,11 @@ export default function ChatPage() {
</div> </div>
{/* User Info */} {/* User Info */}
<div className="flex-1 text-left min-w-0"> <div className="flex-1 text-left">
<p className="font-inter font-semibold text-xs md:text-sm truncate" style={{ color: 'var(--text-primary)' }}> <p className="font-inter font-semibold text-sm" style={{ color: 'var(--text-primary)' }}>
{foundUser.display_name || foundUser.username} {foundUser.display_name || foundUser.username}
</p> </p>
<p className="font-inter text-xs truncate" style={{ color: 'var(--text-secondary)' }}> <p className="font-inter text-xs" style={{ color: 'var(--text-secondary)' }}>
@{foundUser.username} @{foundUser.username}
</p> </p>
</div> </div>