feat(dm): display active conversations in direct message sidebar
This commit is contained in:
parent
fb5b007861
commit
f312316400
6 changed files with 210 additions and 86 deletions
|
|
@ -1,50 +1,92 @@
|
||||||
/**
|
/**
|
||||||
* @file app/(app)/layout.tsx
|
* @file app/(app)/layout.tsx
|
||||||
* @description Root application layout wrapping sidebars and main workspace content within the ServerProvider context.
|
* @description Main application layout component that handles user authentication guards, parallel data loading for servers and direct messages, and wraps the app with navigation sidebars and server context.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { auth } from "@/auth";
|
import { auth } from "@/auth";
|
||||||
import { db } from "@/db";
|
import { db } from "@/db";
|
||||||
import { users } from "@/db/schema";
|
import { users, conversations } from "@/db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq, or } from "drizzle-orm";
|
||||||
import { getUserServers } from "@/lib/services/server.service";
|
import { getUserServers } from "@/lib/services/server.service";
|
||||||
import { AppSidebar } from "@/components/layout/AppSidebar";
|
import { AppSidebar } from "@/components/layout/AppSidebar";
|
||||||
import { MemberSidebar } from "@/components/layout/MemberSidebar";
|
import { MemberSidebar } from "@/components/layout/MemberSidebar";
|
||||||
import { ServerProvider } from "@/lib/context/ServerContext";
|
import { ServerProvider } from "@/lib/context/ServerContext";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the primary application layout with authentication checks, database fetching, and sidebar structure.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @function AppLayout
|
||||||
|
* @param {Object} props - The layout properties.
|
||||||
|
* @param {React.ReactNode} props.children - The nested child route content to be rendered within the layout.
|
||||||
|
* @returns {Promise<JSX.Element>} The rendered application layout container.
|
||||||
|
*/
|
||||||
export default async function AppLayout({
|
export default async function AppLayout({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
const currentUserId = session?.user?.id;
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
// Single Auth Guard
|
||||||
|
if (!currentUserId) {
|
||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const [currentUser] = await db
|
// Parallel Loading: User Details, Servers & DM-Conversations
|
||||||
.select({
|
const [[currentUser], userServers, userConversations] = await Promise.all([
|
||||||
id: users.id,
|
db
|
||||||
username: users.username,
|
.select({
|
||||||
color: users.color,
|
id: users.id,
|
||||||
status: users.status,
|
username: users.username,
|
||||||
})
|
color: users.color,
|
||||||
.from(users)
|
status: users.status,
|
||||||
.where(eq(users.id, session.user.id))
|
})
|
||||||
.limit(1);
|
.from(users)
|
||||||
|
.where(eq(users.id, currentUserId))
|
||||||
|
.limit(1),
|
||||||
|
getUserServers(currentUserId),
|
||||||
|
db.query.conversations.findMany({
|
||||||
|
where: or(
|
||||||
|
eq(conversations.userOneId, currentUserId),
|
||||||
|
eq(conversations.userTwoId, currentUserId),
|
||||||
|
),
|
||||||
|
with: {
|
||||||
|
userOne: true,
|
||||||
|
userTwo: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const userServers = await getUserServers(session.user.id);
|
// Transform conversations to isolate the respective conversation partner
|
||||||
|
const formattedConversations = userConversations.map((c) => {
|
||||||
|
const partner = c.userOne.id === currentUserId ? c.userTwo : c.userOne;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: c.id,
|
||||||
|
partner: {
|
||||||
|
id: partner.id,
|
||||||
|
username: partner.username,
|
||||||
|
color: partner.color,
|
||||||
|
status: partner.status,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServerProvider>
|
<ServerProvider>
|
||||||
<div className="flex h-screen w-full overflow-hidden bg-background text-foreground">
|
<div className="flex h-screen w-full overflow-hidden bg-background text-foreground">
|
||||||
<AppSidebar servers={userServers} user={currentUser} />
|
<AppSidebar
|
||||||
|
servers={userServers}
|
||||||
|
conversations={formattedConversations}
|
||||||
|
user={currentUser}
|
||||||
|
/>
|
||||||
<div className="flex-1 flex min-w-0">{children}</div>
|
<div className="flex-1 flex min-w-0">{children}</div>
|
||||||
<MemberSidebar />
|
<MemberSidebar />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,63 @@
|
||||||
/**
|
/**
|
||||||
* @file app/(app)/page.tsx
|
* @file app/(app)/page.tsx
|
||||||
* @description Main application layout page featuring the FriendsView.
|
* @description Main application page component that performs user authentication checks, fetches raw friendship data from the database, and renders the friends management view.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { auth } from "@/auth";
|
import { auth } from "@/auth";
|
||||||
import { db } from "@/db";
|
import { db } from "@/db";
|
||||||
import { friendships } from "@/db/schema";
|
import { friendships } from "@/db/schema";
|
||||||
import { or, eq } from "drizzle-orm";
|
import { or, eq } from "drizzle-orm";
|
||||||
import { AppHeader } from "@/components/layout/AppHeader";
|
import { redirect } from "next/navigation";
|
||||||
import { FriendsView, Friendship } from "@/components/friends/FriendsView";
|
import { FriendsView, type Friendship } from "@/components/friends/FriendsView";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously renders the main application page for authenticated users.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @function AppPage
|
||||||
|
* @returns {Promise<JSX.Element>} The rendered application page container with the FriendsView component.
|
||||||
|
*/
|
||||||
export default async function AppPage() {
|
export default async function AppPage() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
const currentUserId = session?.user?.id;
|
||||||
|
|
||||||
let initialFriendships: Friendship[] = [];
|
// Single Auth Guard
|
||||||
|
if (!currentUserId) {
|
||||||
|
redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
if (session?.user?.id) {
|
// Retrieve all of the user's friendships
|
||||||
const currentUserId = session.user.id;
|
const rawFriendships = await db.query.friendships.findMany({
|
||||||
|
where: or(
|
||||||
const rawFriendships = await db.query.friendships.findMany({
|
eq(friendships.senderId, currentUserId),
|
||||||
where: or(
|
eq(friendships.receiverId, currentUserId),
|
||||||
eq(friendships.senderId, currentUserId),
|
),
|
||||||
eq(friendships.receiverId, currentUserId),
|
with: {
|
||||||
),
|
sender: {
|
||||||
with: {
|
columns: {
|
||||||
sender: {
|
id: true,
|
||||||
columns: {
|
username: true,
|
||||||
id: true,
|
color: true,
|
||||||
username: true,
|
status: true,
|
||||||
color: true,
|
|
||||||
status: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
receiver: {
|
|
||||||
columns: {
|
|
||||||
id: true,
|
|
||||||
username: true,
|
|
||||||
color: true,
|
|
||||||
status: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
receiver: {
|
||||||
|
columns: {
|
||||||
initialFriendships = rawFriendships as Friendship[];
|
id: true,
|
||||||
}
|
username: true,
|
||||||
|
color: true,
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full w-full bg-background p-4">
|
<div className="flex flex-col h-full w-full bg-background p-4">
|
||||||
{session?.user?.id ? (
|
<FriendsView
|
||||||
<FriendsView
|
currentUserId={currentUserId}
|
||||||
currentUserId={session.user.id}
|
initialFriendships={rawFriendships as Friendship[]}
|
||||||
initialFriendships={initialFriendships}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<AppHeader />
|
|
||||||
<div className="flex flex-col items-center justify-center h-full text-center">
|
|
||||||
<h2 className="text-xl font-bold text-white mb-2">Welcome back!</h2>
|
|
||||||
<p className="text-muted max-w-sm">
|
|
||||||
Please sign in to view your friends list or start a conversation.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,16 @@ import { useSidebarStore } from "@/lib/stores/useSidebarStore";
|
||||||
import { ServerSidebar } from "@/components/sidebar/ServerSidebar";
|
import { ServerSidebar } from "@/components/sidebar/ServerSidebar";
|
||||||
import { ChannelSidebar } from "@/components/sidebar/ChannelSidebar";
|
import { ChannelSidebar } from "@/components/sidebar/ChannelSidebar";
|
||||||
import { UserPanel } from "@/components/sidebar/UserPanel";
|
import { UserPanel } from "@/components/sidebar/UserPanel";
|
||||||
import type { ServerWithChannels } from "@/lib/context/ServerContext";
|
import {
|
||||||
|
useActiveServer,
|
||||||
|
type ServerWithChannels,
|
||||||
|
} from "@/lib/context/ServerContext";
|
||||||
import type { UserStatus } from "@/db/schema";
|
import type { UserStatus } from "@/db/schema";
|
||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
|
import {
|
||||||
|
DirectMessageSidebar,
|
||||||
|
SidebarConversation,
|
||||||
|
} from "../sidebar/DirectMessageSidebar";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties representing the user in the sidebar.
|
* Properties representing the user in the sidebar.
|
||||||
|
|
@ -32,10 +39,12 @@ export interface SidebarUser {
|
||||||
*
|
*
|
||||||
* @interface AppSidebarProps
|
* @interface AppSidebarProps
|
||||||
* @property {ServerWithChannels[]} servers - List of available servers including their channels.
|
* @property {ServerWithChannels[]} servers - List of available servers including their channels.
|
||||||
|
* @property {SidebarConversation[]} conversations - List of direct message conversations.
|
||||||
* @property {SidebarUser} user - Information about the currently authenticated user.
|
* @property {SidebarUser} user - Information about the currently authenticated user.
|
||||||
*/
|
*/
|
||||||
interface AppSidebarProps {
|
interface AppSidebarProps {
|
||||||
servers: ServerWithChannels[];
|
servers: ServerWithChannels[];
|
||||||
|
conversations: SidebarConversation[];
|
||||||
user: SidebarUser;
|
user: SidebarUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,10 +52,18 @@ interface AppSidebarProps {
|
||||||
* Renders the responsive application sidebar containing server navigation, channel lists, and user profile.
|
* Renders the responsive application sidebar containing server navigation, channel lists, and user profile.
|
||||||
*
|
*
|
||||||
* @param {AppSidebarProps} props - The component props.
|
* @param {AppSidebarProps} props - The component props.
|
||||||
|
* @param {ServerWithChannels[]} props.servers - List of available servers including their channels.
|
||||||
|
* @param {SidebarConversation[]} props.conversations - List of direct message conversations.
|
||||||
|
* @param {SidebarUser} props.user - Information about the currently authenticated user.
|
||||||
* @returns {JSX.Element} The rendered mobile overlay and responsive sidebar structure.
|
* @returns {JSX.Element} The rendered mobile overlay and responsive sidebar structure.
|
||||||
*/
|
*/
|
||||||
export function AppSidebar({ servers, user }: AppSidebarProps) {
|
export function AppSidebar({
|
||||||
|
servers,
|
||||||
|
user,
|
||||||
|
conversations = [],
|
||||||
|
}: AppSidebarProps) {
|
||||||
const { isNavOpen, closeAll } = useSidebarStore();
|
const { isNavOpen, closeAll } = useSidebarStore();
|
||||||
|
const { activeServer } = useActiveServer();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -59,7 +76,11 @@ export function AppSidebar({ servers, user }: AppSidebarProps) {
|
||||||
>
|
>
|
||||||
<div className="flex flex-1 min-h-0 w-78">
|
<div className="flex flex-1 min-h-0 w-78">
|
||||||
<ServerSidebar servers={servers} />
|
<ServerSidebar servers={servers} />
|
||||||
<ChannelSidebar />
|
{!activeServer ? (
|
||||||
|
<DirectMessageSidebar conversations={conversations} />
|
||||||
|
) : (
|
||||||
|
<ChannelSidebar />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-78">
|
<div className="w-78">
|
||||||
<UserPanel user={user} />
|
<UserPanel user={user} />
|
||||||
|
|
@ -82,7 +103,11 @@ export function AppSidebar({ servers, user }: AppSidebarProps) {
|
||||||
>
|
>
|
||||||
<div className="flex flex-1 min-h-0 w-full">
|
<div className="flex flex-1 min-h-0 w-full">
|
||||||
<ServerSidebar servers={servers} />
|
<ServerSidebar servers={servers} />
|
||||||
<ChannelSidebar />
|
{!activeServer ? (
|
||||||
|
<DirectMessageSidebar conversations={conversations} />
|
||||||
|
) : (
|
||||||
|
<ChannelSidebar />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<UserPanel user={user} />
|
<UserPanel user={user} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useActiveServer } from "@/lib/context/ServerContext";
|
import { useActiveServer } from "@/lib/context/ServerContext";
|
||||||
import { DirectMessageSidebar } from "@/components/sidebar/DirectMessageSidebar";
|
|
||||||
import { useSidebarStore } from "@/lib/stores/useSidebarStore";
|
import { useSidebarStore } from "@/lib/stores/useSidebarStore";
|
||||||
import { CreateChannelModal } from "@/components/modals/CreateChannelModal";
|
import { CreateChannelModal } from "@/components/modals/CreateChannelModal";
|
||||||
import { EditChannelModal } from "@/components/modals/EditChannelModal";
|
import { EditChannelModal } from "@/components/modals/EditChannelModal";
|
||||||
|
|
@ -19,7 +18,7 @@ import type { Channel } from "@/db/schema";
|
||||||
/**
|
/**
|
||||||
* Renders the channel sidebar for the active server with text channel lists, creation triggers, and settings handlers.
|
* Renders the channel sidebar for the active server with text channel lists, creation triggers, and settings handlers.
|
||||||
*
|
*
|
||||||
* @returns {JSX.Element} The rendered channel sidebar container.
|
* @returns {JSX.Element | null} The rendered channel sidebar container or null if no active server exists.
|
||||||
*/
|
*/
|
||||||
export function ChannelSidebar() {
|
export function ChannelSidebar() {
|
||||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||||
|
|
@ -30,6 +29,10 @@ export function ChannelSidebar() {
|
||||||
const { activeServer } = useActiveServer();
|
const { activeServer } = useActiveServer();
|
||||||
const { closeNav, toggleNav } = useSidebarStore();
|
const { closeNav, toggleNav } = useSidebarStore();
|
||||||
|
|
||||||
|
if (!activeServer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles channel selection clicks, automatically closing the mobile navigation drawer on smaller screens.
|
* Handles channel selection clicks, automatically closing the mobile navigation drawer on smaller screens.
|
||||||
*
|
*
|
||||||
|
|
@ -56,10 +59,6 @@ export function ChannelSidebar() {
|
||||||
setEditingChannel(channel);
|
setEditingChannel(channel);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!activeServer) {
|
|
||||||
return <DirectMessageSidebar />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex-1 w-full bg-surface/50 border-r border-background flex flex-col h-full min-w-0 overflow-hidden">
|
<div className="flex-1 w-full bg-surface/50 border-r border-background flex flex-col h-full min-w-0 overflow-hidden">
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,47 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useSidebarStore } from "@/lib/stores/useSidebarStore";
|
import { useSidebarStore } from "@/lib/stores/useSidebarStore";
|
||||||
import { PanelLeftClose } from "lucide-react";
|
import { PanelLeftClose, Users } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
import { clsx } from "clsx";
|
||||||
|
import { UserAvatar } from "@/components/ui/UserAvatar";
|
||||||
|
import { FriendUser } from "../friends/FriendsView";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DirectMessageSidebar component that renders navigation for direct messages and friends.
|
* Represents a direct message conversation in the sidebar.
|
||||||
* Includes a header with collapsible controls and links for viewing chat channels and active conversations.
|
|
||||||
*
|
*
|
||||||
* @returns {JSX.Element} The direct message sidebar component layout.
|
* @interface SidebarConversation
|
||||||
|
* @property {string} id - The unique identifier of the conversation.
|
||||||
|
* @property {FriendUser} partner - The other user participating in the conversation.
|
||||||
*/
|
*/
|
||||||
export function DirectMessageSidebar() {
|
export interface SidebarConversation {
|
||||||
|
id: string;
|
||||||
|
partner: FriendUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties for the DirectMessageSidebar component.
|
||||||
|
*
|
||||||
|
* @interface DirectMessageSidebarProps
|
||||||
|
* @property {SidebarConversation[]} [conversations=[]] - Array of active direct message conversations.
|
||||||
|
*/
|
||||||
|
interface DirectMessageSidebarProps {
|
||||||
|
conversations?: SidebarConversation[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the direct message sidebar with navigation options, friends view link, and a list of active DM chats.
|
||||||
|
*
|
||||||
|
* @param {DirectMessageSidebarProps} props - The component props.
|
||||||
|
* @param {SidebarConversation[]} [props.conversations=[]] - Array of active direct message conversations.
|
||||||
|
* @returns {JSX.Element} The rendered direct message sidebar.
|
||||||
|
*/
|
||||||
|
export function DirectMessageSidebar({
|
||||||
|
conversations = [],
|
||||||
|
}: DirectMessageSidebarProps) {
|
||||||
const { toggleNav } = useSidebarStore();
|
const { toggleNav } = useSidebarStore();
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 w-full md:w-60 bg-surface/50 border-r border-background flex flex-col h-full shrink-0">
|
<div className="flex-1 w-full md:w-60 bg-surface/50 border-r border-background flex flex-col h-full shrink-0">
|
||||||
|
|
@ -38,19 +68,52 @@ export function DirectMessageSidebar() {
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="flex items-center gap-3 px-3 py-2 rounded-md text-sm text-white bg-accent/20 font-medium transition-all"
|
className={clsx(
|
||||||
|
"flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-all",
|
||||||
|
pathname === "/"
|
||||||
|
? "text-white bg-accent/20"
|
||||||
|
: "text-muted hover:text-white hover:bg-surface/50",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<span>Friends</span>
|
<Users className="w-4 h-4" />
|
||||||
|
<span className="font-bold">Friends</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-semibold text-muted px-2 mb-2 uppercase tracking-wider">
|
<div className="text-xs font-semibold text-muted px-2 mb-2 uppercase tracking-wider">
|
||||||
Direct Messages
|
All Messages
|
||||||
</div>
|
|
||||||
<div className="text-sm text-muted px-2 py-1 italic">
|
|
||||||
No active chats
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{conversations.length === 0 ? (
|
||||||
|
<div className="text-sm text-muted px-2 py-1 italic">
|
||||||
|
No active chats
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
{conversations.map((c) => {
|
||||||
|
const isActive = pathname === `/dm/${c.id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={c.id}
|
||||||
|
href={`/dm/${c.id}`}
|
||||||
|
className={clsx(
|
||||||
|
"flex items-center gap-3 px-2 py-1.5 rounded-md text-sm font-medium transition-colors",
|
||||||
|
isActive
|
||||||
|
? "bg-accent/20 text-white"
|
||||||
|
: "text-muted hover:text-white hover:bg-surface/50",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* UserAvatar Integration */}
|
||||||
|
<UserAvatar user={c.partner} size="xs" />
|
||||||
|
|
||||||
|
<span className="truncate">{c.partner.username}</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import { clsx } from "clsx";
|
||||||
*
|
*
|
||||||
* @type AvatarSize
|
* @type AvatarSize
|
||||||
*/
|
*/
|
||||||
type AvatarSize = "sm" | "md";
|
type AvatarSize = "xs" | "sm" | "md";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User object properties required to render the avatar.
|
* User object properties required to render the avatar.
|
||||||
|
|
@ -46,6 +46,7 @@ interface UserAvatarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SIZE_MAP: Record<AvatarSize, { container: string; badge: string }> = {
|
const SIZE_MAP: Record<AvatarSize, { container: string; badge: string }> = {
|
||||||
|
xs: { container: "w-6 h-6 text-xs", badge: "w-1.5 h-1.5" },
|
||||||
sm: { container: "w-8 h-8 text-xs", badge: "w-2.5 h-2.5" },
|
sm: { container: "w-8 h-8 text-xs", badge: "w-2.5 h-2.5" },
|
||||||
md: { container: "w-10 h-10 text-sm", badge: "w-3 h-3" },
|
md: { container: "w-10 h-10 text-sm", badge: "w-3 h-3" },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue