import { useState } from "react" import { useTranslation } from "react-i18next" import { VisuallyHidden } from "radix-ui" import { Drawer, DrawerContent, DrawerTitle } from "@/components/ui/drawer" import { Button } from "@/components/ui/button" import { ReviewFormFields } from "@/components/review-form-fields" import { SearchOverlay } from "@/components/search-overlay" import type { MovieSelection } from "@/components/search-overlay" import { useLogReview, useEditReview, useDeleteReview } from "@/features/diary" import { ConfirmDialog } from "@/components/confirm-dialog" import { Trash2 } from "lucide-react" import { toast } from "sonner" import { posterUrl } from "@/lib/api/client" import { hapticMedium } from "@/lib/haptics" import { formatWatchedAt, isDateOnly, parseWatchedAt } from "@/lib/date" import type { EditReviewRequest } from "@/features/diary" import type { MovieDto, ReviewDto } from "@/lib/api/common" type LogMode = { mode: "log" } type EditMode = { mode: "edit" movie: MovieDto review: ReviewDto } type ReviewSheetProps = { open: boolean onOpenChange: (open: boolean) => void } & (LogMode | EditMode) export function ReviewSheet(props: ReviewSheetProps) { if (props.mode === "log") { return } return ( ) } function LogMode({ open, onOpenChange }: { open: boolean; onOpenChange: (open: boolean) => void }) { const { t } = useTranslation() const [movie, setMovie] = useState(null) const [rating, setRating] = useState(0) const [comment, setComment] = useState("") const [watchedAt, setWatchedAt] = useState(new Date()) const [watchMedium, setWatchMedium] = useState() const [spoilers, setSpoilers] = useState(false) const logMutation = useLogReview() function reset() { setMovie(null) setRating(0) setComment("") setWatchedAt(new Date()) setWatchMedium(undefined) setSpoilers(false) } function handleClose() { onOpenChange(false) reset() } function handleSubmit() { if (!movie || !rating) return logMutation.mutate( { external_metadata_id: movie.external_metadata_id, manual_title: movie.title, manual_release_year: movie.release_year, manual_director: movie.director, rating, comment: comment || undefined, watched_at: formatWatchedAt(watchedAt), watch_medium: watchMedium, spoilers: spoilers || undefined, }, { onSuccess: () => { hapticMedium() toast.success(t("logReview.logged", { title: movie.title })) handleClose() }, }, ) } if (open && !movie) { return setMovie(m)} /> } return ( !o && handleClose()}> {t("logReview.title")} {movie && ( <> {logMutation.isPending ? t("logReview.logging") : t("logReview.logReview")} > )} ) } function EditMode({ open, onOpenChange, movie, review, }: { open: boolean onOpenChange: (open: boolean) => void movie: MovieDto review: ReviewDto }) { const { t } = useTranslation() const [rating, setRating] = useState(review.rating) const [comment, setComment] = useState(review.comment ?? "") const [watchedAt, setWatchedAt] = useState(() => parseWatchedAt(review.watched_at)) const [dateChanged, setDateChanged] = useState(false) const [watchMedium, setWatchMedium] = useState(review.watch_medium) const [spoilers, setSpoilers] = useState(review.spoilers ?? false) const [confirmDelete, setConfirmDelete] = useState(false) const editMutation = useEditReview() const deleteMutation = useDeleteReview() function handleDateChange(d: Date) { setWatchedAt(d) setDateChanged(true) } function handleSubmit() { if (!rating) return const data: Partial = {} if (rating !== review.rating) data.rating = rating const newComment = comment || null if (newComment !== (review.comment ?? null)) data.comment = newComment if (dateChanged) data.watched_at = formatWatchedAt(watchedAt, isDateOnly(review.watched_at)) if (watchMedium !== review.watch_medium) data.watch_medium = watchMedium ?? null if (spoilers !== (review.spoilers ?? false)) data.spoilers = spoilers if (Object.keys(data).length === 0) { toast.info(t("editReview.noChanges")) onOpenChange(false) return } editMutation.mutate( { id: review.id, data }, { onSuccess: () => { hapticMedium() toast.success(t("editReview.saved", { title: movie.title })) onOpenChange(false) }, }, ) } return ( {t("editReview.title")} {editMutation.isPending ? t("editReview.saving") : t("editReview.save")} setConfirmDelete(true)} disabled={deleteMutation.isPending} > {t("editReview.delete", { defaultValue: "Delete review" })} deleteMutation.mutate(review.id, { onSuccess: () => { hapticMedium() toast.success(t("editReview.deleted", { defaultValue: "Review deleted", title: movie.title })) onOpenChange(false) }, }) } /> ) } function MovieHeader({ title, releaseYear, director, posterPath, genres, }: { title: string releaseYear?: number director?: string | null posterPath?: string | null genres?: string[] }) { return ( {posterPath && } {title} {releaseYear}{director && ` · ${director}`} {genres && genres.length > 0 && {genres.join(", ")}} ) }
{title}
{releaseYear}{director && ` · ${director}`}
{genres.join(", ")}