Code clean 🎉

Tests
Lucas Delanier 2 years ago
parent b80ad6d113
commit f0154a514c

@ -0,0 +1,41 @@
import {StyleSheet, Text, View} from "react-native";
import * as React from "react";
import Stars from "./StarsComponent";
import Movie from "../model/Movie";
type headerMovieProps = {
movie: Movie
}
export function HeaderMovie(props: headerMovieProps) {
function formatTime(time: number) {
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
const styles = StyleSheet.create({
circle: {
width: 6,
height: 6,
borderRadius: 100 / 2,
marginTop: 4,
backgroundColor: "lightgray",
marginHorizontal: 8
},
});
return (<View style={{flexDirection: 'column', alignSelf: 'center', paddingHorizontal: 30, width: '100%', alignItems: "center", paddingTop: 10, flex: 0.07}}>
<Text numberOfLines={1} style={{color: "white", fontSize: 30, fontWeight: "bold", paddingTop: 5, alignSelf: "center"}}>{props.movie.original_title}</Text>
<View style={{flexDirection: 'row', justifyContent: "center", alignItems: "center", alignSelf: "center"}}>
<Text style={{color: "#D1D1D1", fontSize: 20, fontWeight: "normal", paddingTop: 5}}>{`${props.movie.release_date}`}</Text>
<View style={styles.circle}/>
<Text style={{color: "#D1D1D1", fontSize: 20, fontWeight: "normal", paddingTop: 5}}>{`${props.movie.genres[0]} ${props.movie.genres[1] !== undefined ? ", " + props.movie.genres[1] : ""}`}</Text>
<View style={styles.circle}/>
<Text style={{color: "#D1D1D1", fontSize: 20, fontWeight: "normal", paddingTop: 5}}>{`${formatTime(props.movie.runtime)}`}</Text>
</View>
<Stars note={props.movie.vote_average} size={110}/>
</View>);
}

@ -0,0 +1,103 @@
import Movie from "../model/Movie.js";
import {Image, StyleSheet, Text, View} from "react-native";
import {LinearGradient} from "expo-linear-gradient";
import Stars from "./StarsComponent";
import * as React from "react";
type MovieListProps = {
movie: Movie
}
export function MovieListComponent(props: MovieListProps) {
function formatTime(time: number) {
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
const styles = StyleSheet.create({
filmCard: {
width: 70,
height: 110,
borderRadius: 8,
},
body: {
height: 130,
borderRadius: 20,
justifyContent: "flex-start",
flexDirection: 'row',
marginHorizontal: 10,
marginVertical: 7,
paddingHorizontal: 10,
backgroundColor: "#1D1D1D",
alignItems: "center",
borderWidth: 1.5,
borderColor: "#1F1F1F"
},
section: {
height: 130,
width: "85%",
justifyContent: "center",
flexDirection: 'column',
paddingRight: 20,
paddingLeft: 20,
},
h1: {
color: "white",
fontWeight: "700",
fontSize: 21,
},
infoSection: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
width: "100%"
},
top: {
flexDirection: "row",
alignItems: "center"
},
h3: {
color: "grey",
fontWeight: "600"
},
vote: {
paddingLeft: 7,
color: "white",
fontWeight: "bold",
fontSize: 13
}
});
return (
<LinearGradient style={styles.body} start={{x: 0, y: 1}}
end={{x: 1, y: 1}}
// Button Linear Gradient
colors={['#0B0B0B', '#1F1F1F']}>
<Image
style={styles.filmCard}
source={{
uri: props.movie.poster_path_min,
}}
/>
<View style={styles.section}>
<Text numberOfLines={1} style={styles.h1}>{props.movie.original_title}</Text>
<View style={styles.infoSection}>
<View style={styles.top}>
<Stars note={props.movie.vote_average} size={90}></Stars>
<Text style={styles.vote}>{props.movie.vote_average.toFixed(1)}</Text>
</View>
<Text style={styles.h3}>{formatTime(props.movie.runtime)}</Text>
</View>
<Text numberOfLines={3} style={{color: "#C7C7C7", fontWeight: "600",}}>{props.movie.overview}</Text>
</View>
</LinearGradient>
);
}

@ -0,0 +1,46 @@
import {Image, View} from "react-native";
import * as React from "react";
type StarsProps = {
note: number
size: number
}
export function Stars(props: StarsProps) {
let imageSource;
let note = props.note / 2;
if (note < 0.5)
imageSource = require('../assets/images/0.5stars_vote.png');
else if (note < 1)
imageSource = require('../assets/images/1stars_vote.png');
else if (note < 1.5)
imageSource = require('../assets/images/1.5stars_vote.png');
else if (note < 2)
imageSource = require('../assets/images/2stars_vote.png');
else if (note < 2.5)
imageSource = require('../assets/images/2.5stars_vote.png');
else if (note < 3)
imageSource = require('../assets/images/3stars_vote.png');
else if (note < 3.5)
imageSource = require('../assets/images/3.5stars_vote.png');
else if (note < 4)
imageSource = require('../assets/images/4stars_vote.png');
else if (note < 4.5)
imageSource = require('../assets/images/4.5stars_vote.png');
else if (note < 5)
imageSource = require('../assets/images/5stars_vote.png');
return (
<View>
<Image source={imageSource} style={{
width: props.size,
height: 40,
resizeMode: 'contain'
}}/>
</View>
);
};
export default Stars;

@ -0,0 +1,44 @@
import {Image, Text, View} from "react-native";
import * as React from "react";
type TimerProps = {
hours: number
minutes: number
seconds: number
}
export function Timer(props: TimerProps) {
return (
<View style={{zIndex: 1, alignContent: "center", justifyContent: "center", flex: 0.15, flexDirection: "row"}}>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>Nouvelle collection dans</Text>
<Image source={require('../assets/images/timer_icon.png')} style={{
height: 30,
resizeMode: 'contain',
top: -5
}}></Image>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>{`${props.hours.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>{`${props.minutes.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>{`${props.seconds.toString().padStart(2, '0')}`}</Text>
</View>
);
}
export function Timer2(props: TimerProps) {
return (
<View style={{zIndex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 20, paddingVertical: 10, borderRadius: 100, flexDirection: "row", bottom: 0, backgroundColor: "white", marginTop: 50}}>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>Nouvelle collection dans</Text>
<Image source={require('../assets/images/timer_icon2.png')} style={{
height: 30,
resizeMode: 'contain', marginHorizontal: 7
}}></Image>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>{`${props.hours.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>{`${props.minutes.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>{`${props.seconds.toString().padStart(2, '0')}`}</Text>
</View>
);
}

@ -3,19 +3,19 @@ class Review {
public pseudo: string public pseudo: string
public profil_path: string public profile_path: string
public date: string public date: string
constructor(message: string, profil_path: string, date: string, pseudo: string) { constructor(message: string, profile_path: string, date: string, pseudo: string) {
this.message = message; this.message = message;
if (profil_path === null) { if (profile_path == null) {
this.profil_path = "https://thumbs.dreamstime.com/b/profil-vectoriel-avatar-par-d%C3%A9faut-utilisateur-179376714.jpg"; this.profile_path = "https://thumbs.dreamstime.com/b/profil-vectoriel-avatar-par-d%C3%A9faut-utilisateur-179376714.jpg";
} else { } else {
this.profil_path = 'https://image.tmdb.org/t/p/w185' + profil_path; this.profile_path = 'https://image.tmdb.org/t/p/w185' + profile_path;
} }
console.log("profil_path", this.profile_path)
this.date = date.substring(0, 10); this.date = date.substring(0, 10);
this.pseudo = pseudo; this.pseudo = pseudo;

@ -40,7 +40,7 @@ export default function Navigation({colorScheme}: { colorScheme: ColorSchemeName
//console.log("test1:", trendingMovies); //console.log("test1:", trendingMovies);
loadTrendingID(); loadTrendingID();
} catch (e) { } catch (e) {
console.warn(e); //console.warn(e);
} finally { } finally {
// Tell the application to render // Tell the application to render
setAppIsReady(true); setAppIsReady(true);

@ -1,16 +1,8 @@
import { import {ADD_FAVOURITE, ADD_WATCHLATER, FETCH_FAVOURITE, FETCH_TRENDING_MOVIE, FETCH_WATCHLATER, POP_FIRST_TRENDING} from '../constants';
FETCH_TRENDING_MOVIE,
FETCH_TRENDING_ID,
POP_FIRST_TRENDING,
ADD_WATCHLATER,
FETCH_WATCHLATER,
ADD_FAVOURITE,
FETCH_FAVOURITE
} from '../constants';
import config from "../../constants/config"; import config from "../../constants/config";
import Movie from "../../model/Movie"; import Movie from "../../model/Movie";
export const getWatchLaterMovies = (WatchLaterList: Movie[]) => { /*export const getWatchLaterMovies = (WatchLaterList: Movie[]) => {
return { return {
type: FETCH_WATCHLATER, type: FETCH_WATCHLATER,
payload: WatchLaterList, payload: WatchLaterList,
@ -22,7 +14,7 @@ export const getFavouriteMovies = (FavouriteList: Movie[]) => {
type: FETCH_FAVOURITE, type: FETCH_FAVOURITE,
payload: FavouriteList, payload: FavouriteList,
}; };
} }*/
export const fetchWatchLater = (WatchLaterList: Movie[]) => { export const fetchWatchLater = (WatchLaterList: Movie[]) => {
return { return {
@ -66,21 +58,17 @@ export const getTrendingID = () => {
const MovieList: Movie[] = []; const MovieList: Movie[] = [];
Promise.all(idList.map(async elt => { Promise.all(idList.map(async elt => {
try { try {
const infoPromise = await fetch(config.base_url + "movie/" + elt + "?api_key=" + config.api_key + "&language=fr-FR"); return await fetch(config.base_url + "movie/" + elt + "?api_key=" + config.api_key + "&language=fr-FR");
//const infoJson = await infoPromise.json();
//console.log('infos---------', infoJson);
//MovieList.push(new Movie(infoJson["original_title"], infoJson["poster_path"],infoJson["runtime"], infoJson["vote_average"], infoJson["release_date"]))
return infoPromise;
} catch (err) { } catch (err) {
//console.log('ErrorGet---------', err); console.log('Error', err);
} }
})).then(function (responses) { })).then(function (responses) {
// @ts-ignore
Promise.all(responses.map(result => result.json())) Promise.all(responses.map(result => result.json()))
.then(function (elements) { .then(function (elements) {
elements.map(elt => { elements.map(elt => {
const infoJson = elt; const infoJson = elt;
const genreRow: String[] = []; const genreRow: String[] = [];
const videoPath: String = "";
// @ts-ignore // @ts-ignore
elt["genres"].map(genre => { elt["genres"].map(genre => {
genreRow.push(genre.name); genreRow.push(genre.name);
@ -90,17 +78,16 @@ export const getTrendingID = () => {
MovieList.push(new Movie(infoJson["id"], infoJson["title"], infoJson["poster_path"], infoJson["runtime"], infoJson["vote_average"], infoJson["release_date"], genreRow, infoJson["overview"], infoJson["backdrop_path"])) MovieList.push(new Movie(infoJson["id"], infoJson["title"], infoJson["poster_path"], infoJson["runtime"], infoJson["vote_average"], infoJson["release_date"], genreRow, infoJson["overview"], infoJson["backdrop_path"]))
}) })
try { try {
//console.log("tortue", MovieList)
dispatch(setinfoMovie(MovieList)); dispatch(setinfoMovie(MovieList));
} catch (err) { } catch (err) {
//console.log('ErrorGet---------', err); console.log('Error', err);
} }
}) })
}); });
} catch (error) { } catch (error) {
console.log('Error---------', error); console.log('Error', error);
} }
} }
} }

@ -1,12 +1,4 @@
import { import {POP_FIRST_TRENDING, FETCH_TRENDING_MOVIE, FETCH_TRENDING_ID, ADD_WATCHLATER, FETCH_WATCHLATER, ADD_FAVOURITE, FETCH_FAVOURITE} from "../constants";
POP_FIRST_TRENDING,
FETCH_TRENDING_MOVIE,
FETCH_TRENDING_ID,
ADD_WATCHLATER,
FETCH_WATCHLATER,
ADD_FAVOURITE,
FETCH_FAVOURITE
} from "../constants";
import Movie from "../../model/Movie"; import Movie from "../../model/Movie";
const initialState = { const initialState = {
@ -15,10 +7,8 @@ const initialState = {
watchLaterMovies: [] as Movie[], watchLaterMovies: [] as Movie[],
favouriteMovies: [] as Movie[], favouriteMovies: [] as Movie[],
} }
// @ts-ignore // @ts-ignore
export default appReducer = (state = initialState, action) => { export default appReducer = (state = initialState, action) => {
console.log(action.payload)
switch (action.type) { switch (action.type) {
case FETCH_TRENDING_ID: case FETCH_TRENDING_ID:
// @ts-ignore // @ts-ignore
@ -32,13 +22,13 @@ export default appReducer = (state = initialState, action) => {
case FETCH_TRENDING_MOVIE: case FETCH_TRENDING_MOVIE:
return {...state, trendingMovies: action.payload}; return {...state, trendingMovies: action.payload};
case POP_FIRST_TRENDING: case POP_FIRST_TRENDING:
return {...state, trendingMovies: [...state.trendingMovies.filter((item : Movie) => item !== action.payload)]}; return {...state, trendingMovies: [...state.trendingMovies.filter((item: Movie) => item !== action.payload)]};
case ADD_WATCHLATER: case ADD_WATCHLATER:
// @ts-ignore // @ts-ignore
return {...state,watchLaterMovies: [action.payload,...state.watchLaterMovies]}; return {...state, watchLaterMovies: [action.payload, ...state.watchLaterMovies]};
case ADD_FAVOURITE: case ADD_FAVOURITE:
// @ts-ignore // @ts-ignore
return {...state, favouriteMovies: [action.payload,...state.favouriteMovies]}; return {...state, favouriteMovies: [action.payload, ...state.favouriteMovies]};
default: default:
return state; return state;
} }

@ -1,28 +1,32 @@
import {FlatList, StyleSheet, SafeAreaView, Text, View, Image, TextInput, TouchableHighlight} from 'react-native'; import {FlatList, StyleSheet, SafeAreaView, Text, View, Image, TextInput, TouchableHighlight} from 'react-native';
import * as React from "react"; import * as React from "react";
import {BadgeFilm, Stars} from "./HomeScreen";
import {FontAwesomeIcon} from "@fortawesome/react-native-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-native-fontawesome";
import {faHeart} from "@fortawesome/free-solid-svg-icons"; import {faClock} from "@fortawesome/free-solid-svg-icons";
import LinearGradient from 'react-native-linear-gradient';
import {RootTabScreenProps} from "../types"; import {RootTabScreenProps} from "../types";
import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useSafeAreaInsets} from "react-native-safe-area-context";
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {useEffect, useState} from 'react'; import {useEffect, useState} from 'react';
import {getTrendingID, getFavourite, getFavouriteMovies} from "../redux/actions/actionGetTrendingID"; import {getFavourite} from "../redux/actions/actionGetTrendingID";
import Movie from "../model/Movie"; import Movie from "../model/Movie";
import Swipeable from "react-native-gesture-handler/Swipeable";
import {MovieListComponent} from "../components/MovieListComponent";
export default function FavoriteScreen({navigation}: RootTabScreenProps<'Favorite'>) {
export default function FavoriteScreen({ navigation }: RootTabScreenProps<'Favorite'>) {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [filteredDataSource, setFilteredDataSource] = useState<Movie[]>([]); const [filteredDataSource, setFilteredDataSource] = useState<Movie[]>([]);
const [masterDataSource, setMasterDataSource] = useState([]);
const [masterDataSource] = useState([]);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
paddingTop: 22, paddingTop: insets.top + 22,
backgroundColor: "#232323" backgroundColor: "#0E0E0E"
}, },
linearGradient: { linearGradient: {
flex: 1, flex: 1,
@ -41,17 +45,52 @@ export default function FavoriteScreen({ navigation }: RootTabScreenProps<'Favor
height: 100, height: 100,
borderRadius: 8, borderRadius: 8,
}, },
searchSection: {
height: 40,
width: 400,
backgroundColor: "grey",
borderRadius: 20,
marginVertical: 10,
alignSelf: "center"
},
searchBar: {
width: '100%',
height: 40,
marginHorizontal: 20
},
titlePage: {
height: 50,
justifyContent: "flex-start",
flexDirection: 'row',
paddingHorizontal: 20,
marginBottom: 15,
marginVertical: 5,
alignItems: "flex-end"
},
icon: {
marginBottom: -5,
marginRight: 20
},
delimiter: {
height: 2,
width: 400,
resizeMode: "stretch"
},
h1: {
color: "white",
fontSize: 30
}
}); });
const dispatch = useDispatch();
// @ts-ignore // @ts-ignore
const favouriteMovies = useSelector(state => state.appReducer.favouriteMovies); const favouriteMovies = useSelector(state => state.appReducer.favouriteMovies);
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
const loadFavourite = async () => { const loadFavourite = async () => {
// @ts-ignore // @ts-ignore
await dispatch(getFavourite()); await dispatch(getFavourite());
}; };
console.log("test11111:", favouriteMovies);
loadFavourite(); loadFavourite();
}, [dispatch]); }, [dispatch]);
@ -74,87 +113,26 @@ export default function FavoriteScreen({ navigation }: RootTabScreenProps<'Favor
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<View style={{height: 50, justifyContent: "flex-start",flexDirection: 'row', paddingHorizontal:20, marginBottom: 15,marginVertical:5, alignItems:"flex-end"}} > <View style={styles.titlePage}>
<FontAwesomeIcon icon={faHeart} style={{marginBottom: -5, marginRight: 20}} size={50} color="white" /> <FontAwesomeIcon icon={faClock} style={styles.icon} size={50} color="white"/>
<Text style={{color: "white", fontSize:30}}>Favorite</Text> <Text style={styles.h1}>Watch Later</Text>
</View> </View>
<Image <Image
source={require('../assets/images/delimiter.png')} style={{height: 2, width: 400, resizeMode: "stretch"}} source={require('../assets/images/delimiter.png')} style={styles.delimiter}
/> />
<View style={{height:40, width:400, backgroundColor:"grey", borderRadius:20, marginVertical:10, alignSelf:"center"}}>
<TextInput style={{width: '100%', height: 40, marginHorizontal: 20}} onChangeText={(text) => searchFilterFunction(text)} <View style={styles.searchSection}>
<TextInput style={styles.searchBar} onChangeText={(text) => searchFilterFunction(text)}
value={search} value={search}
></TextInput> ></TextInput>
</View> </View>
<FlatList <FlatList
data={search.length !== 0 ? filteredDataSource : favouriteMovies} data={search.length !== 0 ? filteredDataSource : favouriteMovies}
keyExtractor={item => item.original_title} keyExtractor={item => item.original_title}
renderItem={({item}) => <ListWidget movie={item}></ListWidget>} // @ts-ignore
renderItem={({item}) => <TouchableHighlight onPress={() => navigation.navigate("Info", {"item": item})}><MovieListComponent movie={item}></MovieListComponent></TouchableHighlight>}
/> />
</SafeAreaView> </SafeAreaView>
); );
} }
type ListWidgetProps = {
movie: Movie
}
export function ListWidget(props: ListWidgetProps) {
const insets = useSafeAreaInsets();
const styles = StyleSheet.create({
filmCard: {
width: 90,
height: 130,
borderRadius: 8,
},
});
function formatTime(time: number) {
console.log(time);
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
return (
<View style={{
height: 130,
width: "100%",
borderRadius: 20,
justifyContent: "flex-start",
flexDirection: 'row',
marginHorizontal: 20,
marginBottom: 15
}}>
<Image
style={styles.filmCard}
source={{
uri: props.movie.poster_path,
}}
/>
<View style={{
height: 130,
width: "70%",
justifyContent: "center",
flexDirection: 'column',
paddingRight: 20,
paddingLeft: 20,
}}>
<Text numberOfLines={1} style={{
color: "white",
fontWeight: "700",
fontSize: 21,
}}>{props.movie.original_title}</Text>
<View style={{flexDirection: "row", alignItems: "center", justifyContent: "space-between", width: "100%"}}>
<View style={{flexDirection: "row", alignItems: "center",}}>
<Stars note={props.movie.vote_average} size={70}></Stars>
<Text style={{paddingLeft: 10, color: "white", fontWeight: "bold"}}>{props.movie.vote_average.toFixed(1)}</Text>
</View>
<Text style={{color: "grey", fontWeight: "600"}}>{formatTime(props.movie.runtime)}</Text>
</View>
<Text numberOfLines={3} style={{color: "grey", fontWeight: "600",}}>{props.movie.overview}</Text>
</View>
</View>
);
}

@ -1,39 +1,28 @@
import * as React from 'react'; import * as React from 'react';
import { import {TouchableOpacity, View, Text, StyleSheet, Image, ImageBackground, SafeAreaView} from 'react-native';
Button,
TouchableOpacity,
ScrollView,
View,
Text,
StyleSheet,
Image,
ImageBackground,
SafeAreaView,
ActivityIndicator, FlatList
} from 'react-native';
import {RootStackScreenProps} from "../types"; import {RootStackScreenProps} from "../types";
import {useEffect, useRef, useState} from "react"; import {useEffect, useRef, useState} from "react";
import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useSafeAreaInsets} from "react-native-safe-area-context";
import {addMovieToWatchLater, addMovieToFavourite, getTrendingID, removeMovieTrending,} from "../redux/actions/actionGetTrendingID"; import {addMovieToWatchLater, addMovieToFavourite, removeMovieTrending,} from "../redux/actions/actionGetTrendingID";
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import Movie from "../model/Movie"; import Movie from "../model/Movie";
import * as url from "url";
import moment from 'moment'; import moment from 'moment';
import CardsSwipe from 'react-native-cards-swipe'; import CardsSwipe from 'react-native-cards-swipe';
import AnimatedLottieView from "lottie-react-native"; import AnimatedLottieView from "lottie-react-native";
import {Timer, Timer2} from "../components/TimerComponent";
import {HeaderMovie} from "../components/HeaderMovieComponent";
export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) { export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
// @ts-ignore // @ts-ignore
const trendingMovies = useSelector(state => state.appReducer.trendingMovies); const trendingMovies = useSelector(state => state.appReducer.trendingMovies);
const dispatch = useDispatch();
const [hours, setHours] = useState(0); const [hours, setHours] = useState(0);
const [minutes, setMinutes] = useState(0); const [minutes, setMinutes] = useState(0);
const [seconds, setSeconds] = useState(0); const [seconds, setSeconds] = useState(0);
const [displayIndex, setdisplayIndex] = useState(0); const [displayIndex, setdisplayIndex] = useState(0);
var swiper = null;
//console.log("liste [0]: ", trendingMovies[0]); var swiper: any = null;
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
@ -49,7 +38,6 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
width: '100%', width: '100%',
paddingTop: insets.top, paddingTop: insets.top,
}, },
container: { container: {
flex: 1, flex: 1,
}, },
@ -82,21 +70,63 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
flex: 1, flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)', backgroundColor: 'rgba(0,0,0,0.5)',
}, },
circle: { explanation: {
width: 6, color: "grey",
height: 6, fontWeight: "400",
borderRadius: 100 / 2, paddingHorizontal: 70,
marginTop: 4, textAlign: "center"
backgroundColor: "lightgray", },
marginHorizontal: 8 h1: {
color: "white",
fontWeight: "600",
fontSize: 35
},
congratsSection: {
alignItems: "center",
width: "100%",
height: "100%",
justifyContent: "center",
zIndex: 1
},
button: {
resizeMode: "stretch",
height: '55%',
aspectRatio: 1,
},
buttonSection: {
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: "space-evenly",
paddingHorizontal: 30,
width: '100%',
position: "absolute",
top: "74%",
zIndex: 30
},
posterBackground: {
width: "150%",
height: "150%",
justifyContent: "center",
alignItems: "center",
opacity: 0.55,
position: 'absolute',
left: "-50%",
top: "-50%"
}, },
finishBackground: {
width: "110%",
height: "110%",
justifyContent: "center",
alignItems: "center",
opacity: 0.15,
position: "absolute",
zIndex: 0
}
}); });
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
const today = moment(); const today = moment();
@ -120,16 +150,10 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
const s = Math.floor((difference % (1000 * 60)) / 1000); const s = Math.floor((difference % (1000 * 60)) / 1000);
setSeconds(s); setSeconds(s);
//console.log("timer", h, m, s);
}); });
setTimeout(() => interval, 10000); setTimeout(() => interval, 10000);
}, []); }, []);
type ItemProps = {
movie: Movie
}
function addWatchLater(props: Movie) { function addWatchLater(props: Movie) {
dispatch(addMovieToWatchLater(props)); dispatch(addMovieToWatchLater(props));
dispatch(removeMovieTrending(props)); dispatch(removeMovieTrending(props));
@ -157,27 +181,12 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
} }
} }
function formatTime(time: number) {
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
return ( return (
<> <>
<ImageBackground blurRadius={0} <ImageBackground blurRadius={0}
style={{ style={styles.finishBackground}
width: "110%",
height: "110%",
justifyContent: "center",
alignItems: "center",
opacity: 0.15,
position: "absolute",
zIndex: 0
}}
source={require("../assets/images/background.png") source={require("../assets/images/background.png")
} }
></ImageBackground> ></ImageBackground>
@ -188,32 +197,12 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
<ImageBackground blurRadius={29} <ImageBackground blurRadius={29}
style={{ style={styles.posterBackground}
width: "150%",
height: "150%",
justifyContent: "center",
alignItems: "center",
opacity: 0.55,
position: 'absolute',
left: "-50%",
top: "-50%"
}}
source={{ source={{
uri: trendingMovies[displayIndex]?.poster_path, uri: trendingMovies[displayIndex]?.poster_path,
}} }}
></ImageBackground> ></ImageBackground>
<View style={{flexDirection: 'column', alignSelf: 'center', paddingHorizontal: 30, width: '100%', alignItems: "center", paddingTop: 10, flex: 0.07}}> <HeaderMovie movie={trendingMovies[displayIndex]}></HeaderMovie>
<Text numberOfLines={1} style={{color: "white", fontSize: 30, fontWeight: "bold", paddingTop: 5, alignSelf: "center"}}>{trendingMovies[displayIndex].original_title}</Text>
<View style={{flexDirection: 'row', justifyContent: "center", alignItems: "center", alignSelf: "center"}}>
<Text style={{color: "#D1D1D1", fontSize: 20, fontWeight: "normal", paddingTop: 5}}>{`${trendingMovies[displayIndex].release_date}`}</Text>
<View style={styles.circle}/>
<Text style={{color: "#D1D1D1", fontSize: 20, fontWeight: "normal", paddingTop: 5}}>{`${trendingMovies[displayIndex].genres[0]} ${trendingMovies[displayIndex].genres[1] !== undefined ? ", " + trendingMovies[0].genres[1] : ""}`}</Text>
<View style={styles.circle}/>
<Text style={{color: "#D1D1D1", fontSize: 20, fontWeight: "normal", paddingTop: 5}}>{`${formatTime(trendingMovies[displayIndex].runtime)}`}</Text>
</View>
<Stars note={trendingMovies[displayIndex].vote_average} size={110}/>
</View>
<CardsSwipe <CardsSwipe
ref={(rf) => { ref={(rf) => {
@ -223,7 +212,6 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
cards={trendingMovies} cards={trendingMovies}
loop={true} loop={true}
onSwipedLeft={(index) => { onSwipedLeft={(index) => {
console.log(index)
if (index < trendingMovies.length - 1) { if (index < trendingMovies.length - 1) {
setdisplayIndex(index + 1); setdisplayIndex(index + 1);
@ -251,7 +239,7 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
/> />
<View style={{flexDirection: 'row', alignItems: 'flex-end', justifyContent: "space-evenly", paddingHorizontal: 30, width: '100%', position: "absolute", top: "74%", zIndex: 30}}> <View style={styles.buttonSection}>
<TouchableOpacity onPress={() => { <TouchableOpacity onPress={() => {
addWatchLater(trendingMovies[displayIndex]); addWatchLater(trendingMovies[displayIndex]);
@ -259,7 +247,7 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
}}> }}>
<Image <Image
source={require('../assets/images/watchlater_button.png')} style={{resizeMode: "stretch", height: '55%', aspectRatio: 1,}} source={require('../assets/images/watchlater_button.png')} style={styles.button}
/> />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity onPress={ <TouchableOpacity onPress={
@ -267,9 +255,7 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
popFirstTrending(trendingMovies[displayIndex]); popFirstTrending(trendingMovies[displayIndex]);
}}> }}>
<Image <Image
source={require('../assets/images/delete_button.png')} style={{ source={require('../assets/images/delete_button.png')} style={styles.button}
resizeMode: "stretch", height: '55%', aspectRatio: 1,
}}
/> />
</TouchableOpacity> </TouchableOpacity>
@ -278,45 +264,24 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
} }
}> }>
<Image <Image
source={require('../assets/images/like_button.png')} style={{resizeMode: "stretch", height: '55%', aspectRatio: 1,}} source={require('../assets/images/like_button.png')} style={styles.button}
/> />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={{zIndex: 1, alignContent: "center", justifyContent: "center", flex: 0.15, flexDirection: "row"}}> <Timer hours={hours} minutes={minutes} seconds={seconds}></Timer>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>Nouvelle collection dans</Text>
<Image source={require('../assets/images/timer_icon.png')} style={{
height: 30,
resizeMode: 'contain',
top: -5
}}></Image>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>{`${hours.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>{`${minutes.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "#FFF", fontSize: 16, fontWeight: "500"}}>{`${seconds.toString().padStart(2, '0')}`}</Text>
</View>
</SafeAreaView>)} </SafeAreaView>)}
{trendingMovies.length === 0 && ( {trendingMovies.length === 0 && (
<SafeAreaView style={styles.background2}> <SafeAreaView style={styles.background2}>
<View style={{alignItems: "center", width: "100%", height: "100%", justifyContent: "center", zIndex: 1}}> <View style={styles.congratsSection}>
<Text style={{color: "white", fontWeight: "600", fontSize: 35}}>Félicitations !</Text> <Text style={styles.h1}>Félicitations !</Text>
<AnimatedLottieView source={require("../assets/animation.json")} autoPlay={true} loop={true} style={{height: 200}}/> <AnimatedLottieView source={require("../assets/animation.json")} autoPlay={true} loop={true} style={{height: 200}}/>
<Text style={{color: "grey", fontWeight: "400", paddingHorizontal: 70, textAlign: "center"}}>Vous avez fini la collection du jour. <Text style={styles.explanation}>Vous avez fini la collection du jour.
{"\n"}Revenez à la fin du décompte pour découvrir de nouvelles propositions.</Text> {"\n"}Revenez à la fin du décompte pour découvrir de nouvelles propositions.</Text>
<View style={{zIndex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 20, paddingVertical: 10, borderRadius: 100, flexDirection: "row", bottom: 0, backgroundColor: "white", marginTop: 50}}> <Timer2 hours={hours} minutes={minutes} seconds={seconds}></Timer2>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>Nouvelle collection dans</Text>
<Image source={require('../assets/images/timer_icon2.png')} style={{
height: 30,
resizeMode: 'contain', marginHorizontal: 7
}}></Image>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>{`${hours.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>{`${minutes.toString().padStart(2, '0')}:`}</Text>
<Text style={{color: "black", fontSize: 16, fontWeight: "500"}}>{`${seconds.toString().padStart(2, '0')}`}</Text>
</View>
</View> </View>
</SafeAreaView> </SafeAreaView>
@ -328,95 +293,12 @@ export default function HomeScreen({navigation}: RootStackScreenProps<'Home'>) {
) )
} }
type BadgeGenreProps = {
name: String
isSelected: Boolean
}
export function BadgeGenre(props: BadgeGenreProps) {
if (props.isSelected === false) {
return (
<View style={{paddingHorizontal: 20, marginHorizontal: 5, height: 35, backgroundColor: '#2E2E2E', borderRadius: 20, justifyContent: "center"}}>
<Text style={{color: "white"}}>{props.name}</Text>
</View>
);
} else {
return (
<View style={{paddingHorizontal: 20, marginHorizontal: 5, height: 35, backgroundColor: '#5C5C5C', borderRadius: 20, borderWidth: 1, borderColor: "white", justifyContent: "center"}}>
<Text style={{color: "white"}}>{props.name}</Text>
</View>
);
}
}
type BadgeFilmProps = {
name: String
}
export function BadgeFilm(props: BadgeFilmProps) {
return (
<View style={{
paddingHorizontal: 15,
marginHorizontal: 5,
height: 30,
backgroundColor: '#8906B8',
borderRadius: 15,
justifyContent: "center",
alignSelf: "flex-start"
}}>
<Text style={{color: "white", fontSize: 12, fontWeight: "bold"}}>{props.name}</Text>
</View>
);
}
type StarsProps = {
note: number
size: number
}
export function Stars(props: StarsProps) {
let imageSource;
let note = props.note / 2;
if (note < 0.5)
imageSource = require('../assets/images/0.5stars_vote.png');
else if (note < 1)
imageSource = require('../assets/images/1stars_vote.png');
else if (note < 1.5)
imageSource = require('../assets/images/1.5stars_vote.png');
else if (note < 2)
imageSource = require('../assets/images/2stars_vote.png');
else if (note < 2.5)
imageSource = require('../assets/images/2.5stars_vote.png');
else if (note < 3)
imageSource = require('../assets/images/3stars_vote.png');
else if (note < 3.5)
imageSource = require('../assets/images/3.5stars_vote.png');
else if (note < 4)
imageSource = require('../assets/images/4stars_vote.png');
else if (note < 4.5)
imageSource = require('../assets/images/4.5stars_vote.png');
else if (note < 5)
imageSource = require('../assets/images/5stars_vote.png');
return (
<View>
<Image source={imageSource} style={{
width: props.size,
height: 40,
resizeMode: 'contain'
}}/>
</View>
);
};

@ -1,81 +1,362 @@
import * as React from 'react'; import * as React from 'react';
import { import {TouchableOpacity, ScrollView, View, Text, StyleSheet, Image, SafeAreaView, FlatList} from 'react-native';
Button,
TouchableOpacity,
ScrollView,
View,
Text,
StyleSheet,
Image,
ImageBackground,
SafeAreaView,
ActivityIndicator, FlatList, TouchableHighlight
} from 'react-native';
import {RootStackScreenProps} from "../types"; import {RootStackScreenProps} from "../types";
import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useSafeAreaInsets} from "react-native-safe-area-context";
import Movie from "../model/Movie"; import Movie from "../model/Movie";
import {LinearGradient} from 'expo-linear-gradient'; import {LinearGradient} from 'expo-linear-gradient';
import {Stars} from "./HomeScreen";
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import config from "../constants/config"; import config from "../constants/config";
import YoutubeIframe from "react-native-youtube-iframe"; import YoutubeIframe from "react-native-youtube-iframe";
import Icon from "react-native-ionicons";
import Ionicons from "@expo/vector-icons/Ionicons"; import Ionicons from "@expo/vector-icons/Ionicons";
import MinimalMovie from "../model/MinimalMovie"; import MinimalMovie from "../model/MinimalMovie";
import {ListWidget} from "./WatchLaterScreen";
import Review from "../model/review"; import Review from "../model/review";
import Stars from "../components/StarsComponent";
import minimalMovie from "../model/MinimalMovie";
export default function InfoScreen({navigation, route}: RootStackScreenProps<'Info'>) { export default function InfoScreen({navigation, route}: RootStackScreenProps<'Info'>) {
// @ts-ignore // @ts-ignore
const item: Movie = route.params.item const item: Movie = route.params.item
const insets = useSafeAreaInsets(); 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);
function formatTime(time: number) {
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
const handleScroll = (event: any) => {
const {y} = event.nativeEvent.contentOffset;
let padTop = y / -5;
if (padTop <= 0)
setPaddingTopBackground(y / -5);
setOpacityBackground(0.5 - y / 500);
let scale = 1 - y / -1000
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({ const styles = StyleSheet.create({
background1: { background1: {
height: '100%', height: '100%',
width: '100%', width: '100%',
paddingTop: insets.top, 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 [trailerPath, setTrailerPath] = useState("");
const [similarMovies, setsimilarMovies] = useState<MinimalMovie[]>([]);
const [review, setReview] = useState<Review[]>([]);
const [credit, setCredit] = useState<creditItem[]>();
const [paddingtopbackgroud, setpaddingtopbackgroud] = useState(0);
const [opacitybackground, setopacitybackground] = useState(0.7);
const [scalebackground, setscalebackground] = useState(1);
const getTriller = async () => { const getTriller = async () => {
const trailerResponse = (await fetch(config.base_url + "movie/" + item.id + "/videos?api_key=" + config.api_key + "&language=fr-FR")); const trailerResponse = (await fetch(config.base_url + "movie/" + item.id + "/videos?api_key=" + config.api_key + "&language=fr-FR"));
const trailerJson = await trailerResponse.json(); const trailerJson = await trailerResponse.json();
console.log("trailer", trailerJson) //console.log("trailer", trailerJson)
// @ts-ignore // @ts-ignore
const trailer_key = trailerJson.results.slice(0, 1).map((elt) => { const trailer_key = trailerJson.results.slice(0, 1).map((elt) => {
if (elt["type"] === "Trailer" && elt["site"] === "YouTube") { if (elt["type"] === "Trailer" && elt["site"] === "YouTube") {
return elt["key"]; return elt["key"];
} }
}); });
console.log("key", trailer_key) //console.log("key", trailer_key)
setTrailerPath(trailer_key); setTrailerPath(trailer_key);
} }
const getCredits = async () => { const getCredits = async () => {
const creditResponse = (await fetch(config.base_url + "movie/" + item.id + "/credits?api_key=" + config.api_key + "&language=fr-FR")); const creditResponse = (await fetch(config.base_url + "movie/" + item.id + "/credits?api_key=" + config.api_key + "&language=fr-FR"));
const creditJson = await creditResponse.json(); const creditJson = await creditResponse.json();
console.log("credittttttt", creditJson) //console.log("credittttttt", creditJson)
// @ts-ignore // @ts-ignore
let creditList = creditJson.cast.map((elt) => { let creditList = creditJson.cast.map((elt) => {
if (elt["popularity"]) if (elt["popularity"])
return [elt["name"], 'https://image.tmdb.org/t/p/w500' + elt["profile_path"], 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, profil_path: string, popularity: number], b: [fullname: string, profil_path: string, popularity: number]) => b[2] - a[2]); 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]);
console.log("credit", creditList); //console.log("credit", creditList);
setCredit(creditList); setCredit(creditList);
} }
const getSimilarMovies = async () => { const getSimilarMovies = async () => {
const SimilarMoviesResponse = (await fetch(config.base_url + "movie/" + item.id + "/recommendations?api_key=" + config.api_key + "&language=fr-FR")); const SimilarMoviesResponse = (await fetch(config.base_url + "movie/" + item.id + "/recommendations?api_key=" + config.api_key + "&language=fr-FR"));
@ -84,15 +365,16 @@ export default function InfoScreen({navigation, route}: RootStackScreenProps<'In
const SimilarMoviesList = SimilarMoviesJson.results.slice(0, 10).map((elt) => { const SimilarMoviesList = SimilarMoviesJson.results.slice(0, 10).map((elt) => {
return new MinimalMovie(elt["original_title"], elt["poster_path"]) return new MinimalMovie(elt["original_title"], elt["poster_path"])
}); });
console.log("similar", SimilarMoviesList); //console.log("similar", SimilarMoviesList);
setsimilarMovies(SimilarMoviesList); setSimilarMovies(SimilarMoviesList);
} }
const getReview = async () => { 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 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(); const ReviewJson = await ReviewResponse.json();
// @ts-ignore // @ts-ignore
let ReviewList = ReviewJson.results.map((elt) => { 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"]) const newreview = new Review(elt["content"], elt["author_details"].avatar_path, elt["created_at"], elt["author"])
return newreview return newreview
}); });
@ -100,81 +382,25 @@ export default function InfoScreen({navigation, route}: RootStackScreenProps<'In
return array.findIndex((item: Review) => item.pseudo === review.pseudo) === index; return array.findIndex((item: Review) => item.pseudo === review.pseudo) === index;
}); });
console.log("review", ReviewJson.results); //console.log("review", ReviewJson.results);
setReview(ReviewList); setReview(ReviewList);
} }
useEffect(() => { useEffect(() => {
getReview();
getTriller(); getTriller();
getSimilarMovies(); getSimilarMovies();
getCredits(); getCredits();
getReview();
}, []); }, []);
function formatTime(time: number) {
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
type creditItem = [string, string, number];
type creditProps = {
data: creditItem[];
};
const handleScroll = (event: any) => {
const {y} = event.nativeEvent.contentOffset;
let padTop = y / -5;
if (padTop <= 0)
setpaddingtopbackgroud(y / -5);
setopacitybackground(0.5 - y / 500);
let scale = 1 - y / -1000
if (scale >= 1)
setscalebackground(scale);
};
function CreditList({data}: creditProps) {
const renderItem = ({item}: { item: creditItem }) => (
<View style={{width: 90, marginHorizontal: 7, alignItems: "center"}}>
<View style={{justifyContent: "center"}}>
<Image source={{uri: item[1]}} style={{height: 90, width: 90, borderRadius: 200, borderWidth: 3, borderColor: "rgba(255,255,255,0.8)"}}></Image>
<View style={{backgroundColor: "white", borderRadius: 20, padding: 2, paddingHorizontal: 5, justifyContent: "center", alignItems: "center", position: "absolute", bottom: 0, right: 0, flexDirection: "row"}}>
<Text style={{color: "black", fontWeight: "500", paddingRight: 4}}>{item[2].toFixed(1).toString()}</Text>
<Ionicons name="md-star" size={13} color="#FFC42D"/>
</View>
</View>
<Text numberOfLines={2} style={{color: "#DADADA", paddingTop: 5, fontWeight: "300"}}>{item[0]}</Text>
</View>
);
return (
<FlatList
style={{paddingBottom: 40}}
data={data}
horizontal={true}
showsHorizontalScrollIndicator={false}
renderItem={renderItem}
keyExtractor={(item) => item[0]}
/>
);
}
return ( return (
<View style={{backgroundColor: "#0E0E0E"}}> <View style={styles.body}>
<View style={{height: "100%", width: "100%", position: "absolute"}}> <View style={styles.backgroundSection}>
<Image <Image
style={{ style={styles.back_drop}
height: "45%",
top: paddingtopbackgroud,
width: '100%',
opacity: opacitybackground,
position: "absolute",
transform: [{scale: scalebackground}],
}}
source={{ source={{
uri: item.backdrop_path, uri: item.backdrop_path,
}} }}
@ -184,7 +410,7 @@ export default function InfoScreen({navigation, route}: RootStackScreenProps<'In
// Background Linear Gradient // Background Linear Gradient
colors={['rgba(0,0,0,0.8)', 'transparent']} colors={['rgba(0,0,0,0.8)', 'transparent']}
/> />
<LinearGradient style={{height: "30%", top: "25%",}} <LinearGradient style={styles.gradientFade}
// Button Linear Gradient // 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)']}> colors={['rgba(14,14,14,0)', 'rgba(14,14,14,0.7)', 'rgba(14,14,14,1)', 'rgba(14,14,14,1)']}>
</LinearGradient> </LinearGradient>
@ -193,90 +419,64 @@ export default function InfoScreen({navigation, route}: RootStackScreenProps<'In
<SafeAreaView style={styles.background1}> <SafeAreaView style={styles.background1}>
<TouchableOpacity onPress={() => navigation.goBack()} style={{zIndex: 100}}> <TouchableOpacity onPress={() => navigation.goBack()} style={{zIndex: 100}}>
<Ionicons name="ios-arrow-back" size={30} color="white" style={{position: "absolute", top: 10, left: 5}}/> <Ionicons name="ios-arrow-back" size={30} color="white" style={styles.backButton}/>
</TouchableOpacity> </TouchableOpacity>
<ScrollView style={{height: "100%"}} showsVerticalScrollIndicator={false} onScroll={handleScroll} scrollEventThrottle={1} <ScrollView style={styles.list} showsVerticalScrollIndicator={false} onScroll={handleScroll} scrollEventThrottle={1}
> >
<View style={{paddingHorizontal: 35}}> <View style={styles.section1}>
<Text style={{color: "white", fontSize: 43, fontWeight: "bold", paddingBottom: 10, paddingTop: "45%"}} numberOfLines={2}>{item.original_title}</Text> <Text style={styles.title} numberOfLines={2}>{item.original_title}</Text>
<View style={{flexDirection: "row", width: "100%", justifyContent: "flex-start"}}> <View style={styles.characteristics}>
<InfoBadge texte={`${item.genres[0]} ${item.genres[1] !== undefined ? ", " + item.genres[1] : ""}`}></InfoBadge> <InfoBadge texte={`${item.genres[0]} ${item.genres[1] !== undefined ? ", " + item.genres[1] : ""}`}></InfoBadge>
<InfoBadge texte={item.release_date}></InfoBadge> <InfoBadge texte={item.release_date}></InfoBadge>
<InfoBadge texte={formatTime(item.runtime)}></InfoBadge> <InfoBadge texte={formatTime(item.runtime)}></InfoBadge>
</View> </View>
<View style={{flexDirection: "row", width: "100%", justifyContent: "flex-start", alignItems: "center", paddingBottom: 30}}> <View style={styles.stars}>
<Stars note={item.vote_average} size={120}></Stars> <Stars note={item.vote_average} size={120}></Stars>
<Text style={{color: "#FFC42D", fontWeight: "bold", paddingLeft: 10, fontSize: 16}}>{item.vote_average.toFixed(1)}</Text> <Text style={styles.starsLabel}>{item.vote_average.toFixed(1)}</Text>
</View> </View>
{trailerPath !== "" && ( {trailerPath !== "" && (<YoutubeIframe webViewStyle={styles.player} height={195} play={false} videoId={trailerPath}/>)}
<Text style={styles.resume}>{item.overview}</Text>
<YoutubeIframe webViewStyle={{borderRadius: 10, overflow: "hidden"}}
height={195}
play={false}
videoId={trailerPath}
/>)}
<Text style={{color: "#B3B3B3", paddingTop: 30, fontSize: 17}}>{item.overview}</Text>
</View> </View>
{credit !== undefined && ( {credit !== undefined && (
<> <View style={styles.creditSection}>
<Text style={{color: "#2998FD", paddingTop: 30, paddingBottom: 20, paddingLeft: 35, fontSize: 17, fontWeight: "600"}}>Crédits</Text> <Text style={styles.creditTitle}>Crédits</Text>
<CreditList data={credit}></CreditList></> <CreditList data={credit}></CreditList>
</View>
)} )}
{similarMovies.length !== 0 && ( {similarMovies.length !== 0 && (
<> <View style={styles.similarSection}>
<Text style={{color: "#2998FD", paddingTop: 30, paddingLeft: 35, fontSize: 17, fontWeight: "600"}}>Recommendations</Text> <Text style={styles.similarTitle}>Recommendations</Text>
<FlatList <FlatList
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
style={{paddingTop: 20}}
data={similarMovies} data={similarMovies}
horizontal={true} horizontal={true}
keyExtractor={item => item.original_title} keyExtractor={item => item.original_title}
renderItem={({item}) => renderItem={({item}) =>
<View style={{width: 90, marginHorizontal: 7}}> <SimilarMovie movie={item}></SimilarMovie>
<Image source={{uri: item.poster_path}} style={{height: 130, width: 90, borderRadius: 8}}></Image>
<Text numberOfLines={2} style={{color: "#DADADA", paddingTop: 5, fontWeight: "300"}}>{item.original_title}</Text>
</View>
} }
/></> /></View>
)} )}
{review.length !== 0 && ( {review.length !== 0 && (
<> <View style={styles.reviewSection}>
<Text style={{color: "#2998FD", paddingTop: 30, paddingLeft: 35, fontSize: 17, fontWeight: "600"}}>Commentaires</Text> <Text style={styles.reviewTitle}>Commentaires</Text>
<FlatList <FlatList
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
style={{paddingTop: 10}}
data={review} data={review}
horizontal={true} horizontal={true}
keyExtractor={item => item.pseudo} keyExtractor={item => item.pseudo}
renderItem={({item}) => renderItem={({item}) =>
<View style={{marginHorizontal: 7, width: 300, padding: 20, backgroundColor: "#09090F", marginVertical: 10, borderRadius: 14, borderWidth: 0.8, borderColor: "rgba(223,223,223,0.14)"}}> <ReviewComponent review={item}></ReviewComponent>
<View style={{flexDirection: "row", paddingBottom: 20}}>
<Image source={{uri: item.profil_path}} style={{height: 50, width: 50, borderRadius: 100}}></Image>
<View style={{paddingLeft: 10, flexDirection: "row", justifyContent: "space-between", width: "80%", alignItems: "center"}}>
<Text numberOfLines={1} style={{color: "white", paddingTop: 5, fontWeight: "700", fontSize: 16}}>{item.pseudo}</Text>
<Text style={{color: "grey", paddingTop: 5, fontWeight: "500", fontSize: 14}}>{item.date}</Text>
</View>
</View>
<Text numberOfLines={15} style={{color: "#B3B3B3", paddingTop: 5, fontWeight: "400"}}>{item.message}</Text>
</View>
} }
/></> /></View>
)} )}
@ -287,13 +487,3 @@ export default function InfoScreen({navigation, route}: RootStackScreenProps<'In
</View> </View>
) )
} }
type InfoBadgeProps = {
texte: string
}
export 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>);
}

@ -1,26 +1,49 @@
import {FlatList, StyleSheet, SafeAreaView, Text, View, Image, TextInput, TouchableHighlight} from 'react-native'; import {FlatList, StyleSheet, SafeAreaView, Text, View, Image, TextInput, TouchableHighlight} from 'react-native';
import * as React from "react"; import * as React from "react";
import {BadgeFilm, Stars} from "./HomeScreen";
import {FontAwesomeIcon} from "@fortawesome/react-native-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-native-fontawesome";
import {faClock} from "@fortawesome/free-solid-svg-icons"; import {faClock} from "@fortawesome/free-solid-svg-icons";
import {RootTabScreenProps} from "../types"; import {RootTabScreenProps} from "../types";
import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useSafeAreaInsets} from "react-native-safe-area-context";
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {useEffect, useState} from 'react'; import {useEffect, useState} from 'react';
import {getTrendingID, getWatchLater, getWatchLaterMovies} from "../redux/actions/actionGetTrendingID"; import {getWatchLater} from "../redux/actions/actionGetTrendingID";
import Movie from "../model/Movie"; import Movie from "../model/Movie";
import {useNavigation} from "@react-navigation/native"; import {MovieListComponent} from "../components/MovieListComponent";
import {LinearGradient} from 'expo-linear-gradient';
export default function WatchLaterScreen({navigation}: RootTabScreenProps<'WatchLater'>) { export default function WatchLaterScreen({navigation}: RootTabScreenProps<'WatchLater'>) {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [filteredDataSource, setFilteredDataSource] = useState<Movie[]>([]); const [filteredDataSource, setFilteredDataSource] = useState<Movie[]>([]);
const [masterDataSource, setMasterDataSource] = useState([]);
const [masterDataSource] = useState([]);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const searchFilterFunction = (text: string) => {
if (text) {
const newData = watchLaterMovies.filter(function (item: Movie) {
const itemData = item.original_title
? item.original_title.toUpperCase()
: ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilteredDataSource(newData);
setSearch(text);
} else {
setFilteredDataSource(masterDataSource);
setSearch(text);
}
};
// @ts-ignore
const watchLaterMovies = useSelector(state => state.appReducer.watchLaterMovies);
const dispatch = useDispatch();
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
paddingTop: 22, paddingTop: insets.top + 22,
backgroundColor: "#0E0E0E" backgroundColor: "#0E0E0E"
}, },
linearGradient: { linearGradient: {
@ -40,136 +63,73 @@ export default function WatchLaterScreen({navigation}: RootTabScreenProps<'Watch
height: 100, height: 100,
borderRadius: 8, borderRadius: 8,
}, },
searchSection: {
height: 40,
width: 400,
backgroundColor: "grey",
borderRadius: 20,
marginVertical: 10,
alignSelf: "center"
},
searchBar: {
width: '100%',
height: 40,
marginHorizontal: 20
},
titlePage: {
height: 50,
justifyContent: "flex-start",
flexDirection: 'row',
paddingHorizontal: 20,
marginBottom: 15,
marginVertical: 5,
alignItems: "flex-end"
},
icon: {
marginBottom: -5,
marginRight: 20
},
delimiter: {
height: 2,
width: 400,
resizeMode: "stretch"
},
h1: {
color: "white",
fontSize: 30
}
}); });
const dispatch = useDispatch();
// @ts-ignore
const watchLaterMovies = useSelector(state => state.appReducer.watchLaterMovies);
useEffect(() => { useEffect(() => {
const loadWatchLater = async () => { const loadWatchLater = async () => {
// @ts-ignore // @ts-ignore
await dispatch(getWatchLater()); await dispatch(getWatchLater());
}; };
console.log("test11111:", watchLaterMovies);
loadWatchLater(); loadWatchLater();
}, [dispatch]); }, [dispatch]);
const searchFilterFunction = (text: string) => {
if (text) {
const newData = watchLaterMovies.filter(function (item: Movie) {
const itemData = item.original_title
? item.original_title.toUpperCase()
: ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilteredDataSource(newData);
setSearch(text);
} else {
setFilteredDataSource(masterDataSource);
setSearch(text);
}
};
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<View style={{height: 50, justifyContent: "flex-start", flexDirection: 'row', paddingHorizontal: 20, marginBottom: 15, marginVertical: 5, alignItems: "flex-end"}}> <View style={styles.titlePage}>
<FontAwesomeIcon icon={faClock} style={{marginBottom: -5, marginRight: 20}} size={50} color="white"/> <FontAwesomeIcon icon={faClock} style={styles.icon} size={50} color="white"/>
<Text style={{color: "white", fontSize: 30}}>Watch Later</Text> <Text style={styles.h1}>Watch Later</Text>
</View> </View>
<Image <Image
source={require('../assets/images/delimiter.png')} style={{height: 2, width: 400, resizeMode: "stretch"}} source={require('../assets/images/delimiter.png')} style={styles.delimiter}
/> />
<View style={{height: 40, width: 400, backgroundColor: "grey", borderRadius: 20, marginVertical: 10, alignSelf: "center"}}> <View style={styles.searchSection}>
<TextInput style={{width: '100%', height: 40, marginHorizontal: 20}} onChangeText={(text) => searchFilterFunction(text)} <TextInput style={styles.searchBar} onChangeText={(text) => searchFilterFunction(text)}
value={search} value={search}
></TextInput> ></TextInput>
</View> </View>
<FlatList <FlatList
data={search.length !== 0 ? filteredDataSource : watchLaterMovies} data={search.length !== 0 ? filteredDataSource : watchLaterMovies}
keyExtractor={item => item.original_title} keyExtractor={item => item.original_title}
renderItem={({item}) => <TouchableHighlight onPress={() => navigation.navigate("Info", {"item": item})}><ListWidget movie={item}></ListWidget></TouchableHighlight>} // @ts-ignore
renderItem={({item}) => <TouchableHighlight onPress={() => navigation.navigate("Info", {"item": item})}><MovieListComponent movie={item}></MovieListComponent></TouchableHighlight>}
/> />
</SafeAreaView> </SafeAreaView>
); );
} }
type ListWidgetProps = {
movie: Movie
}
export function ListWidget(props: ListWidgetProps) {
const insets = useSafeAreaInsets();
const styles = StyleSheet.create({
filmCard: {
width: 70,
height: 110,
borderRadius: 8,
},
});
function formatTime(time: number) {
console.log(time);
const hours = Math.floor(time / 60);
const minutes = time % 60;
return `${hours}h ${minutes < 10 ? `0${minutes}` : minutes}m`;
}
return (
<LinearGradient style={{
height: 130,
borderRadius: 20,
justifyContent: "flex-start",
flexDirection: 'row',
marginHorizontal: 10,
marginVertical: 7,
paddingHorizontal: 10,
backgroundColor: "#1D1D1D",
alignItems: "center",
borderWidth: 1.5,
borderColor: "#1F1F1F"
}} start={{x: 0, y: 1}}
end={{x: 1, y: 1}}
// Button Linear Gradient
colors={['#0B0B0B', '#1F1F1F']}>
<Image
style={styles.filmCard}
source={{
uri: props.movie.poster_path_min,
}}
/>
<View style={{
height: 130,
width: "85%",
justifyContent: "center",
flexDirection: 'column',
paddingRight: 20,
paddingLeft: 20,
}}>
<Text numberOfLines={1} style={{
color: "white",
fontWeight: "700",
fontSize: 21,
}}>{props.movie.original_title}</Text>
<View style={{flexDirection: "row", alignItems: "center", justifyContent: "space-between", width: "100%"}}>
<View style={{flexDirection: "row", alignItems: "center",}}>
<Stars note={props.movie.vote_average} size={90}></Stars>
<Text style={{paddingLeft: 7, color: "white", fontWeight: "bold", fontSize: 13}}>{props.movie.vote_average.toFixed(1)}</Text>
</View>
<Text style={{color: "grey", fontWeight: "600"}}>{formatTime(props.movie.runtime)}</Text>
</View>
<Text numberOfLines={3} style={{color: "#C7C7C7", fontWeight: "600",}}>{props.movie.overview}</Text>
</View>
</LinearGradient>
);
}
Loading…
Cancel
Save