├── 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 | project-screenshot 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 | --------------------------------------------------------------------------------