feat: update ui

This commit is contained in:
2026-09-18 07:30:23 +08:00
parent be41a5b188
commit 13989c0702
2 changed files with 67 additions and 39 deletions
+39 -34
View File
@@ -41,6 +41,7 @@ export default function ChatsSection({ selected, onSelect }: Props) {
return items.slice(0, 100).map((i) => ({
...i,
contact: i.accountLid ? contacts.findById(i.accountLid) : null,
from: i.isGroup
? contacts.findById(i.previewMessage?.author || null)
: null,
@@ -81,54 +82,58 @@ export default function ChatsSection({ selected, onSelect }: Props) {
{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" />
<Skeleton 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" />
<Skeleton className="flex-1 h-4 bg-accent mb-2 w-1/2" />
<Skeleton className="flex-1 h-11 bg-accent" />
</div>
</div>
))
: null}
{filteredChats?.map((chat) => (
<button
key={chat.id}
onClick={() => onSelect(chat.id)}
className={`flex w-full items-center gap-3 rounded-xl px-3 py-3 text-left transition cursor-pointer ${selected === chat.id ? "bg-accent" : "hover:bg-muted/70"}`}
>
<Avatar
name={chat.formattedTitle || chat.name}
icon={chat.isGroup ? <UsersIcon size={16} /> : null}
/>
{filteredChats?.map((chat) => {
const title =
chat.contact?.formattedName ||
chat.contact?.name ||
chat.formattedTitle ||
chat.name ||
"No title";
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-2">
<p className="truncate text-sm font-semibold">
{chat.formattedTitle || chat.name}
</p>
<span className="shrink-0 text-[11px] text-muted-foreground">
{dayjs((chat.timestamp || 0) * 1000).fromNow()}
</span>
</div>
<div className="mt-1 flex items-center gap-0.5 text-xs text-muted-foreground">
<ChatPreviewMessage
data={chat.previewMessage}
from={chat.isGroup ? chat.from : null}
/>
return (
<button
key={chat.id}
onClick={() => onSelect(chat.id)}
className={`flex w-full items-center gap-3 rounded-xl px-3 py-3 text-left transition cursor-pointer ${selected === chat.id ? "bg-accent" : "hover:bg-muted/70"}`}
>
<Avatar
name={title}
icon={chat.isGroup ? <UsersIcon size={16} /> : null}
/>
{/* <div className="flex-1" />
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-2">
<p className="truncate text-sm font-semibold">{title}</p>
<span className="shrink-0 text-[11px] text-muted-foreground">
{dayjs((chat.timestamp || 0) * 1000).fromNow()}
</span>
</div>
<div className="mt-1 flex items-center gap-0.5 text-xs text-muted-foreground">
<ChatPreviewMessage
data={chat.previewMessage}
from={chat.isGroup ? chat.from : null}
/>
{/* <div className="flex-1" />
{chat.unreadCount ? (
<span className="grid size-5 shrink-0 place-items-center rounded-full bg-primary text-[10px] font-bold text-primary-foreground">
{chat.unreadCount}
</span>
) : null} */}
</div>
</div>
</div>
</button>
))}
</button>
);
})}
</div>
</>
);
+28 -5
View File
@@ -12,7 +12,7 @@ import {
SendIcon,
XIcon,
} from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import MessageMedia from "@/components/message-media";
@@ -187,9 +187,18 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
className={`mb-3 flex ${item.fromMe ? "justify-end" : "justify-start"}`}
>
<div className="max-w-[82%] sm:max-w-[65%]">
<p className="mb-1 text-xs text-muted-foreground">
<a
href="#"
onClick={(e) => {
e.preventDefault();
router.push(
`/?chatId=${getId(item.author || item.from?.id)}`,
);
}}
className="mb-1 text-xs text-muted-foreground"
>
{getFormattedName(item.from) || item.notifyName}
</p>
</a>
<div
className={`rounded-2xl px-4 py-2.5 text-sm shadow-sm ${item.fromMe ? "rounded-br-md bg-[#d8f5e8] dark:bg-[#26855c] text-foreground" : "rounded-bl-md bg-card text-card-foreground"}`}
>
@@ -290,9 +299,8 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
i % 2 === 0 ? "self-start" : "self-end",
)}
>
<Skeleton key={i} className="w-20 mb-3 h-6 bg-card" />
<Skeleton 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],
@@ -332,6 +340,7 @@ function MessageInputSection({
chatId?: string | null;
refetch: () => void;
}) {
const fileRef = useRef<HTMLInputElement>(null);
const { client, isReady } = useWAClient();
const [message, setMessage] = useState("");
const [files, setFiles] = useState<{ id: string; file: File }[]>([]);
@@ -414,6 +423,19 @@ function MessageInputSection({
return (
<div className="border-t border-border bg-card px-3 py-3 sm:py-4">
<input
type="file"
ref={fileRef}
multiple
className="hidden"
onChange={(e) => {
if (e.target.files?.length) {
onFileAttached([...e.target.files]);
}
e.target.value = "";
}}
/>
{files.length > 0 && (
<div className="flex items-center gap-2 mb-3 overflow-x-auto">
{files.map((i) => (
@@ -429,6 +451,7 @@ function MessageInputSection({
<button
aria-label="Add attachment"
className="grid size-10 shrink-0 place-items-center rounded-full text-muted-foreground hover:bg-muted hover:text-muted-foreground"
onClick={() => fileRef.current?.click()}
>
<PaperclipIcon size={19} />
</button>