forked from lucas.delanier/MovieFinder
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
16 KiB
478 lines
16 KiB
import * as React from 'react';
|
|
import {TouchableOpacity, ScrollView, View, Text, StyleSheet, Image, SafeAreaView, FlatList} from 'react-native';
|
|
import {RootStackScreenProps} from "../types";
|
|
import {useSafeAreaInsets} from "react-native-safe-area-context";
|
|
import Movie from "../model/Movie";
|
|
import {LinearGradient} from 'expo-linear-gradient';
|
|
import {useEffect, useState} from "react";
|
|
import config from "../constants/config";
|
|
import YoutubeIframe from "react-native-youtube-iframe";
|
|
import Ionicons from "@expo/vector-icons/Ionicons";
|
|
import MinimalMovie from "../model/MinimalMovie";
|
|
import Review from "../model/review";
|
|
import Stars from "../components/StarsComponent";
|
|
import {formatTime} from "../model/formatTime";
|
|
|
|
export default function InfoScreen({navigation, route}: RootStackScreenProps<'Info'>) {
|
|
// @ts-ignore
|
|
const item: Movie = route.params.item
|
|
|
|
const insets = useSafeAreaInsets();
|
|
|
|
const [trailerPath, setTrailerPath] = useState("");
|
|
|
|
const [similarMovies, setSimilarMovies] = useState<MinimalMovie[]>([]);
|
|
|
|
const [review, setReview] = useState<Review[]>([]);
|
|
|
|
const [credit, setCredit] = useState<creditItem[]>();
|
|
|
|
const [paddingTopBackground, setPaddingTopBackground] = useState(0);
|
|
|
|
const [opacityBackground, setOpacityBackground] = useState(0.7);
|
|
|
|
const [scaleBackground, setScaleBackground] = useState(1);
|
|
|
|
|
|
const handleScroll = (event: any) => {
|
|
const {y} = event.nativeEvent.contentOffset;
|
|
let padTop = y / -20;
|
|
if (padTop <= 0)
|
|
setPaddingTopBackground(padTop);
|
|
setOpacityBackground(0.5 - y / 600);
|
|
let scale = 1 - y / -2000
|
|
if (scale >= 1)
|
|
setScaleBackground(scale);
|
|
};
|
|
|
|
type creditItem = [string, string, number];
|
|
|
|
type creditProps = {
|
|
data: creditItem[];
|
|
};
|
|
|
|
function CreditList({data}: creditProps) {
|
|
const renderItem = ({item}: { item: creditItem }) => (
|
|
<View style={styles.creditContainer}>
|
|
<View style={styles.bubble}>
|
|
<Image source={{uri: item[1]}} style={styles.photo}></Image>
|
|
<View style={styles.popularityDot}>
|
|
<Text style={styles.popularityLabel}>{item[2].toFixed(1).toString()}</Text>
|
|
<Ionicons name="md-star" size={13} color="#FFC42D"/>
|
|
</View>
|
|
</View>
|
|
|
|
<Text numberOfLines={2} style={styles.creditName}>{item[0]}</Text>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<FlatList
|
|
style={{paddingBottom: 40}}
|
|
data={data}
|
|
horizontal={true}
|
|
showsHorizontalScrollIndicator={false}
|
|
renderItem={renderItem}
|
|
keyExtractor={(item) => item[0]}
|
|
/>
|
|
);
|
|
}
|
|
|
|
type SimilarMovieProps = {
|
|
movie: MinimalMovie;
|
|
};
|
|
|
|
function SimilarMovie(props: SimilarMovieProps) {
|
|
return (
|
|
<View style={styles.similarContainer}>
|
|
<Image source={{uri: props.movie.poster_path}} style={styles.similarPoster}></Image>
|
|
<Text numberOfLines={2} style={styles.similarTitleFilm}>{props.movie.original_title}</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
type ReviewProps = {
|
|
review: Review;
|
|
};
|
|
|
|
function ReviewComponent(props: ReviewProps) {
|
|
return (
|
|
<View style={styles.reviewContainer}>
|
|
|
|
<View style={styles.reviewInfo}>
|
|
<Image source={{uri: props.review.profile_path}} style={styles.imageProfile}></Image>
|
|
<View style={styles.infoContainer}>
|
|
<Text numberOfLines={1} style={styles.pseudo}>{props.review.pseudo}</Text>
|
|
<Text style={styles.date}>{props.review.date}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
|
|
<Text numberOfLines={15} style={styles.message}>{props.review.message}</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
type InfoBadgeProps = {
|
|
texte: string
|
|
|
|
}
|
|
|
|
function InfoBadge(props: InfoBadgeProps) {
|
|
return (<View style={{paddingHorizontal: 15, paddingVertical: 7, backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: 10, justifyContent: "center", marginRight: 10}}>
|
|
<Text style={{color: "white", fontSize: 15}}>{props.texte}</Text>
|
|
</View>);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
background1: {
|
|
height: '100%',
|
|
width: '100%',
|
|
paddingTop: insets.top,
|
|
},
|
|
body: {
|
|
backgroundColor: "#0E0E0E"
|
|
},
|
|
backgroundSection: {
|
|
height: "100%",
|
|
width: "100%",
|
|
position: "absolute"
|
|
},
|
|
back_drop: {
|
|
height: "45%",
|
|
top: paddingTopBackground,
|
|
width: '100%',
|
|
opacity: opacityBackground,
|
|
position: "absolute",
|
|
|
|
transform: [{scale: scaleBackground}],
|
|
},
|
|
gradientFade: {
|
|
height: "30%",
|
|
top: "25%"
|
|
},
|
|
backButton: {
|
|
position: "absolute",
|
|
top: 10,
|
|
left: 5
|
|
},
|
|
list: {
|
|
height: "100%"
|
|
},
|
|
section1: {
|
|
paddingHorizontal: 35
|
|
},
|
|
title: {
|
|
color: "white",
|
|
fontSize: 43,
|
|
fontWeight: "bold",
|
|
paddingBottom: 10,
|
|
paddingTop: "45%"
|
|
},
|
|
characteristics: {
|
|
flexDirection: "row",
|
|
width: "100%",
|
|
justifyContent: "flex-start"
|
|
},
|
|
stars: {
|
|
flexDirection: "row",
|
|
width: "100%",
|
|
justifyContent: "flex-start",
|
|
alignItems: "center",
|
|
paddingBottom: 30
|
|
},
|
|
starsLabel: {
|
|
color: "#FFC42D",
|
|
fontWeight: "bold",
|
|
paddingLeft: 10,
|
|
fontSize: 16
|
|
},
|
|
player: {
|
|
borderRadius: 10,
|
|
overflow: "hidden"
|
|
},
|
|
resume: {
|
|
color: "#B3B3B3",
|
|
paddingTop: 30,
|
|
fontSize: 17
|
|
},
|
|
creditSection: {
|
|
paddingTop: 30
|
|
},
|
|
creditTitle: {
|
|
color: "#2998FD",
|
|
paddingBottom: 20,
|
|
paddingLeft: 35,
|
|
fontSize: 17,
|
|
fontWeight: "600"
|
|
},
|
|
similarSection: {
|
|
paddingTop: 30
|
|
},
|
|
similarTitle: {
|
|
color: "#2998FD",
|
|
paddingLeft: 35,
|
|
fontSize: 17,
|
|
fontWeight: "600",
|
|
paddingBottom: 20
|
|
},
|
|
similarContainer: {
|
|
width: 90,
|
|
marginHorizontal: 7
|
|
},
|
|
similarPoster: {
|
|
height: 130,
|
|
width: 90,
|
|
borderRadius: 8
|
|
},
|
|
similarTitleFilm: {
|
|
color: "#DADADA",
|
|
paddingTop: 5,
|
|
fontWeight: "300"
|
|
},
|
|
reviewSection: {
|
|
paddingTop: 30
|
|
},
|
|
reviewTitle: {
|
|
color: "#2998FD",
|
|
paddingLeft: 35,
|
|
fontSize: 17,
|
|
fontWeight: "600",
|
|
paddingBottom: 10
|
|
},
|
|
reviewContainer: {
|
|
marginHorizontal: 7,
|
|
width: 300,
|
|
padding: 20,
|
|
backgroundColor: "#09090F",
|
|
marginVertical: 10,
|
|
borderRadius: 14,
|
|
borderWidth: 0.8,
|
|
borderColor: "rgba(223,223,223,0.14)"
|
|
},
|
|
reviewInfo: {
|
|
flexDirection: "row",
|
|
paddingBottom: 20
|
|
},
|
|
imageProfile: {
|
|
height: 50,
|
|
width: 50,
|
|
borderRadius: 100
|
|
},
|
|
infoContainer: {
|
|
paddingLeft: 10,
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
width: "80%",
|
|
alignItems: "center"
|
|
},
|
|
pseudo: {
|
|
color: "white",
|
|
paddingTop: 5,
|
|
fontWeight: "700",
|
|
fontSize: 16
|
|
},
|
|
date: {
|
|
color: "grey",
|
|
paddingTop: 5,
|
|
fontWeight: "500",
|
|
fontSize: 14
|
|
},
|
|
message: {
|
|
color: "#B3B3B3",
|
|
paddingTop: 5,
|
|
fontWeight: "400"
|
|
},
|
|
creditContainer: {
|
|
width: 90,
|
|
marginHorizontal: 7,
|
|
alignItems: "center"
|
|
},
|
|
bubble: {
|
|
justifyContent: "center"
|
|
},
|
|
photo: {
|
|
height: 90,
|
|
width: 90,
|
|
borderRadius: 200,
|
|
borderWidth: 3,
|
|
borderColor: "rgba(255,255,255,0.8)"
|
|
},
|
|
popularityDot: {
|
|
backgroundColor: "white",
|
|
borderRadius: 20,
|
|
padding: 2,
|
|
paddingHorizontal: 5,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
position: "absolute",
|
|
bottom: 0,
|
|
right: 0,
|
|
flexDirection: "row"
|
|
},
|
|
popularityLabel: {
|
|
color: "black",
|
|
fontWeight: "500",
|
|
paddingRight: 4
|
|
},
|
|
creditName: {
|
|
color: "#DADADA",
|
|
paddingTop: 5,
|
|
fontWeight: "300"
|
|
}
|
|
});
|
|
|
|
const getTriller = async () => {
|
|
const trailerResponse = (await fetch(config.base_url + "movie/" + item.id + "/videos?api_key=" + config.api_key + "&language=fr-FR"));
|
|
|
|
const trailerJson = await trailerResponse.json();
|
|
//console.log("trailer", trailerJson)
|
|
// @ts-ignore
|
|
const trailer_key = trailerJson.results.slice(0, 1).map((elt) => {
|
|
if (elt["type"] === "Trailer" && elt["site"] === "YouTube") {
|
|
return elt["key"];
|
|
}
|
|
});
|
|
//console.log("key", trailer_key)
|
|
setTrailerPath(trailer_key);
|
|
}
|
|
const getCredits = async () => {
|
|
const creditResponse = (await fetch(config.base_url + "movie/" + item.id + "/credits?api_key=" + config.api_key + "&language=fr-FR"));
|
|
|
|
const creditJson = await creditResponse.json();
|
|
// @ts-ignore
|
|
let creditList = creditJson.cast.map((elt) => {
|
|
if (elt["popularity"])
|
|
return [elt["name"], 'https://image.tmdb.org/t/p/w185' + elt["profile_path"], elt["popularity"]]
|
|
});
|
|
creditList = creditList.slice(0, 5).sort((a: [fullname: string, profile_path: string, popularity: number], b: [fullname: string, profil_path: string, popularity: number]) => b[2] - a[2]);
|
|
|
|
setCredit(creditList);
|
|
}
|
|
const getSimilarMovies = async () => {
|
|
const SimilarMoviesResponse = (await fetch(config.base_url + "movie/" + item.id + "/recommendations?api_key=" + config.api_key + "&language=fr-FR"));
|
|
|
|
const SimilarMoviesJson = await SimilarMoviesResponse.json();
|
|
// @ts-ignore
|
|
const SimilarMoviesList = SimilarMoviesJson.results.slice(0, 10).map((elt) => {
|
|
return new MinimalMovie(elt["original_title"], elt["poster_path"])
|
|
});
|
|
setSimilarMovies(SimilarMoviesList);
|
|
}
|
|
const getReview = async () => {
|
|
const ReviewResponse = (await fetch(config.base_url + "movie/" + item.id + "/reviews?api_key=" + config.api_key + "&language=us-EN&page=1"));
|
|
const ReviewJson = await ReviewResponse.json();
|
|
// @ts-ignore
|
|
let ReviewList = ReviewJson.results.slice(0, 5).map((elt) => {
|
|
// @ts-ignore
|
|
const newreview = new Review(elt["content"], elt["author_details"].avatar_path, elt["created_at"], elt["author"])
|
|
return newreview
|
|
});
|
|
ReviewList = ReviewList.filter((review: Review, index: number, array: Review[]) => {
|
|
return array.findIndex((item: Review) => item.pseudo === review.pseudo) === index;
|
|
});
|
|
setReview(ReviewList);
|
|
}
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
getTriller();
|
|
getSimilarMovies();
|
|
getCredits();
|
|
getReview();
|
|
}, []);
|
|
|
|
|
|
return (
|
|
<View style={styles.body}>
|
|
<View style={styles.backgroundSection}>
|
|
<Image
|
|
style={styles.back_drop}
|
|
source={{
|
|
uri: item.backdrop_path,
|
|
}}
|
|
></Image>
|
|
|
|
<LinearGradient
|
|
// Background Linear Gradient
|
|
colors={['rgba(0,0,0,0.8)', 'transparent']}
|
|
/>
|
|
<LinearGradient style={styles.gradientFade}
|
|
// Button Linear Gradient
|
|
colors={['rgba(14,14,14,0)', 'rgba(14,14,14,0.7)', 'rgba(14,14,14,1)', 'rgba(14,14,14,1)']}>
|
|
</LinearGradient>
|
|
</View>
|
|
|
|
|
|
<SafeAreaView style={styles.background1}>
|
|
<TouchableOpacity onPress={() => navigation.goBack()} style={{zIndex: 100}}>
|
|
<Ionicons name="ios-arrow-back" size={30} color="white" style={styles.backButton}/>
|
|
</TouchableOpacity>
|
|
<ScrollView style={styles.list} showsVerticalScrollIndicator={false} onScroll={handleScroll} scrollEventThrottle={4}
|
|
>
|
|
<View style={styles.section1}>
|
|
<Text style={styles.title} numberOfLines={2}>{item.original_title}</Text>
|
|
<View style={styles.characteristics}>
|
|
<InfoBadge texte={`${item.genres[0]} ${item.genres[1] !== undefined ? ", " + item.genres[1] : ""}`}></InfoBadge>
|
|
<InfoBadge texte={item.release_date}></InfoBadge>
|
|
<InfoBadge texte={formatTime(item.runtime)}></InfoBadge>
|
|
</View>
|
|
<View style={styles.stars}>
|
|
<Stars note={item.vote_average} size={120}></Stars>
|
|
<Text style={styles.starsLabel}>{item.vote_average.toFixed(1)}</Text>
|
|
|
|
</View>
|
|
{trailerPath !== "" && (<YoutubeIframe webViewStyle={styles.player} height={195} play={false} videoId={trailerPath}/>)}
|
|
<Text style={styles.resume}>{item.overview}</Text>
|
|
|
|
|
|
</View>
|
|
|
|
{credit !== undefined && (
|
|
<View style={styles.creditSection}>
|
|
<Text style={styles.creditTitle}>Crédits</Text>
|
|
<CreditList data={credit}></CreditList>
|
|
</View>
|
|
|
|
|
|
)}
|
|
{similarMovies.length !== 0 && (
|
|
<View style={styles.similarSection}>
|
|
<Text style={styles.similarTitle}>Recommendations</Text>
|
|
<FlatList
|
|
showsHorizontalScrollIndicator={false}
|
|
data={similarMovies}
|
|
horizontal={true}
|
|
keyExtractor={item => item.original_title}
|
|
renderItem={({item}) =>
|
|
<SimilarMovie movie={item}></SimilarMovie>
|
|
}
|
|
/></View>
|
|
|
|
|
|
)}
|
|
|
|
{review.length !== 0 && (
|
|
<View style={styles.reviewSection}>
|
|
<Text style={styles.reviewTitle}>Commentaires</Text>
|
|
<FlatList
|
|
showsHorizontalScrollIndicator={false}
|
|
data={review}
|
|
horizontal={true}
|
|
keyExtractor={item => item.pseudo}
|
|
renderItem={({item}) =>
|
|
<ReviewComponent review={item}></ReviewComponent>
|
|
}
|
|
/></View>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
</View>
|
|
)
|
|
}
|