diff --git a/src/components/Movie/dtos/index.ts b/src/components/Movie/dtos/index.ts index 2ba817f..bc61813 100644 --- a/src/components/Movie/dtos/index.ts +++ b/src/components/Movie/dtos/index.ts @@ -3,7 +3,7 @@ import ContainerProps from './ContainerProps'; export default interface MovieProps extends ContainerProps { id: number; favorite: boolean; - mediaType?: string; + mediaType: number; poster?: string; backdrop?: string; } diff --git a/src/components/Movie/index.tsx b/src/components/Movie/index.tsx index 171c0fa..79b0f0a 100644 --- a/src/components/Movie/index.tsx +++ b/src/components/Movie/index.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { BsHeartFill } from 'react-icons/bs'; +import { Type } from 'domains/Favorites/enums'; import { useAuth } from 'domains/Auth/hooks'; import { useFavorite } from 'domains/Favorites/hooks'; import Route from 'routes/enums'; @@ -23,14 +24,16 @@ const Movie: React.FC = ({ size, ...movie }) => { return; } - await UpdateFavorite(movie.id); + await UpdateFavorite(movie.id, movie.mediaType); } catch (error) { console.log('handleFavorite -> error', error); } - }, [user, UpdateFavorite, movie.id]); + }, [user, UpdateFavorite, movie.id, movie.mediaType]); const handleRedirect = useCallback(() => { - if (movie.mediaType === 'tv') { + if (movie.mediaType === Type.TV) { + history.push(`${Route.TV}/${movie.id}`); + return; } @@ -39,14 +42,16 @@ const Movie: React.FC = ({ size, ...movie }) => { // Check if movie is in favorite list and change status useEffect(() => { + // console.log('movie', movie); if (user) { const response = favoriteList.find( - favorite => +favorite.movieId === +movie.id, + favorite => + favorite.entityId === movie.id && favorite.typeId === movie.mediaType, ); setIsFavorite(!!response); } - }, [user, favoriteList, movie.id]); + }, [user, favoriteList, movie.id, movie.mediaType]); if (!movie.poster && !movie.backdrop) { return null; diff --git a/src/components/SearchInput/index.tsx b/src/components/SearchInput/index.tsx index ffb02cb..667bc27 100644 --- a/src/components/SearchInput/index.tsx +++ b/src/components/SearchInput/index.tsx @@ -9,7 +9,7 @@ const SearchInput: React.FC> = ({ diff --git a/src/components/SearchInput/styles.ts b/src/components/SearchInput/styles.ts index 67f7700..6bc411a 100644 --- a/src/components/SearchInput/styles.ts +++ b/src/components/SearchInput/styles.ts @@ -8,7 +8,7 @@ export const Container = styled.div` `; export const Input = styled.input` - font-size: ${Size.Large}; + font-size: ${Size.Medium}; border: 0; width: 100%; text-align: center; diff --git a/src/containers/Filmography/index.tsx b/src/containers/Filmography/index.tsx index 5764ebf..c12d352 100644 --- a/src/containers/Filmography/index.tsx +++ b/src/containers/Filmography/index.tsx @@ -2,6 +2,7 @@ import React, { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import Route from 'routes/enums'; +import { Type } from 'domains/Favorites/enums'; import { ReactComponent as Loading } from 'assets/loading.svg'; import { Wrapper } from 'components/Layout'; @@ -33,9 +34,13 @@ const Filmography: React.FC = ({ const handleRedirect = useCallback( (movie: any) => { - if (movie.mediaType === 'movie') { - history.push(`${Route.MOVIE}/${movie.id}`); + if (movie.mediaType === Type.TV) { + history.push(`${Route.TV}/${movie.id}`); + + return; } + + history.push(`${Route.MOVIE}/${movie.id}`); }, [history], ); diff --git a/src/containers/Result/index.tsx b/src/containers/Result/index.tsx index c3a63f2..e7b61f1 100644 --- a/src/containers/Result/index.tsx +++ b/src/containers/Result/index.tsx @@ -3,6 +3,7 @@ import { useHistory } from 'react-router-dom'; import Route from 'routes/enums'; import Props from 'containers/Result/dtos'; +import { Type } from 'domains/Favorites/enums'; import { Movie } from 'components'; import { @@ -18,8 +19,14 @@ const Result: React.FC = ({ data }) => { const history = useHistory(); const handleRedirect = useCallback(() => { + if (data.mediaType === Type.TV) { + history.push(`${Route.TV}/${data.id}`); + + return; + } + history.push(`${Route.MOVIE}/${data.id}`); - }, [history, data.id]); + }, [history, data.id, data.mediaType]); return ( diff --git a/src/containers/SearchModal/index.tsx b/src/containers/SearchModal/index.tsx index b626b63..4c2580d 100644 --- a/src/containers/SearchModal/index.tsx +++ b/src/containers/SearchModal/index.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useMemo, useState } from 'react'; import { debounce } from 'lodash'; -// import { debounce } from 'shared/utils'; import { Multi } from 'domains/Search/api'; import { ReactComponent as Loading } from 'assets/loading.svg'; @@ -20,7 +19,7 @@ import { EmptyLabel, } from './styles'; -const SearchModal: React.FC = ({ onClose, isShow, children }) => { +const SearchModal: React.FC = ({ onClose, isShow }) => { const minLength = useMemo(() => 3, []); const wait = useMemo(() => 750, []); const [data, setData] = useState([] as any); diff --git a/src/domains/Favorites/api/List/RawResponse.ts b/src/domains/Favorites/api/List/RawResponse.ts index adb1f95..51d9b1d 100644 --- a/src/domains/Favorites/api/List/RawResponse.ts +++ b/src/domains/Favorites/api/List/RawResponse.ts @@ -1,7 +1,8 @@ export default interface RawResponse { id: string; user_id: string; - movie_id: number; + entity_id: number; + type_id: number; created_at: string; updated_at: string; } diff --git a/src/domains/Favorites/api/List/Response.ts b/src/domains/Favorites/api/List/Response.ts index 8fbb690..0da803c 100644 --- a/src/domains/Favorites/api/List/Response.ts +++ b/src/domains/Favorites/api/List/Response.ts @@ -3,5 +3,6 @@ import DetailsResponse from 'domains/Movie/api/Details/Response'; export default interface Response extends Omit { favoriteId: string; userId: string; - movieId: number; + entityId: number; + typeId: number; } diff --git a/src/domains/Favorites/api/List/index.ts b/src/domains/Favorites/api/List/index.ts index df39607..9aa43da 100644 --- a/src/domains/Favorites/api/List/index.ts +++ b/src/domains/Favorites/api/List/index.ts @@ -1,8 +1,8 @@ import api from 'services/api'; +import { getByType } from 'domains/Favorites/helpers'; import RawResponse from 'domains/Favorites/api/List/RawResponse'; import Response from 'domains/Favorites/api/List/Response'; -import { Details } from 'domains/Movie/api'; const Favorites = async (): Promise => { const response = await rawFavorites(); @@ -20,16 +20,25 @@ const parseResponse = async ( rawResponse: RawResponse[], ): Promise => { const response = rawResponse.map(async (favorite: RawResponse) => { - // Get favorites details from TMDB API and merge - const details = await Details(favorite.movie_id); - - // Merge API and TMDB results - return { - ...details, - favoriteId: favorite.id, - userId: favorite.user_id, - movieId: +favorite.movie_id, - }; + try { + // Get favorites details from TMDB API and merge + const details = await getByType(favorite); + + if (!details) { + throw new Error('Media not found.'); + } + + // Merge API and TMDB results + return { + ...details, + favoriteId: favorite.id, + userId: favorite.user_id, + entityId: favorite.entity_id, + typeId: favorite.type_id, + }; + } catch (error) { + console.log('error', error); + } }); // Resolve async requests and promises diff --git a/src/domains/Favorites/api/Update/RawResponse.ts b/src/domains/Favorites/api/Update/RawResponse.ts index adb1f95..51d9b1d 100644 --- a/src/domains/Favorites/api/Update/RawResponse.ts +++ b/src/domains/Favorites/api/Update/RawResponse.ts @@ -1,7 +1,8 @@ export default interface RawResponse { id: string; user_id: string; - movie_id: number; + entity_id: number; + type_id: number; created_at: string; updated_at: string; } diff --git a/src/domains/Favorites/api/Update/index.ts b/src/domains/Favorites/api/Update/index.ts index fcb4135..ef180c2 100644 --- a/src/domains/Favorites/api/Update/index.ts +++ b/src/domains/Favorites/api/Update/index.ts @@ -1,11 +1,14 @@ import api from 'services/api'; +import { getByType } from 'domains/Favorites/helpers'; import RawResponse from 'domains/Favorites/api/Update/RawResponse'; import Response from 'domains/Favorites/api/Update/Response'; -import { Details } from 'domains/Movie/api'; -const Update = async (movieId: number): Promise => { - const response = await rawFavorites(movieId); +const Update = async ( + entityId: number, + typeId: number, +): Promise => { + const response = await rawFavorites(entityId, typeId); if (!response) { return null; @@ -15,13 +18,15 @@ const Update = async (movieId: number): Promise => { }; export const rawFavorites = async ( - movieId: number, + entityId: number, + typeId: number, ): Promise => { const response = await api.post('/favorites', { - movie_id: movieId.toString(), + entity_id: entityId.toString(), + type_id: typeId.toString(), }); - if (!response.data.movie_id) { + if (!response.data.entity_id) { return null; } @@ -29,12 +34,13 @@ export const rawFavorites = async ( }; const parseResponse = async (rawResponse: RawResponse): Promise => { - const details = await Details(rawResponse.movie_id); + const details = await getByType(rawResponse); const response = { ...details, favoriteId: rawResponse.id, userId: rawResponse.user_id, - movieId: rawResponse.movie_id, + entityId: rawResponse.entity_id, + typeId: rawResponse.type_id, }; return response; diff --git a/src/domains/Favorites/dtos/ContextData.ts b/src/domains/Favorites/dtos/ContextData.ts index 20131b4..b2a1a27 100644 --- a/src/domains/Favorites/dtos/ContextData.ts +++ b/src/domains/Favorites/dtos/ContextData.ts @@ -7,5 +7,8 @@ export default interface ContextData { favoriteList: ListResponse[]; setFavoriteList: Dispatch>; Favorites(): Promise; - UpdateFavorite(movieId: number): Promise; + UpdateFavorite( + entityId: number, + typeId: number, + ): Promise; } diff --git a/src/domains/Favorites/enums/Type.ts b/src/domains/Favorites/enums/Type.ts new file mode 100644 index 0000000..896d904 --- /dev/null +++ b/src/domains/Favorites/enums/Type.ts @@ -0,0 +1,11 @@ +enum Type { + MOVIE = 1, + TV = 2, + PERSON = 3, + REVIEW = 4, + COMPANY = 5, + COLLECTION = 6, + KEYWORD = 7, +} + +export default Type; diff --git a/src/domains/Favorites/enums/index.ts b/src/domains/Favorites/enums/index.ts new file mode 100644 index 0000000..ef5fd40 --- /dev/null +++ b/src/domains/Favorites/enums/index.ts @@ -0,0 +1 @@ +export { default as Type } from './Type'; diff --git a/src/domains/Favorites/helpers/getByType.ts b/src/domains/Favorites/helpers/getByType.ts new file mode 100644 index 0000000..054bf23 --- /dev/null +++ b/src/domains/Favorites/helpers/getByType.ts @@ -0,0 +1,20 @@ +import { Type } from 'domains/Favorites/enums'; +import { Details as MovieDetails } from 'domains/Movie/api'; +import { Details as TvDetails } from 'domains/Tv/api'; +import { Details as PersonDetails } from 'domains/Person/api'; + +const getByType = async (favorite: any): Promise => { + if (favorite.type_id === Type.MOVIE) { + return MovieDetails(favorite.entity_id); + } + + if (favorite.type_id === Type.TV) { + return TvDetails(favorite.entity_id); + } + + if (favorite.type_id === Type.PERSON) { + return PersonDetails(favorite.entity_id); + } +}; + +export default getByType; diff --git a/src/domains/Favorites/helpers/index.ts b/src/domains/Favorites/helpers/index.ts new file mode 100644 index 0000000..8a86df9 --- /dev/null +++ b/src/domains/Favorites/helpers/index.ts @@ -0,0 +1 @@ +export { default as getByType } from './getByType'; diff --git a/src/domains/Favorites/hooks/index.tsx b/src/domains/Favorites/hooks/index.tsx index 80ddab5..cbbf7bb 100644 --- a/src/domains/Favorites/hooks/index.tsx +++ b/src/domains/Favorites/hooks/index.tsx @@ -26,13 +26,17 @@ const FavoriteProvider: React.FC = ({ children }) => { }, []); const UpdateFavorite = useCallback( - async (movieId: number): Promise => { - const response = await UpdateFavoriteApi(movieId); + async ( + entityId: number, + typeId: number, + ): Promise => { + const response = await UpdateFavoriteApi(entityId, typeId); // Remove favorite if (!response) { const updatedFavoriteList = favoriteList.filter( - favorite => +favorite.movieId !== +movieId, + favorite => + !(favorite.entityId === entityId && favorite.typeId === typeId), ); setFavoriteList(updatedFavoriteList); diff --git a/src/domains/Movie/api/Details/RawResponse.ts b/src/domains/Movie/api/Details/RawResponse.ts index b7299ed..169e4d0 100644 --- a/src/domains/Movie/api/Details/RawResponse.ts +++ b/src/domains/Movie/api/Details/RawResponse.ts @@ -28,4 +28,5 @@ export default interface RawResponse { results: Recommendations[]; }; credits?: Credits; + mediaType: string; } diff --git a/src/domains/Movie/api/Details/Response.ts b/src/domains/Movie/api/Details/Response.ts index adbd028..a72a8b6 100644 --- a/src/domains/Movie/api/Details/Response.ts +++ b/src/domains/Movie/api/Details/Response.ts @@ -28,4 +28,6 @@ export default interface Response { recommendations?: Recommendations[]; credits?: Credits; directorName?: string; + + mediaType: number; } diff --git a/src/domains/Movie/api/Details/index.ts b/src/domains/Movie/api/Details/index.ts index 3c7ddcc..224e1f2 100644 --- a/src/domains/Movie/api/Details/index.ts +++ b/src/domains/Movie/api/Details/index.ts @@ -1,15 +1,16 @@ import tmdb from 'services/api/tmdb'; +import { arrayToString, formatTmdbImage, formatDate } from 'shared/utils'; + +import { Type } from 'domains/Favorites/enums'; import Params from 'domains/Movie/api/Details/Params'; import RawResponse from 'domains/Movie/api/Details/RawResponse'; import Response from 'domains/Movie/api/Details/Response'; import Recommendations from 'domains/Movie/api/Recommendations/Response'; import Credits from 'domains/Movie/api/Credits/Response'; -import formatDate from 'shared/utils/formatDate'; -import formatTmdbImage from 'shared/utils/formatTmdbImage'; + import Crew from 'domains/Movie/api/Credits/dtos/Crew'; import Cast from 'domains/Movie/api/Credits/dtos/Cast'; -import { arrayToString } from 'shared/utils'; const Details = async (movieId: number, params?: Params): Promise => { const response = await rawPopular(movieId, params); @@ -51,6 +52,7 @@ const parseResponse = (movie: RawResponse): Response => { poster: formatTmdbImage({ value: movie.poster_path }), backdrop: formatTmdbImage({ value: movie.backdrop_path }), favorite: false, + mediaType: Type.MOVIE, } as Response; const recommendations = movie.recommendations?.results.map( @@ -60,6 +62,7 @@ const parseResponse = (movie: RawResponse): Response => { id: recommendation.id, title: recommendation.title, favorite: false, + mediaType: Type.MOVIE, }), ) as Recommendations[]; diff --git a/src/domains/Movie/api/NowPlaying/Response.ts b/src/domains/Movie/api/NowPlaying/Response.ts index 3ea37c8..b1ba704 100644 --- a/src/domains/Movie/api/NowPlaying/Response.ts +++ b/src/domains/Movie/api/NowPlaying/Response.ts @@ -11,4 +11,5 @@ export default interface Response { voteCount: number; voteAverage: number; favorite: boolean; + mediaType: number; } diff --git a/src/domains/Movie/api/NowPlaying/index.ts b/src/domains/Movie/api/NowPlaying/index.ts index 21b9c75..eff632a 100644 --- a/src/domains/Movie/api/NowPlaying/index.ts +++ b/src/domains/Movie/api/NowPlaying/index.ts @@ -1,10 +1,11 @@ import tmdb from 'services/api/tmdb'; +import { formatDate, formatTmdbImage } from 'shared/utils'; + +import { Type } from 'domains/Favorites/enums'; import Params from 'domains/Movie/api/NowPlaying/Params'; import RawResponse from 'domains/Movie/api/NowPlaying/RawResponse'; import Response from 'domains/Movie/api/NowPlaying/Response'; -import formatDate from 'shared/utils/formatDate'; -import formatTmdbImage from 'shared/utils/formatTmdbImage'; const NowPlaying = async (params?: Params): Promise => { const response = await rawNowPlaying(params); @@ -40,6 +41,7 @@ const parseResponse = (rawResponse: RawResponse[]): Response[] => { poster: formatTmdbImage({ value: movie.poster_path }), backdrop: formatTmdbImage({ value: movie.backdrop_path }), favorite: false, + mediaType: Type.MOVIE, } as Response; response = [...response, parsedMovie]; diff --git a/src/domains/Movie/api/Popular/Params.ts b/src/domains/Movie/api/Popular/Params.ts new file mode 100644 index 0000000..d09e291 --- /dev/null +++ b/src/domains/Movie/api/Popular/Params.ts @@ -0,0 +1,3 @@ +export default interface Params { + page?: number; +} diff --git a/src/domains/Movie/api/Popular/Response.ts b/src/domains/Movie/api/Popular/Response.ts index 3ea37c8..b1ba704 100644 --- a/src/domains/Movie/api/Popular/Response.ts +++ b/src/domains/Movie/api/Popular/Response.ts @@ -11,4 +11,5 @@ export default interface Response { voteCount: number; voteAverage: number; favorite: boolean; + mediaType: number; } diff --git a/src/domains/Movie/api/Popular/index.ts b/src/domains/Movie/api/Popular/index.ts index 7c9ad5d..b771e4a 100644 --- a/src/domains/Movie/api/Popular/index.ts +++ b/src/domains/Movie/api/Popular/index.ts @@ -1,18 +1,22 @@ import tmdb from 'services/api/tmdb'; +import { formatDate, formatTmdbImage } from 'shared/utils'; + +import { Type } from 'domains/Favorites/enums'; +import Params from 'domains/Movie/api/Popular/Params'; import RawResponse from 'domains/Movie/api/Popular/RawResponse'; import Response from 'domains/Movie/api/Popular/Response'; -import formatDate from 'shared/utils/formatDate'; -import formatTmdbImage from 'shared/utils/formatTmdbImage'; -const Popular = async (): Promise => { - const response = await rawPopular(); +const Popular = async (params?: Params): Promise => { + const response = await rawPopular(params); return parseResponse(response); }; -export const rawPopular = async (): Promise => { - const response = await tmdb.get('/movie/popular'); +export const rawPopular = async (params?: Params): Promise => { + const response = await tmdb.get('/movie/popular', { + params: { page: params?.page }, + }); return response.data.results; }; @@ -35,6 +39,7 @@ const parseResponse = (rawResponse: RawResponse[]): Response[] => { poster: formatTmdbImage({ value: movie.poster_path }), backdrop: formatTmdbImage({ value: movie.backdrop_path }), favorite: false, + mediaType: Type.MOVIE, } as Response; response = [...response, parsedMovie]; diff --git a/src/domains/Movie/api/Recommendations/Response.ts b/src/domains/Movie/api/Recommendations/Response.ts index fd046d8..a36b718 100644 --- a/src/domains/Movie/api/Recommendations/Response.ts +++ b/src/domains/Movie/api/Recommendations/Response.ts @@ -4,6 +4,7 @@ export default interface Response { id: number; title: string; favorite: boolean; + mediaType: number; // overview: string; // releaseDate: string; // genresIds: number[]; diff --git a/src/domains/Movie/api/Recommendations/index.ts b/src/domains/Movie/api/Recommendations/index.ts index f61c556..d517c61 100644 --- a/src/domains/Movie/api/Recommendations/index.ts +++ b/src/domains/Movie/api/Recommendations/index.ts @@ -1,9 +1,10 @@ import tmdb from 'services/api/tmdb'; +import { formatDate, formatTmdbImage } from 'shared/utils'; + +import { Type } from 'domains/Favorites/enums'; import RawResponse from 'domains/Movie/api/Recommendations/RawResponse'; import Response from 'domains/Movie/api/Recommendations/Response'; -import formatDate from 'shared/utils/formatDate'; -import formatTmdbImage from 'shared/utils/formatTmdbImage'; const Recommendations = async (movieId: number): Promise => { const response = await rawPopular(movieId); @@ -35,6 +36,7 @@ const parseResponse = (rawResponse: RawResponse[]): Response[] => { poster: formatTmdbImage({ value: movie.poster_path }), backdrop: formatTmdbImage({ value: movie.backdrop_path }), favorite: false, + mediaType: Type.MOVIE, } as Response; response = [...response, parsedMovie]; diff --git a/src/domains/Person/api/Details/Movie.ts b/src/domains/Person/api/Details/Movie.ts index 070c716..93362b9 100644 --- a/src/domains/Person/api/Details/Movie.ts +++ b/src/domains/Person/api/Details/Movie.ts @@ -7,5 +7,4 @@ export default interface Movie extends MovieDetails { firstAirDate?: string; name?: string; - mediaType?: string; } diff --git a/src/domains/Person/api/Details/Response.ts b/src/domains/Person/api/Details/Response.ts index d195dbb..42c9a3e 100644 --- a/src/domains/Person/api/Details/Response.ts +++ b/src/domains/Person/api/Details/Response.ts @@ -21,4 +21,5 @@ export default interface Response { knownFor?: Movie[]; filmography?: Movie[]; + mediaType: number; } diff --git a/src/domains/Person/api/Details/index.ts b/src/domains/Person/api/Details/index.ts index e03aad8..118cf63 100644 --- a/src/domains/Person/api/Details/index.ts +++ b/src/domains/Person/api/Details/index.ts @@ -5,7 +5,7 @@ import RawResponse from 'domains/Person/api/Details/RawResponse'; import Response from 'domains/Person/api/Details/Response'; import Movie from 'domains/Person/api/Details/Movie'; // import Credits from 'domains/Person/api/Credits/Response'; -import { formatDate, formatTmdbImage } from 'shared/utils'; +import { formatDate, formatTmdbImage, getMediaTypeId } from 'shared/utils'; // import Crew from 'domains/Person/api/Credits/dtos/Crew'; // import Cast from 'domains/Person/api/Credits/dtos/Cast'; // import { arrayToString } from 'shared/utils'; @@ -69,7 +69,7 @@ const parseResponse = (person: RawResponse): Response => { (movie.first_air_date && movie.first_air_date.substring(0, 4)), popularity: movie.popularity, name: movie.name, - mediaType: movie.media_type, + mediaType: getMediaTypeId(movie.media_type), })) .filter(item => item.originalDate && item.character.length > 0) as Movie[]; diff --git a/src/domains/Search/api/Multi/Movie.ts b/src/domains/Search/api/Multi/Movie.ts index 070c716..93362b9 100644 --- a/src/domains/Search/api/Multi/Movie.ts +++ b/src/domains/Search/api/Multi/Movie.ts @@ -7,5 +7,4 @@ export default interface Movie extends MovieDetails { firstAirDate?: string; name?: string; - mediaType?: string; } diff --git a/src/domains/Search/api/Multi/RawResponse.ts b/src/domains/Search/api/Multi/RawResponse.ts index e0b1f22..71ef6aa 100644 --- a/src/domains/Search/api/Multi/RawResponse.ts +++ b/src/domains/Search/api/Multi/RawResponse.ts @@ -4,12 +4,9 @@ import RawMovie from 'domains/Person/api/Details/RawMovie'; export default interface RawResponse { poster_path?: string; overview: string; - release_date: string; genre_ids: number[]; id: number; - original_title: string; original_language: string; - title: string; backdrop_path?: string; popularity: number; vote_count: number; @@ -17,4 +14,10 @@ export default interface RawResponse { vote_average: number; media_type: string; + release_date?: string; + original_title?: string; + title?: string; + first_air_date?: string; + original_name?: string; + name?: string; } diff --git a/src/domains/Search/api/Multi/Response.ts b/src/domains/Search/api/Multi/Response.ts index e1ca2bf..7f5c66d 100644 --- a/src/domains/Search/api/Multi/Response.ts +++ b/src/domains/Search/api/Multi/Response.ts @@ -16,5 +16,5 @@ export default interface Response { voteAverage: number; favorite: boolean; - mediaType: string; + mediaType: number; } diff --git a/src/domains/Search/api/Multi/index.ts b/src/domains/Search/api/Multi/index.ts index 7610a70..7c63e81 100644 --- a/src/domains/Search/api/Multi/index.ts +++ b/src/domains/Search/api/Multi/index.ts @@ -1,9 +1,10 @@ import tmdb from 'services/api/tmdb'; +import { Type } from 'domains/Favorites/enums'; import Params from 'domains/Search/api/Multi/Params'; import RawResponse from 'domains/Search/api/Multi/RawResponse'; import Response from 'domains/Search/api/Multi/Response'; -import { formatDate, formatTmdbImage } from 'shared/utils'; +import { formatDate, formatTmdbImage, getMediaTypeId } from 'shared/utils'; const Multi = async (params: Params): Promise => { const response = await rawPopular(params); @@ -27,26 +28,30 @@ const parseResponse = (rawResponse: RawResponse[]): Response[] => { overview: result.overview, genreIds: result.genre_ids, id: result.id, - originalTitle: result.original_title, - title: result.title, + originalTitle: result.original_title || result.original_name, + title: result.title || result.name, popularity: result.popularity, voteCount: result.vote_count, voteAverage: result.vote_average, - releaseYear: result.release_date?.substring(0, 4), - releaseDate: formatDate({ value: result.release_date }), + releaseYear: + result.release_date?.substring(0, 4) || + result.first_air_date?.substring(0, 4), + releaseDate: + formatDate({ value: result.release_date }) || + formatDate({ value: result.first_air_date }), poster: formatTmdbImage({ value: result.poster_path }), backdrop: formatTmdbImage({ value: result.backdrop_path }), favorite: false, - mediaType: result.media_type, + mediaType: getMediaTypeId(result.media_type), } as Response; response = [...response, parsedMovie]; }); response = [...response] - .filter(item => item.mediaType === 'movie') + .filter(item => item.mediaType === Type.MOVIE || item.mediaType === Type.TV) .sort((a, b) => (a.popularity < b.popularity ? 1 : -1)); return response; diff --git a/src/domains/Tv/api/Credits/RawResponse.ts b/src/domains/Tv/api/Credits/RawResponse.ts new file mode 100644 index 0000000..f68b078 --- /dev/null +++ b/src/domains/Tv/api/Credits/RawResponse.ts @@ -0,0 +1,25 @@ +export default interface RawResponse { + cast: [ + { + cast_id: number; + character: string; + credit_id: string; + gender?: number; + id: number; + name: string; + order: number; + profile_path?: string; + }, + ]; + crew: [ + { + credit_id: string; + department: string; + gender?: number; + id: number; + job: string; + name: string; + profile_path?: string; + }, + ]; +} diff --git a/src/domains/Tv/api/Credits/Response.ts b/src/domains/Tv/api/Credits/Response.ts new file mode 100644 index 0000000..37fc3d9 --- /dev/null +++ b/src/domains/Tv/api/Credits/Response.ts @@ -0,0 +1,7 @@ +import Cast from 'domains/Tv/api/Credits/dtos/Cast'; +import Crew from 'domains/Tv/api/Credits/dtos/Crew'; + +export default interface Response { + cast: Cast[]; + crew: Crew[]; +} diff --git a/src/domains/Tv/api/Credits/dtos/Cast.ts b/src/domains/Tv/api/Credits/dtos/Cast.ts new file mode 100644 index 0000000..a647d31 --- /dev/null +++ b/src/domains/Tv/api/Credits/dtos/Cast.ts @@ -0,0 +1,10 @@ +export default interface Cast { + castId: number; + character: string; + creditId: string; + gender?: number; + id: number; + name: string; + order: number; + profile: string; +} diff --git a/src/domains/Tv/api/Credits/dtos/Crew.ts b/src/domains/Tv/api/Credits/dtos/Crew.ts new file mode 100644 index 0000000..dc1054b --- /dev/null +++ b/src/domains/Tv/api/Credits/dtos/Crew.ts @@ -0,0 +1,9 @@ +export default interface Crew { + creditId: string; + department: string; + gender?: number; + id: number; + job: string; + name: string; + profile: string; +} diff --git a/src/domains/Tv/api/Details/Params.ts b/src/domains/Tv/api/Details/Params.ts new file mode 100644 index 0000000..e1d07a0 --- /dev/null +++ b/src/domains/Tv/api/Details/Params.ts @@ -0,0 +1,3 @@ +export default interface Params { + appendToResponse?: string; +} diff --git a/src/domains/Tv/api/Details/RawResponse.ts b/src/domains/Tv/api/Details/RawResponse.ts new file mode 100644 index 0000000..040b1e9 --- /dev/null +++ b/src/domains/Tv/api/Details/RawResponse.ts @@ -0,0 +1,29 @@ +import Recommendations from 'domains/Movie/api/Recommendations/RawResponse'; +import Credits from 'domains/Movie/api/Credits/RawResponse'; + +export default interface RawResponse { + poster_path?: string; + overview: string; + first_air_date: string; + genres: [ + { + id: number; + name: string; + }, + ]; + homepage?: string; + id: number; + original_language: string; + original_name: string; + name: string; + backdrop_path?: string; + popularity: number; + episode_run_time: number; + vote_count: number; + vote_average: number; + recommendations?: { + results: Recommendations[]; + }; + credits?: Credits; + mediaType: string; +} diff --git a/src/domains/Tv/api/Details/Response.ts b/src/domains/Tv/api/Details/Response.ts new file mode 100644 index 0000000..713dc59 --- /dev/null +++ b/src/domains/Tv/api/Details/Response.ts @@ -0,0 +1,32 @@ +import Recommendations from 'domains/Movie/api/Recommendations/Response'; +import Credits from 'domains/Movie/api/Credits/Response'; + +export default interface Response { + poster?: string; + backdrop?: string; + budget: number; + overview: string; + releaseDate: string; + genres: [ + { + id: number; + name: string; + }, + ]; + genresNames: string; + homepage?: string; + id: number; + title: string; + originalTitle: string; + popularity: number; + runtime: string; + voteCount: number; + voteAverage: number; + favorite: boolean; + + recommendations?: Recommendations[]; + credits?: Credits; + directorName?: string; + + mediaType: number; +} diff --git a/src/domains/Tv/api/Details/index.ts b/src/domains/Tv/api/Details/index.ts new file mode 100644 index 0000000..728a9e8 --- /dev/null +++ b/src/domains/Tv/api/Details/index.ts @@ -0,0 +1,93 @@ +import tmdb from 'services/api/tmdb'; + +import { arrayToString, formatTmdbImage, formatDate } from 'shared/utils'; + +import { Type } from 'domains/Favorites/enums'; +import Params from 'domains/Tv/api/Details/Params'; +import RawResponse from 'domains/Tv/api/Details/RawResponse'; +import Response from 'domains/Tv/api/Details/Response'; +import Recommendations from 'domains/Tv/api/Recommendations/Response'; +import Credits from 'domains/Tv/api/Credits/Response'; + +import Crew from 'domains/Tv/api/Credits/dtos/Crew'; +import Cast from 'domains/Tv/api/Credits/dtos/Cast'; + +const Details = async (tvId: number, params?: Params): Promise => { + const response = await rawPopular(tvId, params); + + return parseResponse(response); +}; + +export const rawPopular = async ( + tvId: number, + params?: Params, +): Promise => { + const response = await tmdb.get(`/tv/${tvId}`, { + params: { append_to_response: params?.appendToResponse }, + }); + + return response.data; +}; + +const parseResponse = (tv: RawResponse): Response => { + let parsedMovie = { + overview: tv.overview, + genres: tv.genres, + genresNames: arrayToString(tv.genres, 'name'), + id: tv.id, + originalTitle: tv.original_name, + title: tv.name, + popularity: tv.popularity, + voteCount: tv.vote_count, + voteAverage: tv.vote_average, + runtime: `${tv.episode_run_time} min`, + + directorName: tv.credits?.crew.find( + person => person.job.toUpperCase() === 'DIRECTOR', + )?.name, + + releaseDate: formatDate({ value: tv.first_air_date }), + poster: formatTmdbImage({ value: tv.poster_path }), + backdrop: formatTmdbImage({ value: tv.backdrop_path }), + favorite: false, + mediaType: Type.TV, + } as Response; + + const recommendations = tv.recommendations?.results.map(recommendation => ({ + poster: formatTmdbImage({ value: recommendation.poster_path }), + backdrop: formatTmdbImage({ value: recommendation.poster_path }), + id: recommendation.id, + title: recommendation.title, + favorite: false, + mediaType: Type.TV, + })) as Recommendations[]; + + const cast = tv.credits?.cast.slice(0, 15).map(person => ({ + order: person.order, + id: person.id, + name: person.name, + character: person.character, + castId: person.cast_id, + creditId: person.credit_id, + gender: person.gender, + profile: formatTmdbImage({ value: person.profile_path }), + })) as Cast[]; + + const crew = tv.credits?.crew.map(person => ({ + id: person.id, + name: person.name, + job: person.job, + department: person.department, + creditId: person.credit_id, + gender: person.gender, + profile: formatTmdbImage({ value: person.profile_path }), + })) as Crew[]; + + const credits = { cast, crew } as Credits; + + parsedMovie = { ...parsedMovie, recommendations, credits }; + + return parsedMovie; +}; + +export default Details; diff --git a/src/domains/Tv/api/Popular/Params.ts b/src/domains/Tv/api/Popular/Params.ts new file mode 100644 index 0000000..d09e291 --- /dev/null +++ b/src/domains/Tv/api/Popular/Params.ts @@ -0,0 +1,3 @@ +export default interface Params { + page?: number; +} diff --git a/src/domains/Tv/api/Popular/RawResponse.ts b/src/domains/Tv/api/Popular/RawResponse.ts new file mode 100644 index 0000000..3be9079 --- /dev/null +++ b/src/domains/Tv/api/Popular/RawResponse.ts @@ -0,0 +1,13 @@ +export default interface RawResponse { + poster_path?: string; + overview: string; + first_air_date: string; + genre_ids: number[]; + id: number; + original_language: string; + original_name: string; + name: string; + backdrop_path?: string; + popularity: number; + vote_count: number; +} diff --git a/src/domains/Tv/api/Popular/Response.ts b/src/domains/Tv/api/Popular/Response.ts new file mode 100644 index 0000000..a858005 --- /dev/null +++ b/src/domains/Tv/api/Popular/Response.ts @@ -0,0 +1,14 @@ +export default interface Response { + poster?: string; + backdrop?: string; + overview: string; + releaseDate: string; + genreIds: number[]; + id: number; + originalName: string; + name: string; + popularity: number; + voteCount: number; + favorite: boolean; + mediaType: number; +} diff --git a/src/domains/Tv/api/Popular/index.ts b/src/domains/Tv/api/Popular/index.ts new file mode 100644 index 0000000..ad61887 --- /dev/null +++ b/src/domains/Tv/api/Popular/index.ts @@ -0,0 +1,50 @@ +import tmdb from 'services/api/tmdb'; + +import { formatDate, formatTmdbImage } from 'shared/utils'; + +import { Type } from 'domains/Favorites/enums'; +import Params from 'domains/Tv/api/Popular/Params'; +import RawResponse from 'domains/Tv/api/Popular/RawResponse'; +import Response from 'domains/Tv/api/Popular/Response'; + +const Popular = async (params?: Params): Promise => { + const response = await rawPopular(params); + + return parseResponse(response); +}; + +export const rawPopular = async (params?: Params): Promise => { + const response = await tmdb.get('/tv/popular', { + params: { page: params?.page }, + }); + + return response.data.results; +}; + +const parseResponse = (rawResponse: RawResponse[]): Response[] => { + let response = [] as Response[]; + + rawResponse.forEach(tv => { + const parsedMovie = { + overview: tv.overview, + genreIds: tv.genre_ids, + id: tv.id, + originalName: tv.original_name, + name: tv.name, + popularity: tv.popularity, + voteCount: tv.vote_count, + + releaseDate: formatDate({ value: tv.first_air_date }), + poster: formatTmdbImage({ value: tv.poster_path }), + backdrop: formatTmdbImage({ value: tv.backdrop_path }), + favorite: false, + mediaType: Type.TV, + } as Response; + + response = [...response, parsedMovie]; + }); + + return response; +}; + +export default Popular; diff --git a/src/domains/Tv/api/Recommendations/RawResponse.ts b/src/domains/Tv/api/Recommendations/RawResponse.ts new file mode 100644 index 0000000..9dad1d5 --- /dev/null +++ b/src/domains/Tv/api/Recommendations/RawResponse.ts @@ -0,0 +1,14 @@ +export default interface RawResponse { + poster_path?: string; + overview: string; + first_air_date: string; + genres_ids: number[]; + id: number; + original_language: string; + original_name: string; + name: string; + backdrop_path?: string; + popularity: number; + vote_count: number; + vote_average: number; +} diff --git a/src/domains/Tv/api/Recommendations/Response.ts b/src/domains/Tv/api/Recommendations/Response.ts new file mode 100644 index 0000000..a36b718 --- /dev/null +++ b/src/domains/Tv/api/Recommendations/Response.ts @@ -0,0 +1,15 @@ +export default interface Response { + poster?: string; + backdrop?: string; + id: number; + title: string; + favorite: boolean; + mediaType: number; + // overview: string; + // releaseDate: string; + // genresIds: number[]; + // originalTitle: string; + // popularity: number; + // voteCount: number; + // voteAverage: number; +} diff --git a/src/domains/Tv/api/index.ts b/src/domains/Tv/api/index.ts new file mode 100644 index 0000000..abc174d --- /dev/null +++ b/src/domains/Tv/api/index.ts @@ -0,0 +1,2 @@ +export { default as Details } from './Details'; +export { default as Popular } from './Popular'; diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 4e59c0c..79f59b4 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -3,7 +3,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useAuth } from 'domains/Auth/hooks'; import { useFavorite } from 'domains/Favorites/hooks'; import { NowPlaying, Popular } from 'domains/Movie/api'; +import { Popular as TvPopular } from 'domains/Tv/api'; import MovieResponse from 'domains/Movie/api/Popular/Response'; +import TvPopularResponse from 'domains/Tv/api/Popular/Response'; +import { Type } from 'domains/Favorites/enums'; import { ColumnLayout } from 'components'; import { Footer, Header, Highlights, MovieList } from 'containers'; @@ -12,25 +15,33 @@ import { HeaderBackground, ContentContainer } from './styles'; const Home: React.FC = () => { const [popularList, setPopularList] = useState([] as MovieResponse[]); const [nowPlayingList, setNowPlayingList] = useState([] as MovieResponse[]); + const [tvPopularList, setTvPopularList] = useState([] as TvPopularResponse[]); const [isFavoriteLoading, setIsFavoriteLoading] = useState(true); const { user } = useAuth(); const { Favorites, favoriteList } = useFavorite(); const getNowPlaying = useCallback(async () => { - const response = await NowPlaying(); + const response = await NowPlaying({ page: 1 }); setNowPlayingList(response); return response; }, []); const getPopular = useCallback(async () => { - const response = await Popular(); + const response = await Popular({ page: 1 }); setPopularList(response); return response; }, []); + const getTvPopular = useCallback(async () => { + const response = await TvPopular({ page: 1 }); + setTvPopularList(response); + + return response; + }, []); + const getFavorites = useCallback(async () => { setIsFavoriteLoading(true); const response = await Favorites(); @@ -42,11 +53,12 @@ const Home: React.FC = () => { useEffect(() => { getNowPlaying(); getPopular(); + getTvPopular(); if (user) { getFavorites(); } - }, [user, getNowPlaying, getPopular, getFavorites]); + }, [user, getNowPlaying, getPopular, getTvPopular, getFavorites]); return ( @@ -63,11 +75,19 @@ const Home: React.FC = () => { data={nowPlayingList} isLoading={nowPlayingList.length === 0} /> + {user && ( item.typeId === Type.MOVIE || item.typeId === Type.TV, + )} isLoading={isFavoriteLoading} message="Você ainda não possui favoritos." /> diff --git a/src/pages/Movie/index.tsx b/src/pages/Movie/index.tsx index c80ac0e..4bcab58 100644 --- a/src/pages/Movie/index.tsx +++ b/src/pages/Movie/index.tsx @@ -5,6 +5,8 @@ import Params from 'pages/Movie/dtos/Params'; import MovieDetails from 'domains/Movie/api/Details/Response'; import { Color } from 'shared/enums'; import { Details } from 'domains/Movie/api'; +import { useAuth } from 'domains/Auth/hooks'; +import { useFavorite } from 'domains/Favorites/hooks'; import { ColumnLayout, Container, Movie as Poster } from 'components'; import { Header, PersonList, MovieList, Footer } from 'containers'; @@ -32,6 +34,9 @@ const Movie: React.FC = () => { const [movie, setMovie] = useState({} as MovieDetails); const [isLoading, setIsLoading] = useState(false); + const { user } = useAuth(); + const { Favorites, favoriteList } = useFavorite(); + const getMovie = useCallback(async () => { try { setIsLoading(true); @@ -55,6 +60,14 @@ const Movie: React.FC = () => { getMovie(); }, [getMovie]); + useEffect(() => { + if (user) { + if (favoriteList?.length === 0) { + Favorites(); + } + } + }, []); // eslint-disable-line + return (
diff --git a/src/pages/Person/index.tsx b/src/pages/Person/index.tsx index f705e99..e4c4a3d 100644 --- a/src/pages/Person/index.tsx +++ b/src/pages/Person/index.tsx @@ -5,6 +5,8 @@ import Params from 'pages/Person/dtos/Params'; import PersonDetails from 'domains/Person/api/Details/Response'; import { Color } from 'shared/enums'; import { Details } from 'domains/Person/api'; +import { useAuth } from 'domains/Auth/hooks'; +import { useFavorite } from 'domains/Favorites/hooks'; import { ColumnLayout, Container, Profile } from 'components'; import { Header, MovieList, Footer, Filmography } from 'containers'; @@ -27,6 +29,9 @@ const Person: React.FC = () => { const [person, setMovie] = useState({} as PersonDetails); const [isLoading, setIsLoading] = useState(false); + const { user } = useAuth(); + const { Favorites, favoriteList } = useFavorite(); + const getPerson = useCallback(async () => { try { setIsLoading(true); @@ -50,6 +55,14 @@ const Person: React.FC = () => { getPerson(); }, [getPerson]); + useEffect(() => { + if (user) { + if (favoriteList?.length === 0) { + Favorites(); + } + } + }, []); // eslint-disable-line + return (
diff --git a/src/pages/Tv/dtos/Params.ts b/src/pages/Tv/dtos/Params.ts new file mode 100644 index 0000000..09eef17 --- /dev/null +++ b/src/pages/Tv/dtos/Params.ts @@ -0,0 +1,3 @@ +export default interface Params { + id: string; +} diff --git a/src/pages/Tv/index.tsx b/src/pages/Tv/index.tsx new file mode 100644 index 0000000..b763715 --- /dev/null +++ b/src/pages/Tv/index.tsx @@ -0,0 +1,125 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; + +import Params from 'pages/Tv/dtos/Params'; +import MovieDetails from 'domains/Tv/api/Details/Response'; +import { Color } from 'shared/enums'; +import { Details } from 'domains/Tv/api'; +import { useAuth } from 'domains/Auth/hooks'; +import { useFavorite } from 'domains/Favorites/hooks'; + +import { ColumnLayout, Container, Movie as Poster } from 'components'; +import { Header, PersonList, MovieList, Footer } from 'containers'; +import { + ContentContainer, + MovieContainer, + PosterContainer, + MovieDetailsContainer, + TitleContainer, + Title, + Subtitle, + OverviewContainer, + OverviewTitle, + Overview, + VoteAverage, + VoteAverageTitle, + Director, + DirectorTitle, + HeaderBackground, +} from './styles'; + +const Tv: React.FC = () => { + const { id } = useParams(); + const [movie, setMovie] = useState({} as MovieDetails); + const [isLoading, setIsLoading] = useState(false); + + const { user } = useAuth(); + const { Favorites, favoriteList } = useFavorite(); + + const getMovie = useCallback(async () => { + try { + setIsLoading(true); + const params = { + appendToResponse: 'videos,images,recommendations,keywords,credits', + }; + + const response = await Details(+id, params); + + setMovie(response); + return response; + } catch (error) { + console.log('getMovie -> error', error); + } finally { + setIsLoading(false); + } + }, [id]); + + useEffect(() => { + window.scrollTo(0, 0); + getMovie(); + }, [getMovie]); + + useEffect(() => { + if (user) { + if (favoriteList?.length === 0) { + Favorites(); + } + } + }, []); // eslint-disable-line + + return ( + +
+ + backdrop + + + + + + + + + + + {movie.title} + + {movie.releaseDate} | {movie.genresNames} | {movie.runtime} + + + + Sinopse + {movie.overview} + + + Votação do público:{' '} + {movie.voteAverage} + + + Diretor: + {movie.directorName} + + + + + + + + + +