feat: add remove message feature
This commit is contained in:
+67
-10
@@ -6,10 +6,12 @@ import {
|
||||
ArrowLeftIcon,
|
||||
CheckCheckIcon,
|
||||
CheckIcon,
|
||||
ChevronDown,
|
||||
Loader2Icon,
|
||||
PaperclipIcon,
|
||||
PhoneCallIcon,
|
||||
SendIcon,
|
||||
Trash2Icon,
|
||||
XIcon,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
@@ -26,6 +28,13 @@ import Paragraph from "@/components/paragraph";
|
||||
import { nanoid } from "nanoid";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import SearchInput from "@/components/search-input";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@@ -34,20 +43,17 @@ type Props = {
|
||||
maxCount?: number;
|
||||
};
|
||||
|
||||
export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
export default function MessagesSection({ chatId, maxCount = 20 }: Props) {
|
||||
const { client, isReady } = useWAClient();
|
||||
const router = useHashRouter();
|
||||
const msgContainerRef = useRef<HTMLDivElement>(null!);
|
||||
const lastFetch = useRef<number>(0);
|
||||
|
||||
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,
|
||||
isLoading,
|
||||
@@ -76,6 +82,10 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
select: (data) => data.pages.flat(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (chatId) msgContainerRef.current.scrollTop = 0;
|
||||
}, [chatId]);
|
||||
|
||||
useMessageEvent((data) => {
|
||||
if (data.chatId !== chatId) return;
|
||||
|
||||
@@ -90,9 +100,15 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
});
|
||||
|
||||
const endScrollRef = useIntersection(() => {
|
||||
if (hasNextPage && !isFetchingNextPage && !isLoading) {
|
||||
fetchNextPage();
|
||||
}
|
||||
if (!hasNextPage || isFetchingNextPage || isLoading) return;
|
||||
|
||||
const t = Date.now() - lastFetch.current;
|
||||
if (t < 1000) return;
|
||||
|
||||
fetchNextPage().then(() => {
|
||||
lastFetch.current = Date.now();
|
||||
msgContainerRef.current.scrollTop -= 10;
|
||||
});
|
||||
});
|
||||
|
||||
const chat = useMemo(
|
||||
@@ -147,6 +163,18 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
});
|
||||
}, [messages, contacts, search]);
|
||||
|
||||
async function onDeleteMsg(id: string) {
|
||||
if (!window.confirm("Delete this message?")) return;
|
||||
try {
|
||||
await client.exec(
|
||||
`return WPP.chat.deleteMessage('${chatId}', '${id}', true, true);`,
|
||||
);
|
||||
refetch();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
function getMentionLabel(username: string) {
|
||||
const data = contacts?.find((c) => (c.id as any).user === username);
|
||||
return data?.notifyName || data?.formattedName || data?.name || username;
|
||||
@@ -232,7 +260,10 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
<div
|
||||
id={item.id.replace(/^(?:false_|true_)/, "")}
|
||||
key={`${item.timestamp}-${index}`}
|
||||
className={`mb-3 flex ${item.fromMe ? "justify-end" : "justify-start"}`}
|
||||
className={cn(
|
||||
"mb-3 flex",
|
||||
item.fromMe ? "justify-end" : "justify-start",
|
||||
)}
|
||||
>
|
||||
<div className="max-w-[82%] sm:max-w-[65%]">
|
||||
<a
|
||||
@@ -248,8 +279,33 @@ export default function MessagesSection({ chatId, maxCount = 50 }: Props) {
|
||||
{getFormattedName(item.from) || item.notifyName}
|
||||
</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"}`}
|
||||
className={cn(
|
||||
"rounded-2xl px-4 py-2.5 text-sm shadow-sm relative group",
|
||||
item.fromMe
|
||||
? "rounded-br-md bg-[#d8f5e8] dark:bg-[#26855c] text-foreground"
|
||||
: "rounded-bl-md bg-card text-card-foreground",
|
||||
)}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
variant="default"
|
||||
size="icon-sm"
|
||||
className="absolute top-1 right-1 z-1 rounded-full opacity-0 group-hover:opacity-100"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ChevronDown />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => onDeleteMsg(item.id)}>
|
||||
<Trash2Icon />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{item.isForwarded ? (
|
||||
<p className="text-xs italic mb-1 text-primary/80">
|
||||
Forwarded
|
||||
@@ -522,6 +578,7 @@ function MessageInputSection({
|
||||
value={message}
|
||||
onChange={(event) => setMessage(event.target.value)}
|
||||
onPaste={onPaste}
|
||||
enterKeyHint="enter"
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
event.key === "Enter" &&
|
||||
|
||||
Reference in New Issue
Block a user