import { createFileRoute } from "@tanstack/react-router"
import { useCallback, useState } from "react"
import { useTranslation } from "react-i18next"
import { Clapperboard, Film, Inbox, Plus, RefreshCw } from "lucide-react"
import { ReviewCard } from "@/components/review-card"
import { MovieCard } from "@/components/movie-card"
import { EmptyState } from "@/components/empty-state"
import { SwipeTabs } from "@/components/swipe-tabs"
import { SwipeToDelete } from "@/components/swipe-to-delete"
import { VirtualList } from "@/components/virtual-list"
import { Button } from "@/components/ui/button"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Skeleton } from "@/components/ui/skeleton"
import { StarRating } from "@/components/star-rating"
import { useAuth } from "@/components/auth-provider"
import { useQueryClient } from "@tanstack/react-query"
import { useInfiniteActivityFeed, useDeleteReview } from "@/hooks/use-diary"
import { SearchOverlay } from "@/components/search-overlay"
import type { MovieSelection } from "@/components/search-overlay"
import { useInfiniteWatchlist, useAddToWatchlist, useRemoveFromWatchlist } from "@/hooks/use-watchlist"
import { useWatchQueue, useConfirmWatch, useDismissWatch } from "@/hooks/use-webhooks"
export const Route = createFileRoute("/_app/")({
component: HomePage,
})
function HomePage() {
const { t } = useTranslation()
const homeTabs = [
{ value: "feed", label: t("feed.tab") },
{ value: "watchlist", label: t("feed.watchlist") },
{ value: "queue", label: t("feed.queue") },
] as const
return (
{t("feed.title")}
{(tab) => (
<>
{tab === "feed" && }
{tab === "watchlist" && }
{tab === "queue" && }
>
)}
)
}
function FeedTab() {
const { t } = useTranslation()
const { auth } = useAuth()
const qc = useQueryClient()
const [refreshing, setRefreshing] = useState(false)
const [sortBy, setSortBy] = useState("date")
const feedSortOptions = [
{ value: "date", label: t("feed.sortLatest") },
{ value: "date_asc", label: t("feed.sortOldest") },
{ value: "rating", label: t("feed.sortTopRated") },
{ value: "rating_asc", label: t("feed.sortLowestRated") },
] as const
const { data, isPending, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteActivityFeed({ sort_by: sortBy })
const deleteReview = useDeleteReview()
const items = data?.pages.flatMap((p) => p.items) ?? []
const loadMore = useCallback(() => fetchNextPage(), [fetchNextPage])
return (
{isPending &&
}
{!isPending && !items.length && (
)}
{items.length > 0 && (
{
const card = (
)
return entry.user_id === auth?.user_id ? (
deleteReview.mutate(entry.review.id)}
confirmTitle={t("feed.deleteReview")}
confirmDescription={entry.movie.title}
>
{card}
) : (
card
)
}}
/>
)}
)
}
function WatchlistTab() {
const { t } = useTranslation()
const { data, isPending, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteWatchlist()
const items = data?.pages.flatMap((p) => p.items) ?? []
const addMutation = useAddToWatchlist()
const removeMutation = useRemoveFromWatchlist()
const loadMore = useCallback(() => fetchNextPage(), [fetchNextPage])
const [searchOpen, setSearchOpen] = useState(false)
function handleAdd(movie: MovieSelection) {
setSearchOpen(false)
addMutation.mutate(
movie.id
? { movie_id: movie.id }
: {
external_metadata_id: movie.external_metadata_id,
manual_title: movie.title,
manual_release_year: movie.release_year,
},
)
}
return (
{searchOpen && (
setSearchOpen(false)} onSelect={handleAdd} />
)}
{isPending && }
{!isPending && !items.length && (
)}
{items.length > 0 && (
(
removeMutation.mutate(entry.movie.id)}
confirmTitle={t("feed.removeFromWatchlist")}
confirmDescription={entry.movie.title}
>
)}
/>
)}
)
}
function QueueTab() {
const { t } = useTranslation()
const { data, isPending } = useWatchQueue()
const confirmMutation = useConfirmWatch()
const dismissMutation = useDismissWatch()
const [ratings, setRatings] = useState>({})
if (isPending) return
if (!data?.length)
return
return (
{data.map((entry) => (
{entry.title}
{entry.year && `${entry.year} · `}{entry.source} · {entry.watched_at}
setRatings((p) => ({ ...p, [entry.id]: v }))}
size="sm"
/>
))}
)
}
function FeedSkeleton() {
return (
{[1, 2, 3].map((i) => (
))}
)
}