Bruno Predot

Gestion des recherches de film vie le composable et implémentation dans le composant MoviesList.

@@ -10,7 +10,7 @@ import { useDateFormat } from "@vueuse/core"; @@ -10,7 +10,7 @@ import { useDateFormat } from "@vueuse/core";
10 //#endregion 10 //#endregion
11 11
12 //#region --Declaration--. 12 //#region --Declaration--.
13 -const { fetchPopularMovies } = useTMDB(); 13 +const { fetchPopularMovies, searchMovies } = useTMDB();
14 //#endregion 14 //#endregion
15 15
16 //#region --Data/refs--. 16 //#region --Data/refs--.
@@ -18,6 +18,7 @@ const isInitialLoading = ref(true); @@ -18,6 +18,7 @@ const isInitialLoading = ref(true);
18 const isLoadingMore = ref(false); 18 const isLoadingMore = ref(false);
19 const currentPage = ref(1); 19 const currentPage = ref(1);
20 const totalPages = ref(0); 20 const totalPages = ref(0);
  21 +const searchQuery = ref("");
21 /** Elément observé pour le défilement infini. */ 22 /** Elément observé pour le défilement infini. */
22 const loadMoreTrigger = ref<HTMLElement | null>(null); 23 const loadMoreTrigger = ref<HTMLElement | null>(null);
23 /** Instance de IntersectionObserver */ 24 /** Instance de IntersectionObserver */
@@ -41,11 +42,12 @@ const fetchMovies = async (page: number) => { @@ -41,11 +42,12 @@ const fetchMovies = async (page: number) => {
41 const data = await fetchPopularMovies(page); 42 const data = await fetchPopularMovies(page);
42 // Save in Movie model. 43 // Save in Movie model.
43 if (isInitialLoading.value) { 44 if (isInitialLoading.value) {
  45 + // First fetch, erase old data before save.
44 useRepo(Movie).fresh(data.results); 46 useRepo(Movie).fresh(data.results);
45 } else { 47 } else {
  48 + // Add to store collection.
46 useRepo(Movie).save(data.results); 49 useRepo(Movie).save(data.results);
47 } 50 }
48 -  
49 totalPages.value = data.total_pages; 51 totalPages.value = data.total_pages;
50 currentPage.value = page; 52 currentPage.value = page;
51 } catch (error) { 53 } catch (error) {
@@ -56,12 +58,53 @@ const fetchMovies = async (page: number) => { @@ -56,12 +58,53 @@ const fetchMovies = async (page: number) => {
56 } 58 }
57 }; 59 };
58 60
  61 +/**
  62 + * Search movies
  63 + * @param query
  64 + * @param page
  65 + */
  66 +const search = async (query: string, page: number) => {
  67 + // If empty search, fetch popular movies.
  68 + if (!query.trim()) {
  69 + await fetchMovies(1);
  70 + return;
  71 + }
  72 + try {
  73 + isLoadingMore.value = true;
  74 + if (page === 1) {
  75 + isInitialLoading.value = true;
  76 + }
  77 + const data = await searchMovies(query, page);
  78 + // Save in Movie model.
  79 + if (isInitialLoading.value) {
  80 + // First fetch, erase old data before save.
  81 + useRepo(Movie).fresh(data.results);
  82 + } else {
  83 + // Add to store collection.
  84 + useRepo(Movie).save(data.results);
  85 + }
  86 + totalPages.value = data.total_pages;
  87 + currentPage.value = page;
  88 + } catch (error) {
  89 + console.error("Error searching movies:", error);
  90 + } finally {
  91 + isInitialLoading.value = false;
  92 + isLoadingMore.value = false;
  93 + }
  94 +};
  95 +
59 function createIntersectionObserver() { 96 function createIntersectionObserver() {
60 return new IntersectionObserver( 97 return new IntersectionObserver(
61 (entries) => { 98 (entries) => {
62 const [entry] = entries; 99 const [entry] = entries;
63 if (entry.isIntersecting && !isLoadingMore.value && currentPage.value < totalPages.value) { 100 if (entry.isIntersecting && !isLoadingMore.value && currentPage.value < totalPages.value) {
64 - fetchMovies(currentPage.value + 1); 101 + if (searchQuery.value) {
  102 + // Continue searching query if already active.
  103 + search(searchQuery.value, currentPage.value + 1)
  104 + } else {
  105 + // Continue fetching popular movies.
  106 + fetchMovies(currentPage.value + 1);
  107 + }
65 } 108 }
66 }, 109 },
67 { threshold: 1.0 }, 110 { threshold: 1.0 },
@@ -69,11 +112,15 @@ function createIntersectionObserver() { @@ -69,11 +112,15 @@ function createIntersectionObserver() {
69 } 112 }
70 113
71 function handleSearchEvent(event: string) { 114 function handleSearchEvent(event: string) {
72 - console.log(event); 115 + currentPage.value = 1;
  116 + searchQuery.value = event;
  117 + search(event, 1);
73 } 118 }
74 119
75 function handleClearSearchEvent() { 120 function handleClearSearchEvent() {
76 - console.log("clear event"); 121 + searchQuery.value = '';
  122 + // Fetch popular movies after clear.
  123 + fetchMovies(1);
77 } 124 }
78 125
79 //#endregion 126 //#endregion
@@ -15,7 +15,7 @@ export const useTMDB = function() { @@ -15,7 +15,7 @@ export const useTMDB = function() {
15 `${apiUrl}/movie/popular?api_key=${apiKey}&language=fr-FR&page=${page}`, 15 `${apiUrl}/movie/popular?api_key=${apiKey}&language=fr-FR&page=${page}`,
16 ); 16 );
17 if (!response.ok) { 17 if (!response.ok) {
18 - console.error("An error occured when fetching popular movies:"); 18 + console.error("An error occurred when fetching popular movies:");
19 } else { 19 } else {
20 return await response.json(); 20 return await response.json();
21 } 21 }
@@ -24,5 +24,25 @@ export const useTMDB = function() { @@ -24,5 +24,25 @@ export const useTMDB = function() {
24 } 24 }
25 }; 25 };
26 26
27 - return { fetchPopularMovies } 27 + /**
  28 + * Search movies
  29 + * @param query
  30 + * @param page
  31 + */
  32 + const searchMovies = async (query: string, page: number) => {
  33 + try {
  34 + const response = await fetch(
  35 + `${apiUrl}/search/movie?api_key=${apiKey}&language=fr-FR&query=${encodeURIComponent(query)}&page=${page}`,
  36 + );
  37 + if (!response.ok) {
  38 + console.error("An error occurred when searching movies:");
  39 + } else {
  40 + return await response.json();
  41 + }
  42 + } catch (error) {
  43 + console.error("Error searching movies:", error);
  44 + }
  45 + };
  46 +
  47 + return { fetchPopularMovies, searchMovies }
28 } 48 }