227 lines
7.1 KiB
TypeScript
227 lines
7.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useTransition } from "react";
|
|
import { signIn } from "next-auth/react";
|
|
import { useRouter } from "next/navigation";
|
|
import { Clipboard, Link2, LogIn, MessageSquare, Trash2 } from "lucide-react";
|
|
import { toast } from "sonner";
|
|
import {
|
|
createCommentAction,
|
|
deleteCommentAction,
|
|
deleteShareAction,
|
|
} from "@/app/actions/share";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from "@/components/ui/alert-dialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Field,
|
|
FieldDescription,
|
|
FieldError,
|
|
FieldLabel,
|
|
} from "@/components/ui/field";
|
|
import { Spinner } from "@/components/ui/spinner";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { MAX_SHARE_COMMENT_LENGTH } from "@/lib/share/limits";
|
|
import { getShareContentForClipboard } from "@/lib/share/copy";
|
|
|
|
export function CopyShareActions() {
|
|
async function copyText() {
|
|
const content = getShareContentForClipboard((id) =>
|
|
document.getElementById(id),
|
|
);
|
|
if (content === null) return;
|
|
await navigator.clipboard.writeText(content);
|
|
toast.success("คัดลอกข้อความแล้ว");
|
|
}
|
|
|
|
async function copyLink() {
|
|
await navigator.clipboard.writeText(window.location.href);
|
|
toast.success("คัดลอกลิงก์แล้ว");
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<Button type="button" size="sm" onClick={copyText}>
|
|
<Clipboard data-icon="inline-start" />
|
|
คัดลอกข้อความ
|
|
</Button>
|
|
<Button type="button" variant="outline" size="sm" onClick={copyLink}>
|
|
<Link2 data-icon="inline-start" />
|
|
คัดลอกลิงก์
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function CommentComposer({
|
|
shareId,
|
|
authenticated,
|
|
}: {
|
|
shareId: string;
|
|
authenticated: boolean;
|
|
}) {
|
|
const router = useRouter();
|
|
const [body, setBody] = useState("");
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [pending, startTransition] = useTransition();
|
|
|
|
if (!authenticated) {
|
|
return (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => signIn("discord", { callbackUrl: window.location.href })}
|
|
>
|
|
<LogIn data-icon="inline-start" />
|
|
เข้าสู่ระบบเพื่อแสดงความคิดเห็น
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<form
|
|
onSubmit={(event) => {
|
|
event.preventDefault();
|
|
setError(null);
|
|
startTransition(async () => {
|
|
const result = await createCommentAction(shareId, body);
|
|
if (!result.success) {
|
|
setError(result.error);
|
|
return;
|
|
}
|
|
setBody("");
|
|
toast.success("เพิ่มความคิดเห็นแล้ว");
|
|
router.refresh();
|
|
});
|
|
}}
|
|
>
|
|
<Field data-invalid={Boolean(error)}>
|
|
<FieldLabel htmlFor="comment-body">แสดงความคิดเห็น</FieldLabel>
|
|
<Textarea
|
|
id="comment-body"
|
|
value={body}
|
|
onChange={(event) => setBody(event.target.value)}
|
|
maxLength={MAX_SHARE_COMMENT_LENGTH}
|
|
aria-invalid={Boolean(error)}
|
|
placeholder="เขียนความคิดเห็นของคุณ…"
|
|
className="min-h-24 resize-y"
|
|
/>
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<FieldDescription>
|
|
{body.length.toLocaleString("th-TH")} / 2,000
|
|
</FieldDescription>
|
|
<Button type="submit" size="sm" disabled={pending || !body.trim()}>
|
|
{pending ? <Spinner data-icon="inline-start" /> : <MessageSquare data-icon="inline-start" />}
|
|
ส่งความคิดเห็น
|
|
</Button>
|
|
</div>
|
|
{error && <FieldError>{error}</FieldError>}
|
|
</Field>
|
|
</form>
|
|
);
|
|
}
|
|
|
|
export function DeleteShareButton({ shareId }: { shareId: string }) {
|
|
const router = useRouter();
|
|
const [pending, setPending] = useState(false);
|
|
|
|
async function remove() {
|
|
setPending(true);
|
|
const result = await deleteShareAction(shareId);
|
|
if (!result.success) {
|
|
setPending(false);
|
|
toast.error(result.error);
|
|
return;
|
|
}
|
|
toast.success("ลบข้อความแชร์แล้ว");
|
|
router.push("/share");
|
|
}
|
|
|
|
return (
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<Button variant="destructive" size="sm" disabled={pending}>
|
|
{pending ? <Spinner data-icon="inline-start" /> : <Trash2 data-icon="inline-start" />}
|
|
ลบข้อความนี้
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>ลบข้อความแชร์นี้หรือไม่</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
ข้อความ รูปประกอบ และความคิดเห็นทั้งหมดจะถูกลบถาวร
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>ยกเลิก</AlertDialogCancel>
|
|
<AlertDialogAction variant="destructive" onClick={remove}>
|
|
ลบถาวร
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
);
|
|
}
|
|
|
|
export function DeleteCommentButton({
|
|
shareId,
|
|
commentId,
|
|
}: {
|
|
shareId: string;
|
|
commentId: string;
|
|
}) {
|
|
const router = useRouter();
|
|
const [pending, setPending] = useState(false);
|
|
|
|
async function remove() {
|
|
setPending(true);
|
|
const result = await deleteCommentAction(shareId, commentId);
|
|
setPending(false);
|
|
if (!result.success) {
|
|
toast.error(result.error);
|
|
return;
|
|
}
|
|
toast.success("ลบความคิดเห็นแล้ว");
|
|
router.refresh();
|
|
}
|
|
|
|
return (
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
disabled={pending}
|
|
aria-label="ลบความคิดเห็น"
|
|
>
|
|
{pending ? <Spinner /> : <Trash2 />}
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>ลบความคิดเห็นนี้หรือไม่</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
การลบความคิดเห็นไม่สามารถย้อนกลับได้
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>ยกเลิก</AlertDialogCancel>
|
|
<AlertDialogAction variant="destructive" onClick={remove}>
|
|
ลบความคิดเห็น
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
);
|
|
}
|