feat: add cache, fix ui

This commit is contained in:
2026-09-18 10:45:11 +08:00
parent 7b0de7056d
commit edf28006b2
10 changed files with 372 additions and 63 deletions
+61 -7
View File
@@ -25,6 +25,7 @@ import { useIntersection } from "@/hooks/useIntersection";
import Paragraph from "@/components/paragraph";
import { nanoid } from "nanoid";
import { Skeleton } from "@/components/ui/skeleton";
import SearchInput from "@/components/search-input";
dayjs.extend(relativeTime);
@@ -36,10 +37,16 @@ type Props = {
export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
const { client, isReady } = useWAClient();
const router = useHashRouter();
const msgContainerRef = useRef<HTMLDivElement>(null!);
const queryClient = useQueryClient();
const { data: chats } = useChats();
const { data: contacts } = useContacts();
const [search, setSearch] = useState("");
useEffect(() => {
if (chatId) msgContainerRef.current.scrollTop = 0;
}, [chatId]);
const {
data: messages,
@@ -94,8 +101,26 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
);
const messageList = useMemo(() => {
const q = search.trim().toLowerCase();
return messages
?.filter((i) => !["revoked"].includes(i.type as never))
?.filter((i) => {
if (["revoked"].includes(i.type as never)) {
return false;
}
if (q) {
const body = i.caption || (i.type === "chat" ? i.body : "") || "";
const name = getFormattedName(i.from) || "";
return (
body.toLowerCase().includes(q) ||
name.toLowerCase().includes(q) ||
i.filename?.toLowerCase().includes(q)
);
}
return true;
})
?.map((i) => {
const from = contacts?.find(
(c) => getId(c.id) === getId(i.author || i.from),
@@ -120,14 +145,26 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
quotedMsg,
};
});
}, [messages, contacts]);
}, [messages, contacts, search]);
function getMentionLabel(username: string) {
const data = contacts?.find((c) => (c.id as any).user === username);
return data?.notifyName || data?.formattedName || data?.name || username;
}
function onMentionClick(username: string) {
const data = contacts?.find((c) => (c.id as any).user === username);
if (data) {
router.push(`/?chatId=${getId(data.id)}`);
}
}
return (
<section
className={`flex min-w-0 h-full flex-1 flex-col bg-muted ${chatId ? "flex" : "hidden md:flex"}`}
>
<header className="flex items-center justify-between border-b border-border bg-card px-4 py-3 sm:px-6">
<div className="flex min-w-0 items-center gap-3">
<div className="flex min-w-0 items-center gap-3 w-full">
<button
aria-label="Back to conversations"
onClick={() => router.goBack()}
@@ -141,7 +178,7 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
className="size-10"
/>
<div className="min-w-0">
<div className="min-w-0 flex-1 truncate">
<h2 className="truncate text-sm font-semibold">
{chat?.formattedTitle || chat?.name}
</h2>
@@ -150,6 +187,14 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
{selected.online ? "Online now" : "Active recently"}
</p> */}
</div>
<SearchInput
className="w-full max-w-40 md:max-w-sm"
inputClassName="text-xs placeholder:text-xs"
placeholder="Search..."
defaultValue={search}
onValueChange={setSearch}
/>
</div>
{/* <div className="flex items-center gap-1 text-muted-foreground">
<button
@@ -173,7 +218,10 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
</div> */}
</header>
<div className="message-pattern flex-1 px-4 py-6 sm:px-8 flex flex-col-reverse overflow-y-auto">
<div
ref={msgContainerRef}
className="message-pattern flex-1 px-4 py-6 sm:px-8 flex flex-col-reverse overflow-y-auto"
>
{/* <div className="mb-6 flex items-center justify-center gap-2 text-[11px] text-muted-foreground">
<span className="h-px flex-1 bg-border" />
<span>Today</span>
@@ -245,6 +293,8 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
? item.quotedMsg.body
: "Quoted")
}
getMentionLabel={getMentionLabel}
onMentionClick={onMentionClick}
/>
</button>
) : null}
@@ -257,7 +307,11 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
) : null}
{item.type === "chat" || item.caption ? (
<Paragraph text={item.caption || item.body} />
<Paragraph
text={item.caption || item.body}
getMentionLabel={getMentionLabel}
onMentionClick={onMentionClick}
/>
) : null}
{item.type === "call_log" && (
@@ -310,7 +364,7 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
))
: null}
<div ref={endScrollRef} className="h-1" />
{messages != 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" />