feat: avatar upload for profile/chats, fix session persistence on refresh
- Profile modal: upload/remove user avatar, edit name/bio/phone, change password - Chat info panel: upload/remove chat photo (owner/admin only) - Avatar component: render photo when available, fallback to initials - Session fix: initialize Zustand store from localStorage to prevent redirect on refresh - Backend: avatar upload endpoints for users and chats, migration adds avatar columns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import ChatHeader from '../components/ChatHeader';
|
||||
import ChatInfoPanel from '../components/ChatInfoPanel';
|
||||
import NewChatModal from '../components/NewChatModal';
|
||||
import AdminPanel from '../components/admin/AdminPanel';
|
||||
import ProfileModal from '../components/ProfileModal';
|
||||
import { usePushNotifications } from '../hooks/usePushNotifications';
|
||||
import { Chat } from '../types';
|
||||
|
||||
@@ -21,6 +22,7 @@ export default function MainLayout() {
|
||||
const [showNew, setShowNew] = useState(false);
|
||||
const [showAdmin, setShowAdmin] = useState(false);
|
||||
const [showInfo, setShowInfo] = useState(false);
|
||||
const [showProfile, setShowProfile] = useState(false);
|
||||
const [mobileChatOpen, setMobileChatOpen] = useState(false);
|
||||
const [fullChat, setFullChat] = useState<Chat | null>(null);
|
||||
|
||||
@@ -91,7 +93,8 @@ export default function MainLayout() {
|
||||
{/* Sidebar Header */}
|
||||
<div className="px-4 py-3 border-b border-gray-100">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<Avatar name={user?.displayName || ''} color={user?.avatarColor || '#3b82f6'} size="sm" />
|
||||
<Avatar name={user?.displayName || ''} color={user?.avatarColor || '#3b82f6'} avatar={user?.avatar} size="sm"
|
||||
onClick={() => setShowProfile(true)} className="cursor-pointer" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="font-semibold text-gray-900 text-sm truncate">{user?.displayName}</div>
|
||||
<div className="flex items-center gap-1 text-xs text-gray-400">
|
||||
@@ -108,6 +111,10 @@ export default function MainLayout() {
|
||||
<Shield className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
<button onClick={() => setShowProfile(true)}
|
||||
className="p-1.5 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600" title="Профиль">
|
||||
<Settings className="w-4 h-4" />
|
||||
</button>
|
||||
<button onClick={() => setShowNew(true)}
|
||||
className="p-1.5 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600" title="Новый чат">
|
||||
<Edit className="w-4 h-4" />
|
||||
@@ -201,6 +208,7 @@ export default function MainLayout() {
|
||||
if (chat) openChat(chat);
|
||||
}} />}
|
||||
{showAdmin && <AdminPanel onClose={() => setShowAdmin(false)} />}
|
||||
{showProfile && <ProfileModal onClose={() => setShowProfile(false)} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user