Аватары заглушки
This commit is contained in:
@@ -98,6 +98,7 @@ const translations = {
|
||||
sendError: 'Ошибка отправки',
|
||||
// New chat
|
||||
newChat: 'Новый чат',
|
||||
add: 'Добавить',
|
||||
menu: 'Меню',
|
||||
// Date picker
|
||||
months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
|
||||
@@ -384,6 +385,7 @@ const translations = {
|
||||
fileBtn: 'File',
|
||||
sendError: 'Send error',
|
||||
newChat: 'New chat',
|
||||
add: 'Add',
|
||||
menu: 'Menu',
|
||||
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
weekDays: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
|
||||
|
||||
@@ -31,16 +31,16 @@ function AvatarInner({ src, name, size = 'md', className = '', online }: AvatarP
|
||||
const gradientClass = generateAvatarColor(name || '');
|
||||
|
||||
return (
|
||||
<div className={`relative shrink-0 ${className} rounded-full border-2 border-outline-variant/10 overflow-hidden`}>
|
||||
<div className={`relative shrink-0 ${className} rounded-2xl border-2 border-outline-variant/10 overflow-hidden`}>
|
||||
{src ? (
|
||||
<img
|
||||
src={src}
|
||||
alt={name}
|
||||
className={`${sizeClass} rounded-full object-cover`}
|
||||
className={`${sizeClass} rounded-2xl object-cover`}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={`${sizeClass} rounded-full ${gradientClass} flex items-center justify-center text-on-primary font-black tracking-tighter shadow-inner`}
|
||||
className={`${sizeClass} rounded-2xl ${gradientClass} flex items-center justify-center text-on-primary font-black tracking-tighter shadow-inner`}
|
||||
>
|
||||
{initials}
|
||||
</div>
|
||||
|
||||
@@ -45,10 +45,10 @@ export default function GlobalNavBar({ activeTab, onTabChange }: GlobalNavBarPro
|
||||
</div>
|
||||
|
||||
<div className="mt-auto group cursor-pointer relative">
|
||||
<div className="absolute -inset-1 bg-primary/20 rounded-full opacity-0 group-hover:opacity-100 blur transition-opacity" />
|
||||
<div className="absolute -inset-1 bg-primary/20 rounded-2xl opacity-0 group-hover:opacity-100 blur transition-opacity" />
|
||||
<img
|
||||
alt="User Profile"
|
||||
className="relative w-10 h-10 rounded-full border-2 border-outline-variant/10 hover:border-primary/50 transition-colors object-cover"
|
||||
className="relative w-10 h-10 rounded-2xl border-2 border-outline-variant/10 hover:border-primary/50 transition-colors object-cover"
|
||||
src={user?.avatar || `https://ui-avatars.com/api/?name=${user?.username || 'user'}&background=3096e5&color=fff`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,8 @@ import DatePicker from '../components/ui/DatePicker';
|
||||
import TelegramImportModal from '../../../modules/users/presentation/components/TelegramImportModal';
|
||||
import type { User as UserType, UserPresence, FriendRequest, FriendWithId } from '../../domain/types';
|
||||
|
||||
import { getInitials } from '../../utils/utils';
|
||||
|
||||
type SideView = 'main' | 'profile' | 'settings' | 'about' | 'themes' | 'friends';
|
||||
|
||||
interface SideMenuProps {
|
||||
@@ -135,12 +137,7 @@ export default function SideMenu({ isOpen, onClose, onOpenProfile }: SideMenuPro
|
||||
onClose();
|
||||
};
|
||||
|
||||
const initials = (user?.displayName || user?.userName || user?.username || '??')
|
||||
.split(' ')
|
||||
.map((w: string) => w[0])
|
||||
.join('')
|
||||
.slice(0, 2)
|
||||
.toUpperCase();
|
||||
const initials = getInitials(user?.displayName || user?.userName || user?.username || '??');
|
||||
|
||||
const menuItems = [
|
||||
{ icon: User, label: t('myProfile'), onClick: () => { onClose(); onOpenProfile(); } },
|
||||
@@ -173,12 +170,12 @@ export default function SideMenu({ isOpen, onClose, onOpenProfile }: SideMenuPro
|
||||
<div className="flex items-start justify-between mb-5">
|
||||
{/* Avatar with glow ring */}
|
||||
<div className="relative group cursor-pointer" onClick={() => { onClose(); onOpenProfile(); }}>
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-accent via-purple-500 to-accent rounded-full opacity-60 blur group-hover:opacity-90 transition duration-500 animate-[spin_4s_linear_infinite]" />
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-accent via-purple-500 to-accent rounded-2xl opacity-60 blur group-hover:opacity-90 transition duration-500 animate-[spin_4s_linear_infinite]" />
|
||||
<div className="relative">
|
||||
{user?.avatar ? (
|
||||
<img src={user.avatar} alt="" className="w-[72px] h-[72px] rounded-full object-cover ring-[3px] ring-surface" />
|
||||
<img src={user.avatar} alt="" className="w-[72px] h-[72px] rounded-2xl object-cover ring-[3px] ring-surface" />
|
||||
) : (
|
||||
<div className="w-[72px] h-[72px] rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center ring-[3px] ring-surface relative overflow-hidden">
|
||||
<div className="w-[72px] h-[72px] rounded-2xl bg-linear-to-br from-primary/80 to-primary flex items-center justify-center ring-[3px] ring-surface relative overflow-hidden">
|
||||
<span className="relative z-10 text-2xl font-black text-on-primary drop-shadow-md">{initials}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -518,10 +515,10 @@ export default function SideMenu({ isOpen, onClose, onOpenProfile }: SideMenuPro
|
||||
{friendSearchResults.map((u) => (
|
||||
<div key={u.id} className="flex items-center gap-3 p-3 rounded-xl bg-white/5 border border-border/50">
|
||||
{u.avatar ? (
|
||||
<img src={u.avatar} alt="" className="w-10 h-10 rounded-full object-cover" />
|
||||
<img src={u.avatar} alt="" className="w-10 h-10 rounded-xl object-cover" />
|
||||
) : (
|
||||
<div className="w-10 h-10 rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary font-black text-sm">
|
||||
{(u.displayName || u.username || '?')[0].toUpperCase()}
|
||||
<div className="w-10 h-10 rounded-xl bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary font-black text-sm">
|
||||
{getInitials(u.displayName || u.username || '?')}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -553,10 +550,10 @@ export default function SideMenu({ isOpen, onClose, onOpenProfile }: SideMenuPro
|
||||
{friendRequests.map((req) => (
|
||||
<div key={req.id} className="flex items-center gap-3 p-3 rounded-xl bg-white/5 border border-border/50">
|
||||
{req.user.avatar ? (
|
||||
<img src={req.user.avatar} alt="" className="w-10 h-10 rounded-full object-cover" />
|
||||
<img src={req.user.avatar} alt="" className="w-10 h-10 rounded-xl object-cover" />
|
||||
) : (
|
||||
<div className="w-10 h-10 rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary font-black text-sm">
|
||||
{(req.user.displayName || req.user.username || '?')[0].toUpperCase()}
|
||||
<div className="w-10 h-10 rounded-xl bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary font-black text-sm">
|
||||
{getInitials(req.user.displayName || req.user.username || '?')}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -598,10 +595,10 @@ export default function SideMenu({ isOpen, onClose, onOpenProfile }: SideMenuPro
|
||||
<div key={friend.id} className="flex items-center gap-3 p-3 rounded-xl hover:bg-white/5 transition-colors group/friend">
|
||||
<div className="relative">
|
||||
{friend.avatar ? (
|
||||
<img src={friend.avatar} alt="" className="w-10 h-10 rounded-full object-cover" />
|
||||
<img src={friend.avatar} alt="" className="w-10 h-10 rounded-xl object-cover" />
|
||||
) : (
|
||||
<div className="w-10 h-10 rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary font-black text-sm">
|
||||
{(friend.displayName || friend.username || '?')[0].toUpperCase()}
|
||||
<div className="w-10 h-10 rounded-xl bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary font-black text-sm">
|
||||
{getInitials(friend.displayName || friend.username || '?')}
|
||||
</div>
|
||||
)}
|
||||
{friend.isOnline && (
|
||||
|
||||
@@ -122,38 +122,44 @@ export default function Sidebar() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Story circles */}
|
||||
{(storyGroups.length > 0 || true) && (
|
||||
<div className="flex items-center gap-4 px-6 py-2 pb-6 overflow-x-auto scrollbar-hide flex-shrink-0">
|
||||
{/* Add story circle */}
|
||||
<button
|
||||
onClick={() => setShowCreateStory(true)}
|
||||
className="flex flex-col items-center gap-1.5 flex-shrink-0 group w-[60px]"
|
||||
>
|
||||
<div className="w-[50px] h-[50px] rounded-full border border-dashed border-zinc-600 flex items-center justify-center group-hover:border-accent group-hover:bg-accent/10 transition-colors">
|
||||
<Plus size={20} className="text-accent transition-colors" />
|
||||
</div>
|
||||
<span className="text-[11px] text-zinc-400 truncate w-full text-center">{t('newStory')}</span>
|
||||
</button>
|
||||
{/* Stories Section */}
|
||||
<div className="px-6 mb-2">
|
||||
<h2 className="text-[11px] font-black text-on-surface-variant/40 uppercase tracking-[0.12em] select-none">
|
||||
{t('stories') || 'ИСТОРИИ'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{storyGroups.map((group, idx) => {
|
||||
const avatar = group.user.avatarUrl || group.user.avatar;
|
||||
const avatarUrl = avatar ? `${API_URL}${avatar}` : null;
|
||||
const isMine = group.user.id === user?.id;
|
||||
return (
|
||||
<button
|
||||
key={group.user.id}
|
||||
onClick={() => openViewer(idx)}
|
||||
className="flex flex-col items-center gap-1.5 flex-shrink-0 group w-[60px]"
|
||||
>
|
||||
<div className={`w-[50px] h-[50px] rounded-full flex items-center justify-center transition-transform group-hover:scale-[1.03] ${
|
||||
group.hasUnviewed
|
||||
? 'border-[2px] border-accent'
|
||||
: isMine
|
||||
? 'border border-zinc-600'
|
||||
: 'border border-zinc-700'
|
||||
}`}>
|
||||
<div className="w-[44px] h-[44px] rounded-full flex items-center justify-center overflow-hidden">
|
||||
<div className="flex items-center gap-4 px-6 py-2 pb-6 overflow-x-auto scrollbar-hide flex-shrink-0">
|
||||
{/* Add story item */}
|
||||
<button
|
||||
onClick={() => setShowCreateStory(true)}
|
||||
className="flex flex-col items-center gap-2 flex-shrink-0 group w-[62px]"
|
||||
>
|
||||
<div className="w-[56px] h-[56px] rounded-2xl border-2 border-dashed border-white/10 flex items-center justify-center group-hover:border-accent group-hover:bg-accent/5 transition-all">
|
||||
<Plus size={22} className="text-accent/80 group-hover:text-accent transition-colors" />
|
||||
</div>
|
||||
<span className="text-[12px] font-medium text-zinc-400 group-hover:text-white transition-colors truncate w-full text-center">
|
||||
{t('add')}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{storyGroups.map((group, idx) => {
|
||||
const avatar = group.user.avatarUrl || group.user.avatar;
|
||||
const avatarUrl = avatar ? `${API_URL}${avatar}` : null;
|
||||
const isMine = group.user.id === user?.id;
|
||||
return (
|
||||
<button
|
||||
key={group.user.id}
|
||||
onClick={() => openViewer(idx)}
|
||||
className="flex flex-col items-center gap-2 flex-shrink-0 group w-[62px]"
|
||||
>
|
||||
<div className={`relative p-[1.5px] rounded-[1.25rem] transition-all duration-300 ${
|
||||
group.hasUnviewed
|
||||
? 'bg-accent shadow-[0_0_15px_rgba(48,150,229,0.2)] scale-[1.02]'
|
||||
: 'bg-white/5'
|
||||
}`}>
|
||||
<div className="w-[53px] h-[53px] rounded-[1.125rem] bg-surface-container-low p-1 flex items-center justify-center">
|
||||
<div className="relative w-full h-full rounded-[0.9rem] overflow-hidden">
|
||||
<Avatar
|
||||
src={avatarUrl}
|
||||
name={group.user.displayName || group.user.userName || group.user.username || '?'}
|
||||
@@ -161,14 +167,14 @@ export default function Sidebar() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-[11px] text-zinc-400 truncate w-full text-center">
|
||||
{isMine ? t('myStory') : (group.user.displayName || group.user.userName || group.user.username || '?').split(' ')[0]}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[12px] font-medium text-zinc-400 group-hover:text-white transition-colors truncate w-full text-center">
|
||||
{isMine ? t('myStory') : (group.user.displayName || group.user.userName || group.user.username || '?').split(' ')[0]}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Список чатов */}
|
||||
<div className="flex-1 overflow-y-auto px-2 custom-scrollbar">
|
||||
|
||||
@@ -148,7 +148,7 @@ function ChatListItem({ chat, isActive }: ChatListItemProps) {
|
||||
{/* Аватар */}
|
||||
<div className="relative flex-shrink-0">
|
||||
{isFavorites ? (
|
||||
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-primary to-primary-container flex items-center justify-center shadow-lg shadow-primary/10 border-2 border-outline-variant/10">
|
||||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-primary to-primary-container flex items-center justify-center shadow-lg shadow-primary/10 border-2 border-outline-variant/10">
|
||||
<span className="material-symbols-outlined text-on-primary-container text-[24px]">bookmark</span>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -427,7 +427,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
|
||||
initial={{ scale: 0.8, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="w-24 h-24 mb-6 rounded-full bg-[#1c1c1c] flex items-center justify-center shadow-[0_0_80px_rgba(48,150,229,0.15)] border border-white/5"
|
||||
className="w-24 h-24 mb-6 rounded-[2rem] bg-[#1c1c1c] flex items-center justify-center shadow-[0_0_80px_rgba(48,150,229,0.15)] border border-white/5"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[#3096e5] text-5xl select-none" style={{ fontVariationSettings: "'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 48" }}>
|
||||
forum
|
||||
@@ -613,7 +613,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
|
||||
>
|
||||
<div className="relative flex-shrink-0 transform transition-all duration-500 group-hover:scale-105 active:scale-95">
|
||||
{isFavorites ? (
|
||||
<div className="w-11 h-11 rounded-full bg-gradient-to-br from-primary to-primary-container flex items-center justify-center shadow-lg shadow-primary/10 border-2 border-outline-variant/10">
|
||||
<div className="w-11 h-11 rounded-2xl bg-gradient-to-br from-primary to-primary-container flex items-center justify-center shadow-lg shadow-primary/10 border-2 border-outline-variant/10">
|
||||
<span className="material-symbols-outlined text-on-primary-container text-[20px]">bookmark</span>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useAuthStore } from '../../../auth/application/authStore';
|
||||
import { useChatStore } from '../../application/chatStore';
|
||||
import { getSocket } from '../../../../core/infrastructure/socket';
|
||||
import { useLang } from '../../../../core/infrastructure/i18n';
|
||||
import { extractWaveform, getMediaUrl, generateAvatarColor } from '../../../../core/utils/utils';
|
||||
import { extractWaveform, getMediaUrl, generateAvatarColor, getInitials } from '../../../../core/utils/utils';
|
||||
import type { Message, MediaItem, Reaction, ChatMember } from '../../../../core/domain/types';
|
||||
import ImageLightbox from '../../../../core/presentation/components/ui/ImageLightbox';
|
||||
import LinkPreview from './LinkPreview';
|
||||
@@ -313,7 +313,7 @@ function MessageBubble({
|
||||
if (reactionGroups[r.emoji].avatars.length < 3) {
|
||||
reactionGroups[r.emoji].avatars.push({
|
||||
url: r.user?.avatar,
|
||||
initials: displayName[0].toUpperCase(),
|
||||
initials: getInitials(displayName),
|
||||
colorClass: 'from-primary/80 to-primary-container'
|
||||
});
|
||||
}
|
||||
@@ -398,10 +398,10 @@ function MessageBubble({
|
||||
{showAvatar ? (
|
||||
<button onClick={() => onViewProfile?.(message.senderId)}>
|
||||
{senderAvatar ? (
|
||||
<img src={senderAvatar} alt="" className="w-8 h-8 rounded-full object-cover" />
|
||||
<img src={senderAvatar} alt="" className="w-8 h-8 rounded-lg object-cover" />
|
||||
) : (
|
||||
<div className="w-8 h-8 rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary text-xs font-black shadow-inner">
|
||||
{senderName[0]?.toUpperCase() || '?'}
|
||||
<div className="w-8 h-8 rounded-lg bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary text-xs font-black shadow-inner">
|
||||
{getInitials(senderName)}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
@@ -845,10 +845,10 @@ function MessageBubble({
|
||||
{showAvatar ? (
|
||||
<button onClick={() => onViewProfile?.(message.senderId)}>
|
||||
{senderAvatar ? (
|
||||
<img src={senderAvatar} alt="" className="w-8 h-8 rounded-full object-cover" />
|
||||
<img src={senderAvatar} alt="" className="w-8 h-8 rounded-lg object-cover" />
|
||||
) : (
|
||||
<div className="w-8 h-8 rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary text-xs font-black shadow-inner">
|
||||
{senderName[0]?.toUpperCase() || '?'}
|
||||
<div className="w-8 h-8 rounded-lg bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary text-[10px] font-black shadow-inner">
|
||||
{getInitials(senderName)}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@@ -9,6 +9,8 @@ import { useAuthStore } from '../../../auth/application/authStore';
|
||||
import { useLang } from '../../../../core/infrastructure/i18n';
|
||||
import type { UserPresence, FriendWithId } from '../../../../core/domain/types';
|
||||
|
||||
import { getInitials } from '../../../../core/utils/utils';
|
||||
|
||||
interface NewChatModalProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
@@ -55,6 +57,18 @@ export default function NewChatModal({ onClose }: NewChatModalProps) {
|
||||
const handleSelectUser = async (selectedUser: UserPresence) => {
|
||||
if (mode === 'personal') {
|
||||
try {
|
||||
// Check if personal chat with this user already exists
|
||||
const existingChat = useChatStore.getState().chats.find(
|
||||
(c) => c.type === 'personal' && c.members.some((m) => m.user.id === selectedUser.id)
|
||||
);
|
||||
|
||||
if (existingChat) {
|
||||
setActiveChat(existingChat.id);
|
||||
loadMessages(existingChat.id);
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
const chat = await ChatApi.createPersonalChat(selectedUser.id);
|
||||
addChat(chat);
|
||||
import('../../../../core/infrastructure/socket').then(({ getSocket }) => getSocket()?.emit('join_chat', chat.id));
|
||||
@@ -170,10 +184,10 @@ export default function NewChatModal({ onClose }: NewChatModalProps) {
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-knot-500/20 border border-knot-500/30"
|
||||
>
|
||||
{u.avatar ? (
|
||||
<img src={u.avatar} alt="" className="w-5 h-5 rounded-full object-cover" />
|
||||
<img src={u.avatar} alt="" className="w-5 h-5 rounded-lg object-cover" />
|
||||
) : (
|
||||
<div className="w-5 h-5 rounded-full bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary text-[9px] font-black shadow-inner">
|
||||
{(u.displayName || u.userName || u.username || '?')[0]?.toUpperCase()}
|
||||
<div className="w-5 h-5 rounded-lg bg-linear-to-br from-primary/80 to-primary flex items-center justify-center text-on-primary text-[9px] font-black shadow-inner">
|
||||
{getInitials(u.displayName || u.userName || u.username || '?')}
|
||||
</div>
|
||||
)}
|
||||
<span className="text-xs text-white">{u.displayName || u.userName || u.username || ''}</span>
|
||||
@@ -279,7 +293,7 @@ export default function NewChatModal({ onClose }: NewChatModalProps) {
|
||||
<img src={u.avatar} alt="" className="w-11 h-11 rounded-2xl object-cover" />
|
||||
) : (
|
||||
<div className="w-11 h-11 rounded-2xl bg-linear-to-br from-primary to-primary-container flex items-center justify-center text-on-primary font-black text-sm shadow-inner">
|
||||
{(u.displayName || u.userName || u.username || '?')[0]?.toUpperCase() || '?'}
|
||||
{getInitials(u.displayName || u.userName || u.username || '?')}
|
||||
</div>
|
||||
)}
|
||||
{u.isOnline && (
|
||||
@@ -331,7 +345,7 @@ export default function NewChatModal({ onClose }: NewChatModalProps) {
|
||||
<img src={u.avatar} alt="" className="w-11 h-11 rounded-2xl object-cover" />
|
||||
) : (
|
||||
<div className="w-11 h-11 rounded-2xl bg-linear-to-br from-primary to-primary-container flex items-center justify-center text-on-primary font-black text-sm shadow-inner">
|
||||
{(u.displayName || u.userName || u.username || '?')[0]?.toUpperCase() || '?'}
|
||||
{getInitials(u.displayName || u.userName || u.username || '?')}
|
||||
</div>
|
||||
)}
|
||||
{u.isOnline && (
|
||||
|
||||
@@ -15,6 +15,7 @@ import { httpClient } from '../../../../core/infrastructure/httpClient';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import ImageLightbox from '../../../../core/presentation/components/ui/ImageLightbox';
|
||||
import type { FriendWithId, FriendRequest } from '../../../../core/domain/types';
|
||||
import { getInitials } from '../../../../core/utils/utils';
|
||||
|
||||
interface UserProfileProps {
|
||||
userId: string;
|
||||
@@ -225,12 +226,12 @@ export default function UserProfile({ userId, onClose, onMessage, isSelf: isSelf
|
||||
{/* Profile Card */}
|
||||
<div className="flex flex-col items-center mb-10">
|
||||
<div className="relative mb-8 group">
|
||||
<div className="absolute -inset-4 bg-primary/20 blur-3xl rounded-full opacity-40 group-hover:opacity-60 transition-opacity" />
|
||||
<div className="absolute -inset-4 bg-primary/20 blur-3xl rounded-[3rem] opacity-40 group-hover:opacity-60 transition-opacity" />
|
||||
<div className="relative w-40 h-40 rounded-[3rem] bg-zinc-900 border-2 border-white/10 overflow-hidden shadow-2xl">
|
||||
{user.avatar ? (
|
||||
<img src={resolveUrl(user.avatar)} className="w-full h-full object-cover" alt="" />
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center bg-gradient-to-br from-primary/30 to-primary-container/10 text-5xl font-black text-primary">{user.displayName?.[0] || 'U'}</div>
|
||||
<div className="w-full h-full flex items-center justify-center bg-gradient-to-br from-primary/30 to-primary-container/10 text-5xl font-black text-primary">{getInitials(user.displayName)}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user