feat: Aether Drift UI polish — popout redesign, sidebar cleanup, status dot fix
- Redesign UserProfilePopout with glass material, viewport clamping, flow-based avatar layout, and subtle send-message button - Server sidebar: downsize icons to 40px (matching prototype), remove nonfunctional Explore button, add separator before action buttons, replace Tooltip with native title, remove hover boxShadow artifacts - Avatar status dot: fixed 12px size with semi-transparent border for universal cutout effect matching the design prototype - MemberListToggleButton: distinct three-person icon (was duplicate of Add Friends) - FriendsPage: fix Add Friend button contrast (dark text on mint background) - ChannelSidebar: rename Nitro/Shop to Coming Soon placeholders
This commit is contained in:
@@ -142,25 +142,22 @@ export function ChannelSidebar() {
|
||||
<span className="font-medium text-[16px]">Friends</span>
|
||||
</div>
|
||||
|
||||
{/* Nitro */}
|
||||
{/* Placeholder nav items */}
|
||||
<div
|
||||
className="flex items-center gap-3 px-2 h-[42px] rounded-[4px] cursor-pointer mb-[2px] transition-colors group text-txt-tertiary hover:bg-interactive-hover hover:text-txt-secondary"
|
||||
className="flex items-center gap-3 px-2 h-[42px] rounded-[4px] mb-[2px] text-txt-tertiary cursor-default opacity-50"
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 opacity-70 group-hover:opacity-100">
|
||||
<path d="M2.98966977,9.35789159 C2.98966977,9.77582472 2.63542482,10.1300697 2.21749169,10.1300697 L2.21749169,10.1300697 C1.79955856,10.1300697 1.44531361,9.77582472 1.44531361,9.35789159 L1.44531361,9.35789159 C1.44531361,8.93995846 1.79955856,8.58571351 2.21749169,8.58571351 L2.21749169,8.58571351 C2.63542482,8.58571351 2.98966977,8.93995846 2.98966977,9.35789159 Z" transform="translate(12, 12) scale(1.2) translate(-12, -12)" />
|
||||
<path d="M21.7,9.358 L12.957,2.46 C12.396,2.014 11.604,2.014 11.043,2.46 L2.3,9.358 C2.113,9.507 2,9.734 2,9.975 L2,19.5 C2,20.881 3.119,22 4.5,22 L19.5,22 C20.881,22 22,20.881 22,19.5 L22,9.975 C22,9.734 21.887,9.507 21.7,9.358 Z M12,17.5 C10.619,17.5 9.5,16.381 9.5,15 C9.5,13.619 10.619,12.5 12,12.5 C13.381,12.5 14.5,13.619 14.5,15 C14.5,16.381 13.381,17.5 12,17.5 Z" />
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
|
||||
</svg>
|
||||
<span className="font-medium text-[16px]">Nitro</span>
|
||||
<span className="font-medium text-[16px]">Coming Soon</span>
|
||||
</div>
|
||||
|
||||
{/* Shop */}
|
||||
<div
|
||||
className="flex items-center gap-3 px-2 h-[42px] rounded-[4px] cursor-pointer mb-[2px] transition-colors group text-txt-tertiary hover:bg-interactive-hover hover:text-txt-secondary"
|
||||
className="flex items-center gap-3 px-2 h-[42px] rounded-[4px] mb-[2px] text-txt-tertiary cursor-default opacity-50"
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 opacity-70 group-hover:opacity-100">
|
||||
<path d="M2 5.5A1.5 1.5 0 0 1 3.5 4h17A1.5 1.5 0 0 1 22 5.5V7H2V5.5ZM2 9v9.5A1.5 1.5 0 0 0 3.5 20h17a1.5 1.5 0 0 0 1.5-1.5V9H2Zm9.5 3a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5Zm-4-1h2a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1Z" />
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0">
|
||||
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z" />
|
||||
</svg>
|
||||
<span className="font-medium text-[16px]">Shop</span>
|
||||
<span className="font-medium text-[16px]">Coming Soon</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-[18px] px-2 mb-1 flex items-center justify-between group">
|
||||
|
||||
@@ -14,7 +14,7 @@ export function MemberListToggleButton() {
|
||||
title="Toggle Member List"
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14 8.00598C14 10.211 12.206 12.006 10 12.006C7.795 12.006 6 10.211 6 8.00598C6 5.80098 7.794 4.00598 10 4.00598C12.206 4.00598 14 5.80098 14 8.00598ZM2 19.006C2 15.473 5.29 13.006 10 13.006C14.711 13.006 18 15.473 18 19.006V20.006H2V19.006ZM20 20.006H22V19.006C22 16.451 20.178 14.471 17.532 13.471C19.461 14.601 20 16.561 20 19.006V20.006Z" />
|
||||
<path d="M12 12.75c1.63 0 3.07.39 4.24.9 1.08.48 1.76 1.56 1.76 2.73V18H6v-1.61c0-1.18.68-2.26 1.76-2.73 1.17-.52 2.61-.91 4.24-.91zM4 13c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm1.13 1.1c-.37-.06-.74-.1-1.13-.1-.99 0-1.93.21-2.78.58A2.01 2.01 0 0 0 0 16.43V18h4.5v-1.61c0-.83.23-1.61.63-2.29zM20 13c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm4 3.43c0-.81-.48-1.53-1.22-1.85A6.95 6.95 0 0 0 20 14c-.39 0-.76.04-1.13.1.4.68.63 1.46.63 2.29V18H24v-1.57zM12 6c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3z" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { useServerStore } from '../../stores/serverStore';
|
||||
import { useChatStore } from '../../stores/chatStore';
|
||||
import { useUIStore } from '../../stores/uiStore';
|
||||
import { Tooltip } from '../ui/Tooltip';
|
||||
|
||||
import { getServerGradient, HOME_GRADIENT } from '../../utils/gradients';
|
||||
|
||||
interface SidebarItemProps {
|
||||
@@ -13,7 +13,7 @@ interface SidebarItemProps {
|
||||
active: boolean;
|
||||
onClick: () => void;
|
||||
type?: 'server' | 'dm' | 'action';
|
||||
actionType?: 'add' | 'join' | 'explore';
|
||||
actionType?: 'add' | 'join';
|
||||
hasUnread?: boolean;
|
||||
}
|
||||
|
||||
@@ -22,94 +22,86 @@ function SidebarItem({ id, name, icon, active, onClick, type = 'server', actionT
|
||||
const firstLetter = name.charAt(0).toUpperCase();
|
||||
|
||||
const getPillHeight = () => {
|
||||
if (active) return 'h-10';
|
||||
if (isHovered) return 'h-5';
|
||||
if (active) return 'h-8';
|
||||
if (isHovered) return 'h-4';
|
||||
if (hasUnread && !active) return 'h-2';
|
||||
return 'h-2 scale-0';
|
||||
};
|
||||
|
||||
const backgroundStyle = useMemo((): React.CSSProperties | undefined => {
|
||||
if (type === 'action') return undefined;
|
||||
if (type === 'action') {
|
||||
return {
|
||||
background: isHovered ? 'rgba(134, 239, 172, 0.12)' : 'rgba(255, 255, 255, 0.04)',
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'dm') {
|
||||
return {
|
||||
background: HOME_GRADIENT.gradient,
|
||||
...(isHovered ? { boxShadow: `0 0 12px ${HOME_GRADIENT.glow}40` } : {}),
|
||||
};
|
||||
return { background: HOME_GRADIENT.gradient };
|
||||
}
|
||||
|
||||
// Server type — if it has a custom icon image, no gradient needed
|
||||
if (icon) return undefined;
|
||||
|
||||
const serverGrad = getServerGradient(id, name);
|
||||
return {
|
||||
background: serverGrad.gradient,
|
||||
...(isHovered ? { boxShadow: `0 0 12px ${serverGrad.glow}40` } : {}),
|
||||
};
|
||||
}, [type, id, name, icon, active, isHovered]);
|
||||
return { background: serverGrad.gradient };
|
||||
}, [type, id, name, icon, isHovered]);
|
||||
|
||||
const getButtonClasses = () => {
|
||||
const base = 'w-12 h-12 flex items-center justify-center transition-all duration-200 overflow-hidden relative group';
|
||||
const base = 'w-10 h-10 flex items-center justify-center duration-200 overflow-hidden [transition:border-radius_0.2s,background_0.2s,color_0.2s]';
|
||||
|
||||
if (type === 'dm') {
|
||||
return `${base} text-white ${active ? 'rounded-[16px]' : 'rounded-[24px] hover:rounded-[16px]'}`;
|
||||
return `${base} text-white ${active ? 'rounded-[13px]' : 'rounded-[20px] hover:rounded-[13px]'}`;
|
||||
}
|
||||
|
||||
if (type === 'action') {
|
||||
return `${base} bg-surface-chat rounded-[24px] hover:rounded-[16px] text-status-online hover:bg-status-online hover:text-white`;
|
||||
return `${base} rounded-[20px] hover:rounded-[13px] text-accent-mint`;
|
||||
}
|
||||
|
||||
if (icon) {
|
||||
return `${base} ${active ? 'rounded-[16px]' : 'rounded-[24px] hover:rounded-[16px]'}`;
|
||||
return `${base} ${active ? 'rounded-[13px]' : 'rounded-[20px] hover:rounded-[13px]'}`;
|
||||
}
|
||||
|
||||
return `${base} text-white ${active ? 'rounded-[16px]' : 'rounded-[24px] hover:rounded-[16px]'}`;
|
||||
return `${base} text-white ${active ? 'rounded-[13px]' : 'rounded-[20px] hover:rounded-[13px]'}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative flex items-center mb-2 w-full justify-center"
|
||||
className="relative flex items-center mb-1.5 w-full justify-center"
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
{/* Pill Indicator */}
|
||||
{(type === 'server' || type === 'dm') && (
|
||||
<div className="absolute -left-0 w-2 h-12 flex items-center">
|
||||
<div className="absolute -left-0 w-2 h-10 flex items-center">
|
||||
<div
|
||||
className={`bg-white rounded-r-full transition-all duration-200 origin-left ${getPillHeight()} w-1`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Tooltip content={name} position="right">
|
||||
<button onClick={onClick} className={getButtonClasses()} style={backgroundStyle}>
|
||||
{type === 'dm' ? (
|
||||
<span className="text-[20px] font-bold">B</span>
|
||||
) : type === 'action' ? (
|
||||
actionType === 'add' ? (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z" />
|
||||
</svg>
|
||||
) : actionType === 'explore' ? (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zm-3.146-5.351l2.78-1.042 1.042-2.78-2.78 1.042-1.042 2.78zM14.5 7.5l-2.5 5-5 2.5 2.5-5 5-2.5z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z" />
|
||||
</svg>
|
||||
)
|
||||
) : icon ? (
|
||||
<img
|
||||
src={icon.startsWith('http') ? icon : `/api/uploads/${icon}`}
|
||||
alt={name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<button onClick={onClick} className={getButtonClasses()} style={backgroundStyle} title={name}>
|
||||
{type === 'dm' ? (
|
||||
<span className="text-[17px] font-bold">B</span>
|
||||
) : type === 'action' ? (
|
||||
actionType === 'add' ? (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z" />
|
||||
</svg>
|
||||
) : (
|
||||
<span className="text-[16px] font-medium">{firstLetter}</span>
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z" />
|
||||
</svg>
|
||||
)
|
||||
) : icon ? (
|
||||
<img
|
||||
src={icon.startsWith('http') ? icon : `/api/uploads/${icon}`}
|
||||
alt={name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-[15px] font-medium">{firstLetter}</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -167,7 +159,7 @@ export function ServerSidebar() {
|
||||
hasUnread={hasDmUnread}
|
||||
/>
|
||||
|
||||
<div className="w-8 h-[2px] bg-interactive-muted rounded-full mb-2" />
|
||||
<div className="w-8 h-[2px] bg-interactive-muted rounded-full mb-1.5" />
|
||||
|
||||
{servers.map((server) => (
|
||||
<SidebarItem
|
||||
@@ -181,6 +173,8 @@ export function ServerSidebar() {
|
||||
/>
|
||||
))}
|
||||
|
||||
<div className="w-8 h-[2px] bg-interactive-muted rounded-full mb-1.5" />
|
||||
|
||||
<SidebarItem
|
||||
id="add-server"
|
||||
name="Add a Server"
|
||||
@@ -199,14 +193,6 @@ export function ServerSidebar() {
|
||||
actionType="join"
|
||||
/>
|
||||
|
||||
<SidebarItem
|
||||
id="explore"
|
||||
name="Explore Discoverable Servers"
|
||||
active={false}
|
||||
onClick={() => {}}
|
||||
type="action"
|
||||
actionType="explore"
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user