|
|
@@ -0,0 +1,91 @@
|
|
|
+"use client";
|
|
|
+
|
|
|
+import { useCallback, useMemo, useState } from "react";
|
|
|
+import { SearchBar } from "./search-bar";
|
|
|
+import { SearchResults } from "./search-results";
|
|
|
+import { PosterGrid } from "./poster-grid";
|
|
|
+import { useGroupMovies } from "@/hooks/use-group-movies";
|
|
|
+import { useMovieSearch } from "@/hooks/use-movie-search";
|
|
|
+import { useAddMovie } from "@/hooks/use-add-movie";
|
|
|
+import { useRealtimeMovies } from "@/hooks/use-realtime-movies";
|
|
|
+import type { Database } from "@/types/database";
|
|
|
+
|
|
|
+type MovieRow = Database["public"]["Tables"]["movies"]["Row"];
|
|
|
+
|
|
|
+interface MemberInfo {
|
|
|
+ id: string;
|
|
|
+ displayName: string;
|
|
|
+ avatarColor: string | null;
|
|
|
+}
|
|
|
+
|
|
|
+interface MovieListClientProps {
|
|
|
+ groupId: string;
|
|
|
+ groupName: string;
|
|
|
+ members: MemberInfo[];
|
|
|
+}
|
|
|
+
|
|
|
+export function MovieListClient({ groupId, members }: MovieListClientProps) {
|
|
|
+ const [searchQuery, setSearchQuery] = useState("");
|
|
|
+ const [addingTmdbId, setAddingTmdbId] = useState<number | null>(null);
|
|
|
+
|
|
|
+ const { data } = useGroupMovies(groupId);
|
|
|
+ const allMovies = useMemo(() => data?.pages.flat() ?? [], [data]);
|
|
|
+
|
|
|
+ const { data: searchData, isLoading: isSearchLoading } = useMovieSearch(searchQuery);
|
|
|
+ const tmdbResults = searchData?.results ?? [];
|
|
|
+
|
|
|
+ const addMovie = useAddMovie();
|
|
|
+
|
|
|
+ useRealtimeMovies(groupId);
|
|
|
+
|
|
|
+ const users = useMemo(
|
|
|
+ () => members.map((m) => ({ id: m.id, avatar_color: m.avatarColor })),
|
|
|
+ [members],
|
|
|
+ );
|
|
|
+
|
|
|
+ // TODO: Wire selectedMovieId, genre filter, userNames when Unit 3 adds optional PosterGrid props
|
|
|
+ const handleSelect = useCallback((_movie: MovieRow) => {
|
|
|
+ // Will toggle selectedMovieId once PosterGrid accepts it
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const handleSearch = useCallback((query: string) => {
|
|
|
+ setSearchQuery(query);
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const handleAdd = useCallback(
|
|
|
+ (tmdbId: number) => {
|
|
|
+ setAddingTmdbId(tmdbId);
|
|
|
+ addMovie.mutate(
|
|
|
+ { tmdb_id: tmdbId, group_id: groupId },
|
|
|
+ {
|
|
|
+ onSettled: () => setAddingTmdbId(null),
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ [addMovie, groupId],
|
|
|
+ );
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="space-y-6">
|
|
|
+ <div>
|
|
|
+ <SearchBar onSearch={handleSearch} isLoading={isSearchLoading} />
|
|
|
+ {searchQuery.length >= 2 && (
|
|
|
+ <div className="mt-2 rounded-lg border border-white/10 bg-white/5 p-3">
|
|
|
+ <SearchResults
|
|
|
+ tmdbResults={tmdbResults}
|
|
|
+ groupMovies={allMovies}
|
|
|
+ query={searchQuery}
|
|
|
+ isAdding={addMovie.isPending}
|
|
|
+ addingTmdbId={addingTmdbId}
|
|
|
+ onAdd={handleAdd}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <section aria-label="Movie list" aria-live="polite">
|
|
|
+ <PosterGrid groupId={groupId} users={users} onSelect={handleSelect} />
|
|
|
+ </section>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|