├── src
├── constants
│ └── index.js
├── styles
│ └── global.css
├── components
│ ├── top-rated-movie.js
│ ├── progress-loader.js
│ ├── loader.js
│ ├── trending-movie.js
│ ├── movie-card.js
│ ├── upcoming-movie.js
│ └── cast.js
├── api
│ ├── axios.js
│ └── index.js
├── navigations
│ ├── app.navigation.js
│ └── tab.navigation.js
└── screens
│ ├── home.js
│ ├── search.js
│ ├── movie.js
│ └── person.js
├── assets
├── icon.png
├── logo.png
├── favicon.png
├── splash.png
├── not-found.png
├── adaptive-icon.png
└── loader.json
├── postcss.config.js
├── tailwind.config.js
├── babel.config.js
├── App.js
├── metro.config.js
├── app.json
├── package.json
└── README.md
/src/constants/index.js:
--------------------------------------------------------------------------------
1 | export const api_key = 'bb7d3261607b747d50a1918e43167936';
2 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samarbadriddin0v/movie-app/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samarbadriddin0v/movie-app/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samarbadriddin0v/movie-app/HEAD/assets/favicon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samarbadriddin0v/movie-app/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/assets/not-found.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samarbadriddin0v/movie-app/HEAD/assets/not-found.png
--------------------------------------------------------------------------------
/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samarbadriddin0v/movie-app/HEAD/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./src/**/*.{js,jsx,ts,tsx}'],
3 | theme: {
4 | extend: {},
5 | },
6 | plugins: [],
7 | };
8 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | plugins: ['nativewind/babel'],
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import AppNavigation from './src/navigations/app.navigation';
2 | import './src/styles/global.css';
3 |
4 | export default function App() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require('expo/metro-config');
2 |
3 | const config = getDefaultConfig(__dirname, {
4 | isCSSEnabled: true,
5 | });
6 |
7 | module.exports = config;
8 |
--------------------------------------------------------------------------------
/src/components/top-rated-movie.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Text, View } from 'react-native';
3 |
4 | export default function TopRatedMovie() {
5 | return (
6 |
7 | TopRatedMovie
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/api/axios.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const apiRequest = async (endpoint, params) => {
4 | const options = {
5 | method: 'GET',
6 | url: endpoint,
7 | params: params ? params : {},
8 | };
9 |
10 | try {
11 | const { data } = await axios.request(options);
12 | return data;
13 | } catch (error) {
14 | console.log('error', error);
15 | return error;
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/progress-loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dimensions, View } from 'react-native';
3 | import * as Progress from 'react-native-progress';
4 |
5 | const { width, height } = Dimensions.get('window');
6 |
7 | export default function ProgressLoader() {
8 | return (
9 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import AnimatedLoader from 'react-native-animated-loader';
4 |
5 | export default function Loader() {
6 | return (
7 |
14 | );
15 | }
16 |
17 | const styles = StyleSheet.create({
18 | lottie: {
19 | width: 100,
20 | height: 100,
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/trending-movie.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dimensions, View } from 'react-native';
3 | import Carousel from 'react-native-snap-carousel';
4 | import MovieCard from './movie-card';
5 |
6 | const { width } = Dimensions.get('window');
7 |
8 | export default function TrendingMovie({ trending }) {
9 | return (
10 |
11 | }
14 | firstItem={1}
15 | inactiveSlideOpacity={0.6}
16 | sliderWidth={width}
17 | itemWidth={width * 0.7}
18 | slideStyle={{ display: 'flex', alignItems: 'center' }}
19 | />
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "movie-app",
4 | "slug": "movie-app",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "userInterfaceStyle": "light",
9 | "splash": {
10 | "image": "./assets/splash.png",
11 | "resizeMode": "contain",
12 | "backgroundColor": "#ffffff"
13 | },
14 | "assetBundlePatterns": [
15 | "**/*"
16 | ],
17 | "ios": {
18 | "supportsTablet": true
19 | },
20 | "android": {
21 | "adaptiveIcon": {
22 | "foregroundImage": "./assets/adaptive-icon.png",
23 | "backgroundColor": "#ffffff"
24 | }
25 | },
26 | "web": {
27 | "favicon": "./assets/favicon.png"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/movie-card.js:
--------------------------------------------------------------------------------
1 | import { useNavigation } from '@react-navigation/native';
2 | import React from 'react';
3 | import {
4 | Dimensions,
5 | Image,
6 | TouchableWithoutFeedback,
7 | } from 'react-native';
8 | import { image500 } from '../api';
9 |
10 | const { width, height } = Dimensions.get('window');
11 |
12 | export default function MovieCard({ item }) {
13 | const navigation = useNavigation();
14 |
15 | return (
16 | navigation.navigate('Movie', item.id)}
18 | >
19 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/navigations/app.navigation.js:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native';
2 | import { createNativeStackNavigator } from '@react-navigation/native-stack';
3 | import React from 'react';
4 | import Home from '../screens/home';
5 | import Movie from '../screens/movie';
6 | import Person from '../screens/person';
7 | import Search from '../screens/search';
8 |
9 | const Stack = createNativeStackNavigator();
10 |
11 | export default function AppNavigation() {
12 | return (
13 |
14 |
15 |
20 |
25 |
30 |
35 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "movie-app",
3 | "version": "1.0.0",
4 | "main": "node_modules/expo/AppEntry.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web"
10 | },
11 | "dependencies": {
12 | "@react-navigation/bottom-tabs": "^6.5.8",
13 | "@react-navigation/native": "^6.1.7",
14 | "@react-navigation/native-stack": "^6.9.13",
15 | "axios": "^1.5.0",
16 | "expo": "^49.0.0",
17 | "expo-linear-gradient": "^12.3.0",
18 | "expo-status-bar": "~1.6.0",
19 | "lodash": "^4.17.21",
20 | "nativewind": "^2.0.11",
21 | "react": "18.2.0",
22 | "react-native": "0.72.4",
23 | "react-native-animated-loader": "^1.0.0",
24 | "react-native-heroicons": "^3.2.0",
25 | "react-native-progress": "^5.0.0",
26 | "react-native-safe-area-context": "4.6.3",
27 | "react-native-screens": "~3.22.0",
28 | "react-native-snap-carousel": "^3.9.1",
29 | "react-native-vector-icons": "^10.0.0"
30 | },
31 | "devDependencies": {
32 | "@babel/core": "^7.20.0",
33 | "tailwindcss": "3.3.2"
34 | },
35 | "private": true
36 | }
37 |
--------------------------------------------------------------------------------
/src/navigations/tab.navigation.js:
--------------------------------------------------------------------------------
1 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
2 | import { NavigationContainer } from '@react-navigation/native';
3 | import React from 'react';
4 | import Ionicons from 'react-native-vector-icons/Ionicons';
5 | import Detailed from '../screens/detailed';
6 | import Home from '../screens/home';
7 |
8 | const Tab = createBottomTabNavigator();
9 |
10 | export default function TabNavigation() {
11 | return (
12 |
13 | ({
15 | tabBarIcon: ({ focused, color, size }) => {
16 | let iconName;
17 |
18 | if (route.name === 'Home') {
19 | iconName = focused ? 'home' : 'home-outline';
20 | } else if (route.name === 'Details') {
21 | iconName = focused ? 'settings' : 'settings-outline';
22 | }
23 |
24 | return (
25 |
26 | );
27 | },
28 | tabBarActiveTintColor: 'crimson',
29 | tabBarInactiveTintColor: 'black',
30 | })}
31 | >
32 |
37 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/upcoming-movie.js:
--------------------------------------------------------------------------------
1 | import { useNavigation } from '@react-navigation/native';
2 | import React from 'react';
3 | import {
4 | Dimensions,
5 | Image,
6 | ScrollView,
7 | Text,
8 | TouchableWithoutFeedback,
9 | View,
10 | } from 'react-native';
11 | import { image185 } from '../api';
12 |
13 | const { width, height } = Dimensions.get('window');
14 |
15 | export default function UpcomingMovie({ upcoming, title }) {
16 | const navigation = useNavigation();
17 |
18 | return (
19 |
20 |
21 | {title}
22 |
23 |
24 |
29 | {upcoming.map(item => (
30 | navigation.navigate('Movie', item.id)}
33 | >
34 |
35 |
40 |
41 | {item.title.length > 22
42 | ? item.title.slice(0, 22) + '...'
43 | : item.title}
44 |
45 |
46 |
47 | ))}
48 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Movie API's application
2 |
3 | Introducing our Movie Application a cutting-edge mobile platform developed with React Native. Experience seamless navigation and stunning visual aesthetics thanks to our beautiful UI design. Our project boasts a robust architecture ensuring smooth functionality and efficient performance. TailwindCSS integration guarantees a responsive and visually appealing layout while shared components enhance collaboration and streamline development. Dive into the world of movies like never before with our user-friendly and visually captivating Movie Application.
4 |
5 | 🚀 Demo
6 |
7 | [https://youtu.be/wJxJ4VnC\_TY?si=kUHpEsLnGLgtQ4Xj](https://youtu.be/wJxJ4VnC_TY?si=kUHpEsLnGLgtQ4Xj)
8 |
9 | Project Screenshots:
10 |
11 |
12 |
13 |
14 |
15 | 🧐 Features
16 |
17 | Here're some of the project's best features:
18 |
19 | * Mobile application
20 | * IOS
21 | * Adnroid
22 | * Mobile developments
23 |
24 | 🛠️ Installation Steps:
25 |
26 | 1. Install all dependencies
27 |
28 | ```
29 | npm install | yarn install
30 | ```
31 |
32 | 2. Run application using expo metro
33 |
34 | ```
35 | npm start | yarn start
36 | ```
37 |
38 |
39 |
40 | 💻 Built with
41 |
42 | Technologies used in the project:
43 |
44 | * JavaScript
45 | * ReactJS
46 | * React Native
47 | * Expo
48 | * React Navigation
49 | * Custom theme
50 | * TailwindCSS
51 | * React Wind
52 | * IOS
53 | * Android
54 |
--------------------------------------------------------------------------------
/src/components/cast.js:
--------------------------------------------------------------------------------
1 | import { useNavigation } from '@react-navigation/native';
2 | import React from 'react';
3 | import {
4 | Image,
5 | ScrollView,
6 | Text,
7 | TouchableOpacity,
8 | View,
9 | } from 'react-native';
10 | import { image185 } from '../api';
11 |
12 | export default function Cast({ cast }) {
13 | const navigation = useNavigation();
14 |
15 | return (
16 |
17 |
18 | Actors
19 |
20 |
25 | {cast &&
26 | cast.map((person, idx) => (
27 | navigation.navigate('Person', person.id)}
31 | >
32 |
37 |
41 |
42 |
43 | {person?.character.length > 10
44 | ? person.character.slice(0, 10) + '...'
45 | : person?.character}
46 |
47 |
48 | {person?.original_name.length > 10
49 | ? person.original_name.slice(0, 10) + '...'
50 | : person?.original_name}
51 |
52 |
53 | ))}
54 |
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/api/index.js:
--------------------------------------------------------------------------------
1 | import { api_key } from '../constants';
2 | import { apiRequest } from './axios';
3 |
4 | const base_url = 'https://api.themoviedb.org/3';
5 |
6 | const trendingMovie = `${base_url}/trending/movie/day?api_key=${api_key}`;
7 | const upcomingMovie = `${base_url}/movie/upcoming?api_key=${api_key}`;
8 | const topRatedMovie = `${base_url}/movie/top_rated?api_key=${api_key}`;
9 | const popularMovie = `${base_url}/movie/popular?api_key=${api_key}`;
10 | const searchMovie = `${base_url}/search/movie?api_key=${api_key}`;
11 |
12 | const movieDetail = id =>
13 | `${base_url}/movie/${id}?api_key=${api_key}`;
14 | const movieCredits = id =>
15 | `${base_url}/movie/${id}/credits?api_key=${api_key}`;
16 | const similarMovie = id =>
17 | `${base_url}/movie/${id}/similar?api_key=${api_key}`;
18 |
19 | const personalDetail = id =>
20 | `${base_url}/person/${id}?api_key=${api_key}`;
21 | const personMovies = id =>
22 | `${base_url}/person/${id}/movie_credits?api_key=${api_key}`;
23 |
24 | export const fetchTrendingMovie = () => {
25 | return apiRequest(trendingMovie);
26 | };
27 |
28 | export const fetchUpcomingMovie = () => {
29 | return apiRequest(upcomingMovie);
30 | };
31 |
32 | export const fetchTopRatedMovie = () => {
33 | return apiRequest(topRatedMovie);
34 | };
35 |
36 | export const fetchPopularMovie = () => {
37 | return apiRequest(popularMovie);
38 | };
39 |
40 | export const fetchMovieDetail = id => {
41 | return apiRequest(movieDetail(id));
42 | };
43 |
44 | export const fetchMovieCredits = id => {
45 | return apiRequest(movieCredits(id));
46 | };
47 |
48 | export const fetchSimilarMovie = id => {
49 | return apiRequest(similarMovie(id));
50 | };
51 |
52 | export const fetchPersonDetail = id => {
53 | return apiRequest(personalDetail(id));
54 | };
55 |
56 | export const fetchPersonMovies = id => {
57 | return apiRequest(personMovies(id));
58 | };
59 |
60 | export const fetchSearchMovie = params => {
61 | return apiRequest(searchMovie, params);
62 | };
63 |
64 | export const image500 = posterPath => {
65 | return posterPath
66 | ? 'https://image.tmdb.org/t/p/w500' + posterPath
67 | : null;
68 | };
69 |
70 | export const image342 = posterPath => {
71 | return posterPath
72 | ? 'https://image.tmdb.org/t/p/w342' + posterPath
73 | : null;
74 | };
75 |
76 | export const image185 = posterPath => {
77 | return posterPath
78 | ? 'https://image.tmdb.org/t/p/w185' + posterPath
79 | : null;
80 | };
81 |
--------------------------------------------------------------------------------
/src/screens/home.js:
--------------------------------------------------------------------------------
1 | import { useNavigation } from '@react-navigation/native';
2 | import { StatusBar } from 'expo-status-bar';
3 | import React, { useEffect, useState } from 'react';
4 | import {
5 | Image,
6 | ScrollView,
7 | TouchableOpacity,
8 | View,
9 | } from 'react-native';
10 | import { MagnifyingGlassIcon } from 'react-native-heroicons/outline';
11 | import { SafeAreaView } from 'react-native-safe-area-context';
12 | import {
13 | fetchPopularMovie,
14 | fetchTopRatedMovie,
15 | fetchTrendingMovie,
16 | fetchUpcomingMovie,
17 | } from '../api';
18 | import Loader from '../components/loader';
19 | import TrendingMovie from '../components/trending-movie';
20 | import UpcomingMovie from '../components/upcoming-movie';
21 |
22 | export default function Home() {
23 | const [trending, setTrending] = useState([]);
24 | const [upcoming, setUpcoming] = useState([]);
25 | const [topRated, setTopRated] = useState([]);
26 | const [popular, setPopular] = useState([]);
27 | const [isLoading, setIsLoading] = useState(true);
28 |
29 | const navigation = useNavigation();
30 |
31 | useEffect(() => {
32 | getTrendingMovie();
33 | getUpcomingMovie();
34 | getTopRatedMovie();
35 | getPopularMovie();
36 | }, []);
37 |
38 | const getTrendingMovie = async () => {
39 | const data = await fetchTrendingMovie();
40 | data.results && setTrending(data.results);
41 | setIsLoading(false);
42 | };
43 |
44 | const getUpcomingMovie = async () => {
45 | const data = await fetchUpcomingMovie();
46 | data.results && setUpcoming(data.results);
47 | };
48 |
49 | const getTopRatedMovie = async () => {
50 | const data = await fetchTopRatedMovie();
51 | data.results && setTopRated(data.results);
52 | };
53 |
54 | const getPopularMovie = async () => {
55 | const data = await fetchPopularMovie();
56 | data.results && setPopular(data.results);
57 | };
58 |
59 | return (
60 |
61 |
62 |
63 |
68 |
69 | navigation.navigate('Search')}
71 | >
72 |
77 |
78 |
79 |
80 |
81 | {isLoading ? (
82 |
83 | ) : (
84 |
88 | {trending.length > 0 && (
89 |
90 | )}
91 | {upcoming.length > 0 && (
92 |
96 | )}
97 | {upcoming.length > 0 && (
98 |
102 | )}
103 | {popular.length > 0 && (
104 |
108 | )}
109 | {topRated.length > 0 && (
110 |
111 | )}
112 |
113 | )}
114 |
115 | );
116 | }
117 |
--------------------------------------------------------------------------------
/src/screens/search.js:
--------------------------------------------------------------------------------
1 | import { useNavigation } from '@react-navigation/native';
2 | import { debounce } from 'lodash';
3 | import React, { useCallback, useState } from 'react';
4 | import {
5 | Dimensions,
6 | Image,
7 | ScrollView,
8 | Text,
9 | TextInput,
10 | TouchableOpacity,
11 | TouchableWithoutFeedback,
12 | View,
13 | } from 'react-native';
14 | import { XMarkIcon } from 'react-native-heroicons/outline';
15 | import { SafeAreaView } from 'react-native-safe-area-context';
16 | import { fetchSearchMovie, image185 } from '../api';
17 | import Loader from '../components/loader';
18 |
19 | const { width, height } = Dimensions.get('window');
20 |
21 | export default function Search() {
22 | const [results, setResults] = useState([]);
23 | const [isLoading, setIsLoading] = useState(false);
24 |
25 | const navigation = useNavigation();
26 |
27 | const handleSearch = searchText => {
28 | if (searchText && searchText.length > 3) {
29 | setIsLoading(true);
30 | fetchSearchMovie({
31 | query: searchText,
32 | include_adult: false,
33 | page: '1',
34 | }).then(data => {
35 | setIsLoading(false);
36 | setResults(data.results);
37 | });
38 | } else {
39 | setResults([]);
40 | setIsLoading(false);
41 | }
42 | };
43 |
44 | const handleTextDobounce = useCallback(
45 | debounce(handleSearch, 400),
46 | []
47 | );
48 |
49 | return (
50 |
51 |
56 |
64 | navigation.navigate('Home')}
66 | className={'rounded-full p-3 m-1 bg-neutral-400'}
67 | >
68 |
69 |
70 |
71 |
72 | {isLoading ? (
73 |
74 | ) : results.length > 0 ? (
75 |
80 |
81 | Results ({results.length})
82 |
83 |
84 | {results?.map(item => (
85 |
86 |
87 |
95 |
96 | {item.title.length > 22
97 | ? item.title.slice(0, 22) + '...'
98 | : item.title}
99 |
100 |
101 |
102 | ))}
103 |
104 |
105 | ) : (
106 |
107 |
111 |
112 | Movies not found
113 |
114 |
115 | )}
116 |
117 | );
118 | }
119 |
--------------------------------------------------------------------------------
/src/screens/movie.js:
--------------------------------------------------------------------------------
1 | import { useNavigation, useRoute } from '@react-navigation/native';
2 | import { LinearGradient } from 'expo-linear-gradient';
3 | import React, { useEffect, useState } from 'react';
4 | import {
5 | Dimensions,
6 | Image,
7 | ScrollView,
8 | Text,
9 | TouchableOpacity,
10 | View,
11 | } from 'react-native';
12 | import { ChevronLeftIcon } from 'react-native-heroicons/outline';
13 | import { HeartIcon } from 'react-native-heroicons/solid';
14 | import { SafeAreaView } from 'react-native-safe-area-context';
15 | import {
16 | fetchMovieCredits,
17 | fetchMovieDetail,
18 | fetchSimilarMovie,
19 | image500,
20 | } from '../api';
21 | import Cast from '../components/cast';
22 | import Loader from '../components/loader';
23 | import UpcomingMovie from '../components/upcoming-movie';
24 |
25 | const { width, height } = Dimensions.get('window');
26 |
27 | export default function Movie() {
28 | const [isFavourite, setIsFavourite] = useState(false);
29 | const [movie, setMovie] = useState({});
30 | const [cast, setCast] = useState([]);
31 | const [similarMovie, setSimilarMovie] = useState([]);
32 | const [isLoading, setIsLoading] = useState(true);
33 |
34 | const navigation = useNavigation();
35 | const { params: id } = useRoute();
36 |
37 | useEffect(() => {
38 | getMovieDetail();
39 | getMovieCredits();
40 | getSimilarMovie();
41 | }, [id]);
42 |
43 | const getMovieDetail = async () => {
44 | const data = await fetchMovieDetail(id);
45 | setMovie(data);
46 | setIsLoading(false);
47 | };
48 |
49 | const getMovieCredits = async () => {
50 | const data = await fetchMovieCredits(id);
51 | setCast(data.cast);
52 | };
53 |
54 | const getSimilarMovie = async () => {
55 | const data = await fetchSimilarMovie(id);
56 | setSimilarMovie(data.results);
57 | };
58 |
59 | return (
60 |
64 |
65 |
70 | navigation.goBack()}>
71 |
76 |
77 |
78 | setIsFavourite(prev => !prev)}
80 | >
81 |
86 |
87 |
88 | {isLoading ? (
89 |
90 | ) : (
91 |
92 |
96 |
107 |
108 | )}
109 |
110 |
111 |
112 |
117 | {movie?.title}
118 |
119 | {movie?.id ? (
120 |
125 | {movie?.status} • {movie?.release_date?.split('-')[0]} •{' '}
126 | {movie?.runtime} min
127 |
128 | ) : null}
129 |
130 |
131 | {movie?.genres?.map((genre, idx) => (
132 |
138 | {genre?.name}{' '}
139 | {idx + 1 !== movie.genres.length ? '•' : null}
140 |
141 | ))}
142 |
143 |
144 |
145 | {movie?.overview}
146 |
147 |
148 |
149 | {movie?.id && cast.length > 0 && }
150 |
151 | {movie?.id && similarMovie.length > 0 && (
152 |
156 | )}
157 |
158 | );
159 | }
160 |
--------------------------------------------------------------------------------
/src/screens/person.js:
--------------------------------------------------------------------------------
1 | import { useNavigation, useRoute } from '@react-navigation/native';
2 | import React, { useEffect } from 'react';
3 | import {
4 | Dimensions,
5 | Image,
6 | ScrollView,
7 | Text,
8 | TouchableOpacity,
9 | View,
10 | } from 'react-native';
11 | import { ChevronLeftIcon } from 'react-native-heroicons/outline';
12 | import { HeartIcon } from 'react-native-heroicons/solid';
13 | import { SafeAreaView } from 'react-native-safe-area-context';
14 | import { useState } from 'react/cjs/react.development';
15 | import {
16 | fetchPersonDetail,
17 | fetchPersonMovies,
18 | image342,
19 | } from '../api';
20 | import Loader from '../components/loader';
21 | import UpcomingMovie from '../components/upcoming-movie';
22 |
23 | const { width, height } = Dimensions.get('window');
24 |
25 | export default function Person() {
26 | const [isLoading, setIsLoading] = useState(true);
27 | const [person, setPerson] = useState({});
28 | const [personMovies, setPersonMovies] = useState([]);
29 | const [isFavourite, setIsFavourite] = useState(false);
30 |
31 | const { params: id } = useRoute();
32 | const navigation = useNavigation();
33 |
34 | useEffect(() => {
35 | getPersonDetail();
36 | getPersonMovies();
37 | }, [id]);
38 |
39 | const getPersonDetail = async () => {
40 | const data = await fetchPersonDetail(id);
41 | setPerson(data);
42 | setIsLoading(false);
43 | };
44 |
45 | const getPersonMovies = async () => {
46 | const data = await fetchPersonMovies(id);
47 | setPersonMovies(data.cast);
48 | };
49 |
50 | return (
51 |
55 |
60 | navigation.goBack()}>
61 |
66 |
67 |
68 | setIsFavourite(prev => !prev)}
70 | >
71 |
76 |
77 |
78 | {isLoading ? (
79 |
80 | ) : (
81 |
82 |
91 |
92 |
96 |
97 |
98 |
99 |
102 | {person?.name}
103 |
104 |
107 | {person?.place_of_birth}
108 |
109 |
110 |
111 |
116 |
121 |
122 | Gender
123 |
124 |
125 | {person?.gender === 1 ? 'Female' : 'Male'}
126 |
127 |
128 |
133 |
134 | Birthday
135 |
136 |
137 | {person?.birthday}
138 |
139 |
140 |
141 |
142 | Known for
143 |
144 |
145 | {person?.known_for_department}
146 |
147 |
148 |
149 |
150 | Popularity
151 |
152 |
153 | {person?.popularity?.toFixed(2)} %
154 |
155 |
156 |
157 |
158 |
159 | Biography
160 |
161 | {person?.biography}
162 |
163 |
164 | {person?.id && personMovies.length > 0 && (
165 |
166 | )}
167 |
168 | )}
169 |
170 | );
171 | }
172 |
--------------------------------------------------------------------------------
/assets/loader.json:
--------------------------------------------------------------------------------
1 | {
2 | "v": "5.8.1",
3 | "fr": 29.9700012207031,
4 | "ip": 0,
5 | "op": 57.0000023216576,
6 | "w": 1080,
7 | "h": 1080,
8 | "nm": "Comp 1",
9 | "ddd": 0,
10 | "assets": [],
11 | "layers": [
12 | {
13 | "ddd": 0,
14 | "ind": 5,
15 | "ty": 4,
16 | "nm": "Load3 Outlines",
17 | "sr": 1,
18 | "ks": {
19 | "o": { "a": 0, "k": 100, "ix": 11 },
20 | "r": { "a": 0, "k": 0, "ix": 10 },
21 | "p": { "a": 0, "k": [540, 539, 0], "ix": 2, "l": 2 },
22 | "a": { "a": 0, "k": [1518, 1004.5, 0], "ix": 1, "l": 2 },
23 | "s": { "a": 0, "k": [51.5, 51.5, 100], "ix": 6, "l": 2 }
24 | },
25 | "ao": 0,
26 | "shapes": [
27 | {
28 | "ty": "gr",
29 | "it": [
30 | {
31 | "ind": 0,
32 | "ty": "sh",
33 | "ix": 1,
34 | "ks": {
35 | "a": 0,
36 | "k": {
37 | "i": [
38 | [0, 190.724],
39 | [0, 233.258],
40 | [-193.633, 0],
41 | [0, 0],
42 | [0, -193.632],
43 | [0, 0],
44 | [0, 0],
45 | [190.724, 0],
46 | [0, 0]
47 | ],
48 | "o": [
49 | [0, 0],
50 | [0, -193.632],
51 | [0, 0],
52 | [193.632, 0],
53 | [0, 0],
54 | [0, 0],
55 | [0, 190.724],
56 | [0, 0],
57 | [-190.724, 0]
58 | ],
59 | "v": [
60 | [-350.603, 339.305],
61 | [-350.603, -334.046],
62 | [0, -684.641],
63 | [0.001, -684.641],
64 | [350.603, -334.046],
65 | [350.603, 334.722],
66 | [350.603, 339.305],
67 | [5.267, 684.641],
68 | [-5.267, 684.641]
69 | ],
70 | "c": true
71 | },
72 | "ix": 2
73 | },
74 | "nm": "Path 1",
75 | "mn": "ADBE Vector Shape - Group",
76 | "hd": false
77 | },
78 | {
79 | "ty": "tm",
80 | "s": {
81 | "a": 1,
82 | "k": [
83 | {
84 | "i": { "x": [0.667], "y": [1] },
85 | "o": { "x": [0.333], "y": [0] },
86 | "t": 7,
87 | "s": [50]
88 | },
89 | { "t": 37.0000015070409, "s": [0] }
90 | ],
91 | "ix": 1
92 | },
93 | "e": {
94 | "a": 1,
95 | "k": [
96 | {
97 | "i": { "x": [0.667], "y": [1] },
98 | "o": { "x": [0.333], "y": [0] },
99 | "t": 7,
100 | "s": [69]
101 | },
102 | { "t": 37.0000015070409, "s": [19] }
103 | ],
104 | "ix": 2
105 | },
106 | "o": { "a": 0, "k": 0, "ix": 3 },
107 | "m": 1,
108 | "ix": 2,
109 | "nm": "Trim Paths 1",
110 | "mn": "ADBE Vector Filter - Trim",
111 | "hd": false
112 | },
113 | {
114 | "ty": "st",
115 | "c": {
116 | "a": 1,
117 | "k": [
118 | {
119 | "i": { "x": [0.667], "y": [1] },
120 | "o": { "x": [0.333], "y": [0] },
121 | "t": 7,
122 | "s": [0.851, 0.8784, 0.902, 1]
123 | },
124 | {
125 | "t": 37.0000015070409,
126 | "s": [0.0314, 0.3412, 0.6275, 1]
127 | }
128 | ],
129 | "ix": 3
130 | },
131 | "o": { "a": 0, "k": 100, "ix": 4 },
132 | "w": { "a": 0, "k": 150, "ix": 5 },
133 | "lc": 2,
134 | "lj": 1,
135 | "ml": 10,
136 | "bm": 0,
137 | "nm": "Stroke 1",
138 | "mn": "ADBE Vector Graphic - Stroke",
139 | "hd": false
140 | },
141 | {
142 | "ty": "tr",
143 | "p": { "a": 0, "k": [1327.382, 1042.938], "ix": 2 },
144 | "a": { "a": 0, "k": [0, 0], "ix": 1 },
145 | "s": { "a": 0, "k": [100, 100], "ix": 3 },
146 | "r": { "a": 0, "k": 0, "ix": 6 },
147 | "o": { "a": 0, "k": 100, "ix": 7 },
148 | "sk": { "a": 0, "k": 0, "ix": 4 },
149 | "sa": { "a": 0, "k": 0, "ix": 5 },
150 | "nm": "Transform"
151 | }
152 | ],
153 | "nm": "Group 4",
154 | "np": 3,
155 | "cix": 2,
156 | "bm": 0,
157 | "ix": 1,
158 | "mn": "ADBE Vector Group",
159 | "hd": false
160 | }
161 | ],
162 | "ip": 0,
163 | "op": 58.0000023623884,
164 | "st": -38.0000015477717,
165 | "bm": 0
166 | },
167 | {
168 | "ddd": 0,
169 | "ind": 6,
170 | "ty": 4,
171 | "nm": "Load3 Outlines",
172 | "sr": 1,
173 | "ks": {
174 | "o": { "a": 0, "k": 100, "ix": 11 },
175 | "r": { "a": 0, "k": 180, "ix": 10 },
176 | "p": { "a": 0, "k": [344, 580, 0], "ix": 2, "l": 2 },
177 | "a": { "a": 0, "k": [1518, 1004.5, 0], "ix": 1, "l": 2 },
178 | "s": { "a": 0, "k": [51.5, 51.5, 100], "ix": 6, "l": 2 }
179 | },
180 | "ao": 0,
181 | "shapes": [
182 | {
183 | "ty": "gr",
184 | "it": [
185 | {
186 | "ind": 0,
187 | "ty": "sh",
188 | "ix": 1,
189 | "ks": {
190 | "a": 0,
191 | "k": {
192 | "i": [
193 | [0, 190.724],
194 | [0, 233.258],
195 | [-193.633, 0],
196 | [0, 0],
197 | [0, -193.632],
198 | [0, 0],
199 | [0, 0],
200 | [190.724, 0],
201 | [0, 0]
202 | ],
203 | "o": [
204 | [0, 0],
205 | [0, -193.632],
206 | [0, 0],
207 | [193.632, 0],
208 | [0, 0],
209 | [0, 0],
210 | [0, 190.724],
211 | [0, 0],
212 | [-190.724, 0]
213 | ],
214 | "v": [
215 | [-350.603, 339.305],
216 | [-350.603, -334.046],
217 | [0, -684.641],
218 | [0.001, -684.641],
219 | [350.603, -334.046],
220 | [350.603, 334.722],
221 | [350.603, 339.305],
222 | [5.267, 684.641],
223 | [-5.267, 684.641]
224 | ],
225 | "c": true
226 | },
227 | "ix": 2
228 | },
229 | "nm": "Path 1",
230 | "mn": "ADBE Vector Shape - Group",
231 | "hd": false
232 | },
233 | {
234 | "ty": "st",
235 | "c": {
236 | "a": 1,
237 | "k": [
238 | {
239 | "i": { "x": [0.667], "y": [1] },
240 | "o": { "x": [0.333], "y": [0] },
241 | "t": 7,
242 | "s": [0.0314, 0.3412, 0.6275, 1]
243 | },
244 | {
245 | "t": 37.0000015070409,
246 | "s": [0.3556, 0.659, 0.9244, 1]
247 | }
248 | ],
249 | "ix": 3
250 | },
251 | "o": { "a": 0, "k": 100, "ix": 4 },
252 | "w": { "a": 0, "k": 150, "ix": 5 },
253 | "lc": 2,
254 | "lj": 1,
255 | "ml": 10,
256 | "bm": 0,
257 | "nm": "Stroke 1",
258 | "mn": "ADBE Vector Graphic - Stroke",
259 | "hd": false
260 | },
261 | {
262 | "ty": "tr",
263 | "p": { "a": 0, "k": [1327.382, 1042.938], "ix": 2 },
264 | "a": { "a": 0, "k": [0, 0], "ix": 1 },
265 | "s": { "a": 0, "k": [100, 100], "ix": 3 },
266 | "r": { "a": 0, "k": 0, "ix": 6 },
267 | "o": { "a": 0, "k": 100, "ix": 7 },
268 | "sk": { "a": 0, "k": 0, "ix": 4 },
269 | "sa": { "a": 0, "k": 0, "ix": 5 },
270 | "nm": "Transform"
271 | }
272 | ],
273 | "nm": "Group 2",
274 | "np": 2,
275 | "cix": 2,
276 | "bm": 0,
277 | "ix": 1,
278 | "mn": "ADBE Vector Group",
279 | "hd": false
280 | },
281 | {
282 | "ty": "tm",
283 | "s": {
284 | "a": 1,
285 | "k": [
286 | {
287 | "i": { "x": [0.667], "y": [1] },
288 | "o": { "x": [0.333], "y": [0] },
289 | "t": 7,
290 | "s": [50]
291 | },
292 | { "t": 37.0000015070409, "s": [0] }
293 | ],
294 | "ix": 1
295 | },
296 | "e": {
297 | "a": 1,
298 | "k": [
299 | {
300 | "i": { "x": [0.667], "y": [1] },
301 | "o": { "x": [0.333], "y": [0] },
302 | "t": 7,
303 | "s": [69]
304 | },
305 | { "t": 37.0000015070409, "s": [19] }
306 | ],
307 | "ix": 2
308 | },
309 | "o": { "a": 0, "k": 0, "ix": 3 },
310 | "m": 1,
311 | "ix": 2,
312 | "nm": "Trim Paths 1",
313 | "mn": "ADBE Vector Filter - Trim",
314 | "hd": false
315 | }
316 | ],
317 | "ip": 0,
318 | "op": 58.0000023623884,
319 | "st": -48.0000019550801,
320 | "bm": 0
321 | },
322 | {
323 | "ddd": 0,
324 | "ind": 7,
325 | "ty": 4,
326 | "nm": "Load3 Outlines",
327 | "sr": 1,
328 | "ks": {
329 | "o": { "a": 0, "k": 100, "ix": 11 },
330 | "r": { "a": 0, "k": 0, "ix": 10 },
331 | "p": { "a": 0, "k": [799.136, 559.345, 0], "ix": 2, "l": 2 },
332 | "a": {
333 | "a": 0,
334 | "k": [2021.177, 1040.875, 0],
335 | "ix": 1,
336 | "l": 2
337 | },
338 | "s": { "a": 0, "k": [51.5, 51.5, 100], "ix": 6, "l": 2 }
339 | },
340 | "ao": 0,
341 | "shapes": [
342 | {
343 | "ty": "gr",
344 | "it": [
345 | {
346 | "ind": 0,
347 | "ty": "sh",
348 | "ix": 1,
349 | "ks": {
350 | "a": 0,
351 | "k": {
352 | "i": [
353 | [0, 0],
354 | [-192.192, 0],
355 | [0, 0],
356 | [0, 192.192],
357 | [0, 0],
358 | [0, 0],
359 | [190.767, 0],
360 | [0, 0],
361 | [0, -190.771]
362 | ],
363 | "o": [
364 | [0, 192.192],
365 | [0, 0],
366 | [192.192, 0],
367 | [0, 0],
368 | [0, 0],
369 | [0, -190.768],
370 | [0, 0],
371 | [-190.77, 0],
372 | [0, 0]
373 | ],
374 | "v": [
375 | [-347.995, 336.15],
376 | [0, 684.141],
377 | [0, 684.141],
378 | [347.995, 336.15],
379 | [347.995, -334.141],
380 | [347.995, -338.725],
381 | [2.58, -684.141],
382 | [-2.575, -684.141],
383 | [-347.995, -338.72]
384 | ],
385 | "c": true
386 | },
387 | "ix": 2
388 | },
389 | "nm": "Path 1",
390 | "mn": "ADBE Vector Shape - Group",
391 | "hd": false
392 | },
393 | {
394 | "ty": "tm",
395 | "s": {
396 | "a": 1,
397 | "k": [
398 | {
399 | "i": { "x": [0.667], "y": [1] },
400 | "o": { "x": [0.333], "y": [0] },
401 | "t": 21,
402 | "s": [81]
403 | },
404 | { "t": 51.0000020772726, "s": [31] }
405 | ],
406 | "ix": 1
407 | },
408 | "e": {
409 | "a": 1,
410 | "k": [
411 | {
412 | "i": { "x": [0.667], "y": [1] },
413 | "o": { "x": [0.333], "y": [0] },
414 | "t": 21,
415 | "s": [100]
416 | },
417 | { "t": 51.0000020772726, "s": [50] }
418 | ],
419 | "ix": 2
420 | },
421 | "o": { "a": 0, "k": 0, "ix": 3 },
422 | "m": 1,
423 | "ix": 2,
424 | "nm": "Trim Paths 1",
425 | "mn": "ADBE Vector Filter - Trim",
426 | "hd": false
427 | },
428 | {
429 | "ty": "st",
430 | "c": {
431 | "a": 1,
432 | "k": [
433 | {
434 | "i": { "x": [0.667], "y": [1] },
435 | "o": { "x": [0.333], "y": [0] },
436 | "t": 21,
437 | "s": [0.3124, 0.7929, 0.9676, 1]
438 | },
439 | {
440 | "t": 51.0000020772726,
441 | "s": [0.2666, 0.6436, 0.9734, 1]
442 | }
443 | ],
444 | "ix": 3
445 | },
446 | "o": { "a": 0, "k": 100, "ix": 4 },
447 | "w": { "a": 0, "k": 150, "ix": 5 },
448 | "lc": 2,
449 | "lj": 1,
450 | "ml": 10,
451 | "bm": 0,
452 | "nm": "Stroke 1",
453 | "mn": "ADBE Vector Graphic - Stroke",
454 | "hd": false
455 | },
456 | {
457 | "ty": "tr",
458 | "p": { "a": 0, "k": [1673.182, 1042.438], "ix": 2 },
459 | "a": { "a": 0, "k": [0, 0], "ix": 1 },
460 | "s": { "a": 0, "k": [100, 100], "ix": 3 },
461 | "r": { "a": 0, "k": 0, "ix": 6 },
462 | "o": { "a": 0, "k": 100, "ix": 7 },
463 | "sk": { "a": 0, "k": 0, "ix": 4 },
464 | "sa": { "a": 0, "k": 0, "ix": 5 },
465 | "nm": "Transform"
466 | }
467 | ],
468 | "nm": "Group 1",
469 | "np": 3,
470 | "cix": 2,
471 | "bm": 0,
472 | "ix": 1,
473 | "mn": "ADBE Vector Group",
474 | "hd": false
475 | }
476 | ],
477 | "ip": 0,
478 | "op": 58.0000023623884,
479 | "st": -42.0000017106951,
480 | "bm": 0
481 | },
482 | {
483 | "ddd": 0,
484 | "ind": 8,
485 | "ty": 4,
486 | "nm": "Load3 Outlines",
487 | "sr": 1,
488 | "ks": {
489 | "o": { "a": 0, "k": 100, "ix": 11 },
490 | "r": { "a": 0, "k": -180, "ix": 10 },
491 | "p": { "a": 0, "k": [440.864, 560.955, 0], "ix": 2, "l": 2 },
492 | "a": {
493 | "a": 0,
494 | "k": [2021.177, 1020.875, 0],
495 | "ix": 1,
496 | "l": 2
497 | },
498 | "s": { "a": 0, "k": [51.5, 51.5, 100], "ix": 6, "l": 2 }
499 | },
500 | "ao": 0,
501 | "shapes": [
502 | {
503 | "ty": "gr",
504 | "it": [
505 | {
506 | "ind": 0,
507 | "ty": "sh",
508 | "ix": 1,
509 | "ks": {
510 | "a": 0,
511 | "k": {
512 | "i": [
513 | [0, 0],
514 | [-192.192, 0],
515 | [0, 0],
516 | [0, 192.192],
517 | [0, 0],
518 | [0, 0],
519 | [190.767, 0],
520 | [0, 0],
521 | [0, -190.771]
522 | ],
523 | "o": [
524 | [0, 192.192],
525 | [0, 0],
526 | [192.192, 0],
527 | [0, 0],
528 | [0, 0],
529 | [0, -190.768],
530 | [0, 0],
531 | [-190.77, 0],
532 | [0, 0]
533 | ],
534 | "v": [
535 | [-347.995, 336.15],
536 | [0, 684.141],
537 | [0, 684.141],
538 | [347.995, 336.15],
539 | [347.995, -334.141],
540 | [347.995, -338.725],
541 | [2.58, -684.141],
542 | [-2.575, -684.141],
543 | [-347.995, -338.72]
544 | ],
545 | "c": true
546 | },
547 | "ix": 2
548 | },
549 | "nm": "Path 1",
550 | "mn": "ADBE Vector Shape - Group",
551 | "hd": false
552 | },
553 | {
554 | "ty": "tm",
555 | "s": {
556 | "a": 1,
557 | "k": [
558 | {
559 | "i": { "x": [0.667], "y": [1] },
560 | "o": { "x": [0.333], "y": [0] },
561 | "t": 21,
562 | "s": [81]
563 | },
564 | { "t": 51.0000020772726, "s": [31] }
565 | ],
566 | "ix": 1
567 | },
568 | "e": {
569 | "a": 1,
570 | "k": [
571 | {
572 | "i": { "x": [0.667], "y": [1] },
573 | "o": { "x": [0.333], "y": [0] },
574 | "t": 21,
575 | "s": [100]
576 | },
577 | { "t": 51.0000020772726, "s": [50] }
578 | ],
579 | "ix": 2
580 | },
581 | "o": { "a": 0, "k": 0, "ix": 3 },
582 | "m": 1,
583 | "ix": 2,
584 | "nm": "Trim Paths 1",
585 | "mn": "ADBE Vector Filter - Trim",
586 | "hd": false
587 | },
588 | {
589 | "ty": "st",
590 | "c": {
591 | "a": 1,
592 | "k": [
593 | {
594 | "i": { "x": [0.667], "y": [1] },
595 | "o": { "x": [0.333], "y": [0] },
596 | "t": 21,
597 | "s": [0.2666, 0.6436, 0.9734, 1]
598 | },
599 | {
600 | "t": 51.0000020772726,
601 | "s": [0.6902, 0.9098, 0.9922, 1]
602 | }
603 | ],
604 | "ix": 3
605 | },
606 | "o": { "a": 0, "k": 100, "ix": 4 },
607 | "w": { "a": 0, "k": 150, "ix": 5 },
608 | "lc": 2,
609 | "lj": 1,
610 | "ml": 10,
611 | "bm": 0,
612 | "nm": "Stroke 1",
613 | "mn": "ADBE Vector Graphic - Stroke",
614 | "hd": false
615 | },
616 | {
617 | "ty": "tr",
618 | "p": { "a": 0, "k": [1673.182, 1022.438], "ix": 2 },
619 | "a": { "a": 0, "k": [0, 0], "ix": 1 },
620 | "s": { "a": 0, "k": [100, 100], "ix": 3 },
621 | "r": { "a": 0, "k": 0, "ix": 6 },
622 | "o": { "a": 0, "k": 100, "ix": 7 },
623 | "sk": { "a": 0, "k": 0, "ix": 4 },
624 | "sa": { "a": 0, "k": 0, "ix": 5 },
625 | "nm": "Transform"
626 | }
627 | ],
628 | "nm": "Group 3",
629 | "np": 3,
630 | "cix": 2,
631 | "bm": 0,
632 | "ix": 1,
633 | "mn": "ADBE Vector Group",
634 | "hd": false
635 | }
636 | ],
637 | "ip": 0,
638 | "op": 58.0000023623884,
639 | "st": -42.0000017106951,
640 | "bm": 0
641 | }
642 | ],
643 | "markers": []
644 | }
645 |
--------------------------------------------------------------------------------