feat: add skeleton loader
This commit is contained in:
+17
-1
@@ -1,4 +1,5 @@
|
||||
import Avatar from "@/components/ui/avatar";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useChats } from "@/hooks/useChats";
|
||||
import { useContacts } from "@/hooks/useContact";
|
||||
import { getFormattedName } from "@/lib/utils";
|
||||
@@ -23,7 +24,7 @@ export default function ChatsSection({ selected, onSelect }: Props) {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const contacts = useContacts();
|
||||
const { data: chats } = useChats({
|
||||
const { data: chats, isLoading } = useChats({
|
||||
refetchInterval: 10_000,
|
||||
});
|
||||
|
||||
@@ -77,6 +78,21 @@ export default function ChatsSection({ selected, onSelect }: Props) {
|
||||
</div> */}
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-2 pb-3">
|
||||
{chats == null || isLoading
|
||||
? [...Array(20)].map((_, i) => (
|
||||
<div key={i} className="w-full flex items-center gap-3 px-3 mb-4">
|
||||
<Skeleton key={i} className="size-12 rounded-full bg-accent" />
|
||||
<div className="flex-1">
|
||||
<Skeleton
|
||||
key={i}
|
||||
className="flex-1 h-4 bg-accent mb-2 w-1/2"
|
||||
/>
|
||||
<Skeleton key={i} className="flex-1 h-11 bg-accent" />
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
|
||||
{filteredChats?.map((chat) => (
|
||||
<button
|
||||
key={chat.id}
|
||||
|
||||
+25
-4
@@ -24,6 +24,7 @@ import Avatar from "@/components/ui/avatar";
|
||||
import { useIntersection } from "@/hooks/useIntersection";
|
||||
import Paragraph from "@/components/paragraph";
|
||||
import { nanoid } from "nanoid";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@@ -280,15 +281,35 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
</div>
|
||||
))}
|
||||
|
||||
{messages == null || isLoading || isFetchingNextPage
|
||||
? [...Array(10)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={cn(
|
||||
"mb-4 shrink-0 w-full max-w-[44%]",
|
||||
i % 2 === 0 ? "self-start" : "self-end",
|
||||
)}
|
||||
>
|
||||
<Skeleton key={i} className="w-20 mb-3 h-6 bg-card" />
|
||||
<Skeleton
|
||||
key={i}
|
||||
className={cn(
|
||||
"w-full h-20 bg-card rounded-2xl",
|
||||
["h-20", "h-32", "h-24", "h-40"][i % 4],
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
|
||||
<div ref={endScrollRef} className="h-1" />
|
||||
|
||||
<div className="mb-6 flex items-center justify-center gap-2 text-[11px] text-muted-foreground">
|
||||
<span className="h-px flex-1 bg-border" />
|
||||
<div className="flex items-center gap-2">
|
||||
{isLoading ||
|
||||
(isFetchingNextPage && (
|
||||
<Loader2Icon className="animate-spin size-4" />
|
||||
))}
|
||||
{isLoading || isFetchingNextPage ? (
|
||||
<Loader2Icon className="animate-spin size-4" />
|
||||
) : null}
|
||||
<span>
|
||||
{isLoading || isFetchingNextPage
|
||||
? "Loading..."
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { cn } from "cn";
|
||||
|
||||
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="skeleton"
|
||||
className={cn("animate-pulse rounded-md bg-background/30", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Skeleton };
|
||||
Reference in New Issue
Block a user