edit sheet delete btn, fix medium icons, 44px touch targets, fix drag leak on drawer close
Some checks failed
CI / Check / Test (push) Failing after 8m44s

This commit is contained in:
2026-07-10 17:46:56 +02:00
parent 0ee2e04fe5
commit 498f3b1818
5 changed files with 48 additions and 14 deletions

View File

@@ -6,7 +6,9 @@ import { Button } from "@/components/ui/button"
import { ReviewFormFields } from "@/components/review-form-fields" import { ReviewFormFields } from "@/components/review-form-fields"
import { SearchOverlay } from "@/components/search-overlay" import { SearchOverlay } from "@/components/search-overlay"
import type { MovieSelection } from "@/components/search-overlay" import type { MovieSelection } from "@/components/search-overlay"
import { useLogReview, useEditReview } from "@/features/diary" import { useLogReview, useEditReview, useDeleteReview } from "@/features/diary"
import { ConfirmDialog } from "@/components/confirm-dialog"
import { Trash2 } from "lucide-react"
import { toast } from "sonner" import { toast } from "sonner"
import { posterUrl } from "@/lib/api/client" import { posterUrl } from "@/lib/api/client"
import { hapticMedium } from "@/lib/haptics" import { hapticMedium } from "@/lib/haptics"
@@ -146,7 +148,9 @@ function EditMode({
const [watchedAt, setWatchedAt] = useState<Date>(() => parseLocalDate(review.watched_at)) const [watchedAt, setWatchedAt] = useState<Date>(() => parseLocalDate(review.watched_at))
const [dateChanged, setDateChanged] = useState(false) const [dateChanged, setDateChanged] = useState(false)
const [watchMedium, setWatchMedium] = useState<string | undefined>(review.watch_medium) const [watchMedium, setWatchMedium] = useState<string | undefined>(review.watch_medium)
const [confirmDelete, setConfirmDelete] = useState(false)
const editMutation = useEditReview() const editMutation = useEditReview()
const deleteMutation = useDeleteReview()
function handleDateChange(d: Date) { function handleDateChange(d: Date) {
setWatchedAt(d) setWatchedAt(d)
@@ -207,6 +211,32 @@ function EditMode({
<Button onClick={handleSubmit} disabled={!rating || editMutation.isPending} className="w-full" size="lg"> <Button onClick={handleSubmit} disabled={!rating || editMutation.isPending} className="w-full" size="lg">
{editMutation.isPending ? t("editReview.saving") : t("editReview.save")} {editMutation.isPending ? t("editReview.saving") : t("editReview.save")}
</Button> </Button>
<Button
variant="ghost"
className="mt-2 w-full text-destructive hover:text-destructive"
onClick={() => setConfirmDelete(true)}
disabled={deleteMutation.isPending}
>
<Trash2 className="mr-1.5 size-4" />
{t("editReview.delete", { defaultValue: "Delete review" })}
</Button>
<ConfirmDialog
open={confirmDelete}
onOpenChange={setConfirmDelete}
title={t("diary.deleteReview", { defaultValue: "Delete review?" })}
description={`${movie.title}${review.watched_at.slice(0, 10)}`}
onConfirm={() =>
deleteMutation.mutate(review.id, {
onSuccess: () => {
hapticMedium()
toast.success(t("editReview.deleted", { defaultValue: "Review deleted", title: movie.title }))
onOpenChange(false)
},
})
}
/>
</div> </div>
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>

View File

@@ -8,21 +8,25 @@ type StarRatingProps = {
size?: "sm" | "md" | "lg" size?: "sm" | "md" | "lg"
} }
const sizes = { sm: "size-5", md: "size-8", lg: "size-10" } const iconSizes = { sm: "size-5", md: "size-7", lg: "size-9" }
const buttonSizes = { sm: "size-8", md: "size-10", lg: "size-11" }
export function StarRating({ value, onChange, size = "lg" }: StarRatingProps) { export function StarRating({ value, onChange, size = "lg" }: StarRatingProps) {
return ( return (
<div className="flex gap-1"> <div className="flex gap-0.5">
{[1, 2, 3, 4, 5].map((star) => ( {[1, 2, 3, 4, 5].map((star) => (
<button <button
key={star} key={star}
type="button" type="button"
onClick={() => { hapticLight(); onChange(star) }} onClick={() => { hapticLight(); onChange(star) }}
className="transition-transform active:scale-90" className={cn(
"flex items-center justify-center rounded-md transition-transform active:scale-90",
buttonSizes[size],
)}
> >
<Star <Star
className={cn( className={cn(
sizes[size], iconSizes[size],
star <= value star <= value
? "fill-amber-500 text-amber-500 aero-star-filled" ? "fill-amber-500 text-amber-500 aero-star-filled"
: "text-muted-foreground/30", : "text-muted-foreground/30",

View File

@@ -41,7 +41,7 @@ export function SwipeToDelete({
} }
} }
}, },
{ axis: "x", filterTaps: true, pointer: { capture: true } }, { axis: "x", filterTaps: true },
) )
function handleDeleteTap() { function handleDeleteTap() {

View File

@@ -17,7 +17,7 @@ export function WatchMediumPicker({ value, onChange }: WatchMediumPickerProps) {
<p className="mb-2 text-xs uppercase tracking-wide text-muted-foreground"> <p className="mb-2 text-xs uppercase tracking-wide text-muted-foreground">
{t("watchMedium.label")} {t("watchMedium.label")}
</p> </p>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-2">
{WATCH_MEDIUMS.map(({ value: val, icon: Icon, labelKey }) => { {WATCH_MEDIUMS.map(({ value: val, icon: Icon, labelKey }) => {
const selected = value === val const selected = value === val
return ( return (
@@ -28,14 +28,14 @@ export function WatchMediumPicker({ value, onChange }: WatchMediumPickerProps) {
variant="outline" variant="outline"
size="icon" size="icon"
className={cn( className={cn(
"size-8", "size-11",
selected && "border-[var(--aero-primary)] bg-[var(--aero-primary)] text-white shadow-[0_0_8px_var(--aero-primary-glow)]", selected && "border-[var(--aero-primary)] bg-[var(--aero-primary)] text-white shadow-[0_0_8px_var(--aero-primary-glow)]",
)} )}
aria-label={t(labelKey)} aria-label={t(labelKey)}
aria-pressed={selected} aria-pressed={selected}
onClick={() => onChange(selected ? undefined : val)} onClick={() => onChange(selected ? undefined : val)}
> >
<Icon className="size-4" /> <Icon className="size-5" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent sideOffset={4}>{t(labelKey)}</TooltipContent> <TooltipContent sideOffset={4}>{t(labelKey)}</TooltipContent>

View File

@@ -1,12 +1,12 @@
import type { LucideIcon } from "lucide-react" import type { LucideIcon } from "lucide-react"
import { import {
Cast,
Clapperboard, Clapperboard,
Tv,
Radio,
Disc3, Disc3,
Download, Download,
Server,
Ellipsis, Ellipsis,
Server,
Tv,
} from "lucide-react" } from "lucide-react"
export type WatchMediumDef = { export type WatchMediumDef = {
@@ -17,8 +17,8 @@ export type WatchMediumDef = {
export const WATCH_MEDIUMS: WatchMediumDef[] = [ export const WATCH_MEDIUMS: WatchMediumDef[] = [
{ value: "cinema", icon: Clapperboard, labelKey: "watchMedium.cinema" }, { value: "cinema", icon: Clapperboard, labelKey: "watchMedium.cinema" },
{ value: "streaming", icon: Tv, labelKey: "watchMedium.streaming" }, { value: "streaming", icon: Cast, labelKey: "watchMedium.streaming" },
{ value: "tv", icon: Radio, labelKey: "watchMedium.tv" }, { value: "tv", icon: Tv, labelKey: "watchMedium.tv" },
{ value: "physical_media", icon: Disc3, labelKey: "watchMedium.physicalMedia" }, { value: "physical_media", icon: Disc3, labelKey: "watchMedium.physicalMedia" },
{ value: "download", icon: Download, labelKey: "watchMedium.download" }, { value: "download", icon: Download, labelKey: "watchMedium.download" },
{ value: "media_server", icon: Server, labelKey: "watchMedium.mediaServer" }, { value: "media_server", icon: Server, labelKey: "watchMedium.mediaServer" },