feat: add WatchMedium field, general review editing, configurable deploy
Some checks failed
CI / Check / Test (push) Failing after 27m0s

- WatchMedium enum (cinema/streaming/tv/physical_media/download/media_server/other)
- PATCH /api/v1/reviews/:id partial update (rating, comment, watched_at, watch_medium)
- edit_review use case w/ ownership + remote review guard, best-effort AP Update broadcast
- SPA: icon picker, edit sheet (long-press mobile / pencil desktop), watch medium badge
- shared ReviewFormFields, EditableContextMenu, parse_watched_at/format_watched_at
- deploy.sh parameterized (--features, --tag), CORS allows PATCH
- CONTEXT.md glossary, ADR-0001 general review editing
This commit is contained in:
2026-07-10 00:01:14 +02:00
parent 9794babe06
commit 29cc68b07c
72 changed files with 1298 additions and 124 deletions

View File

@@ -15,7 +15,9 @@ import { Textarea } from "@/components/ui/textarea"
import { StarRating } from "@/components/star-rating"
import { useAuth } from "@/components/auth-provider"
import { useQueryClient } from "@tanstack/react-query"
import { EditReviewSheet } from "@/components/edit-review-sheet"
import { useInfiniteActivityFeed, useDeleteReview } from "@/hooks/use-diary"
import type { FeedEntryDto } from "@/lib/api/diary"
import { SearchOverlay } from "@/components/search-overlay"
import type { MovieSelection } from "@/components/search-overlay"
import { useInfiniteWatchlist, useAddToWatchlist, useRemoveFromWatchlist } from "@/hooks/use-watchlist"
@@ -66,6 +68,7 @@ function FeedTab() {
const { data, isPending, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteActivityFeed({ sort_by: sortBy })
const deleteReview = useDeleteReview()
const [editingEntry, setEditingEntry] = useState<FeedEntryDto | null>(null)
const items = data?.pages.flatMap((p) => p.items) ?? []
const loadMore = useCallback(() => fetchNextPage(), [fetchNextPage])
@@ -110,6 +113,7 @@ function FeedTab() {
isFetching={isFetchingNextPage}
onLoadMore={loadMore}
renderItem={(entry) => {
const isOwn = entry.user_id === auth?.user_id
const card = (
<ReviewCard
movie={entry.movie}
@@ -118,9 +122,10 @@ function FeedTab() {
userId={entry.user_id}
isFederated={entry.is_federated}
actorUrl={entry.actor_url}
onEdit={isOwn ? () => setEditingEntry(entry) : undefined}
/>
)
return entry.user_id === auth?.user_id ? (
return isOwn ? (
<SwipeToDelete
onDelete={() => deleteReview.mutate(entry.review.id)}
confirmTitle={t("feed.deleteReview")}
@@ -134,6 +139,16 @@ function FeedTab() {
}}
/>
)}
{editingEntry && (
<EditReviewSheet
key={editingEntry.review.id}
open={!!editingEntry}
onOpenChange={(open) => !open && setEditingEntry(null)}
movie={editingEntry.movie}
review={editingEntry.review}
/>
)}
</div>
)
}