Commit c449c05c authored by ErlendHer's avatar ErlendHer
Browse files

#15 add more sound loading spinner logic and fix css errors.

parent 1afca964
...@@ -59,7 +59,7 @@ const FilterPane: React.FC<FilterPaneProps> = ({ ...@@ -59,7 +59,7 @@ const FilterPane: React.FC<FilterPaneProps> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
marginTop: 96, marginTop: 8,
marginHorizontal: 16, marginHorizontal: 16,
width: '95%', width: '95%',
display: 'flex', display: 'flex',
......
import { StatusBar } from 'expo-status-bar';
import * as React from 'react'; import * as React from 'react';
import { Platform, StyleSheet, Image, useWindowDimensions, Button, ScrollView } from 'react-native'; import {
Button,
Image,
ScrollView,
StyleSheet,
useWindowDimensions,
} from 'react-native';
import Lord from '../assets/images/lord.jpg';
import { MovieDetail } from '../models/movieDetail.model'; import { MovieDetail } from '../models/movieDetail.model';
import ProfileInfo from './ProfileInfo';
import { Text, View } from './Themed';
import Lord from "../assets/images/lord.jpg";
import { movies } from '../store/ducks/movies';
import KeyStatisticsItem from './KeyStatisticsItem'; import KeyStatisticsItem from './KeyStatisticsItem';
import Seperator from './Seperator';
import Pill from './Pill'; import Pill from './Pill';
import WrapperStatistic from 'antd/lib/statistic/Statistic'; import Seperator from './Seperator';
import { Text, View } from './Themed';
interface MovieDetailProps { interface MovieDetailProps {
movieDetail: MovieDetail; movieDetail: MovieDetail;
} }
export default function MovieDetailScreen({movieDetail}: MovieDetailProps) { export default function MovieDetailScreen({ movieDetail }: MovieDetailProps) {
const {height} = useWindowDimensions(); const { height } = useWindowDimensions();
const styles = StyleSheet.create({ const styles = StyleSheet.create({
root: { root: {
...@@ -28,10 +30,10 @@ export default function MovieDetailScreen({movieDetail}: MovieDetailProps) { ...@@ -28,10 +30,10 @@ export default function MovieDetailScreen({movieDetail}: MovieDetailProps) {
height: 350, height: 350,
}, },
container: { container: {
width: "100%", width: '100%',
padding: 15, padding: 15,
marginVertical: 5, marginVertical: 5,
alignItems: "center", alignItems: 'center',
borderRadius: 5, borderRadius: 5,
}, },
subText: { subText: {
...@@ -45,98 +47,101 @@ export default function MovieDetailScreen({movieDetail}: MovieDetailProps) { ...@@ -45,98 +47,101 @@ export default function MovieDetailScreen({movieDetail}: MovieDetailProps) {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
flexWrap: 'wrap', flexWrap: 'wrap',
alignItems: 'flex-start' alignItems: 'flex-start',
}, },
genres: { genres: {
flex: 1, flex: 1,
flexWrap: "wrap", flexWrap: 'wrap',
flexDirection: "row", flexDirection: 'row',
marginTop: 15, marginTop: 15,
paddingBottom: "1rem", paddingBottom: '1rem',
},
} });
})
return ( return (
<ScrollView style={styles.root}> <ScrollView style={styles.root}>
<View style={{alignItems: "center"}}> <View style={{ alignItems: 'center' }}>
<Image source={Lord} style={[styles.logo, {height: height*0.5}]} resizeMode="contain"/> <Image
</View> source={Lord}
<View style={{alignItems: "center"}}> style={[styles.logo, { height: height * 0.5 }]}
Stars -{'>'} To be implemented resizeMode="contain"
</View> />
<View> </View>
<Button onPress={() => console.log("To be implemented")} title={"Give a rating"}/> <View style={{ alignItems: 'center' }}>
</View> Stars -{'>'} To be implemented
<View style={styles.key_number_container}> </View>
<KeyStatisticsItem title="RUNTIME" statistics={String(movieDetail.runtime)}/> <View>
<KeyStatisticsItem title="IMDG-RATING" statistics={String(movieDetail.avgRating)}/> <Button
<KeyStatisticsItem title="RATING" statistics={String(movieDetail.avgRating)}/> onPress={() => console.log('To be implemented')}
<KeyStatisticsItem title="PGA-RATING" statistics={String(movieDetail.avgRating)}/> title={'Give a rating'}
</View> />
<Seperator/> </View>
<View> <View style={styles.key_number_container}>
<Text style={{fontSize: 22}}> <KeyStatisticsItem
{movieDetail.year} title="RUNTIME"
</Text> statistics={String(movieDetail.runtime)}
<Text style={{fontSize: 18}}> />
{movieDetail.title} <KeyStatisticsItem
</Text> title="IMDG-RATING"
<View style={styles.genres}> statistics={String(movieDetail.avgRating)}
{movieDetail.genres.map((genre) => <Pill text={genre}>genre</Pill>)} />
</View> <KeyStatisticsItem
</View> title="RATING"
<Seperator/> statistics={String(movieDetail.avgRating)}
<Text> />
{movieDetail.plot} <KeyStatisticsItem
</Text> title="PGA-RATING"
<Seperator/> statistics={String(movieDetail.avgRating)}
{LabelAndText("DIRECTORS", "Joshua King")} />
<Seperator/> </View>
{LabelAndText("PRODUCTION", "Paramount Pictures, W365")} <Seperator />
<Seperator/> <View>
{LabelAndText("WRITERS", "Phil Hay, Matt Manfredi, Peter Chung")} <Text style={{ fontSize: 22 }}>{movieDetail.year}</Text>
<Seperator/> <Text style={{ fontSize: 18 }}>{movieDetail.title}</Text>
{LabelAndText("STARRING ACTORS", "Charlize Theron, Frances McDormand, Sophie Okonedo")} <View style={styles.genres}>
<Seperator/> {movieDetail.genres.map((genre) => (
<Pill text={genre}>genre</Pill>
))}
</View>
</View>
<Seperator />
<Text>{movieDetail.plot}</Text>
<Seperator />
{LabelAndText('DIRECTORS', 'Joshua King')}
<Seperator />
{LabelAndText('PRODUCTION', 'Paramount Pictures, W365')}
<Seperator />
{LabelAndText('WRITERS', 'Phil Hay, Matt Manfredi, Peter Chung')}
<Seperator />
{LabelAndText(
'STARRING ACTORS',
'Charlize Theron, Frances McDormand, Sophie Okonedo'
)}
<Seperator />
<View> <View>Reviews -{'>'} To be implemented</View>
Reviews -{'>'} To be implemented
</View>
</ScrollView> </ScrollView>
); );
} }
function LabelAndText(label: string, text: string){ function LabelAndText(label: string, text: string) {
const styles = StyleSheet.create({
const styles= StyleSheet.create({
label: { label: {
textTransform: "capitalize", textTransform: 'capitalize',
fontWeight: "bold", fontWeight: 'bold',
marginRight: 5, marginRight: 5,
}, },
container: { container: {
flex: 1, flex: 1,
flexWrap: "wrap", flexWrap: 'wrap',
flexDirection: "row", flexDirection: 'row',
} },
}) });
return( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.label}> <Text style={styles.label}>{label}:</Text>
{label}: <Text>{text}</Text>
</Text>
<Text>
{text}
</Text>
</View> </View>
) );
}
}
\ No newline at end of file
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from 'react';
import { FlatList, StyleSheet, Text, View } from "react-native"; import {
import { SafeAreaView } from "react-native-safe-area-context"; ActivityIndicator,
import { useSelector } from "react-redux"; FlatList,
import { FetchMovieParams, MovieEntity } from "../store/ducks/movies/types"; StyleSheet,
import { ApplicationState } from "../store/interface"; Text,
import MovieCard from "./MovieCard"; View,
} from 'react-native';
import { useSelector } from 'react-redux';
import { FetchMovieParams, MovieEntity } from '../store/ducks/movies/types';
import { ApplicationState } from '../store/interface';
import MovieCard from './MovieCard';
export type MovieTableProps = { export type MovieTableProps = {
path: string; path: string;
query: FetchMovieParams; query: FetchMovieParams;
movies: MovieEntity[]; movies: MovieEntity[];
currentPage: number; currentPage: number;
moviesLoading: boolean;
onPageScroll: () => void; onPageScroll: () => void;
}; };
...@@ -18,59 +24,95 @@ const MovieTable: React.FC<MovieTableProps> = ({ ...@@ -18,59 +24,95 @@ const MovieTable: React.FC<MovieTableProps> = ({
path, path,
query, query,
movies, movies,
moviesLoading,
currentPage, currentPage,
onPageScroll, onPageScroll,
}: MovieTableProps) => { }: MovieTableProps) => {
const { data, documentCount, loading, error } = useSelector( const { documentCount } = useSelector(
({ movies }: ApplicationState) => movies ({ movies }: ApplicationState) => movies
); );
const [isFull, setIsfull] = useState(false);
useEffect(() => {
setIsfull(!(movies.length < documentCount));
}, [movies]);
return ( return (
<View style={styles.movieList}> <View style={styles.movieList}>
<FlatList {movies.length > 0 || moviesLoading ? (
style={{ width: "100%", height: "100%" }} <FlatList
columnWrapperStyle={{ flex: 1, justifyContent: "space-around" }} style={styles.movieFlatList}
contentContainerStyle={styles.movieItems} columnWrapperStyle={{ flex: 1, justifyContent: 'space-around' }}
data={movies} contentContainerStyle={styles.movieItems}
numColumns={2} data={movies}
keyExtractor={(movie) => movie.id} numColumns={2}
onEndReached={({ distanceFromEnd }) => { keyExtractor={(movie) => movie.id}
// Prevent bug where onEndReached is called multiple times onEndReached={({ distanceFromEnd }) => {
if (distanceFromEnd < 0) return; // Prevent bug where onEndReached is called multiple times
// Prevent fetching if all movies are loaded if (distanceFromEnd < 0) return;
if (currentPage >= Math.ceil(documentCount / query.perPage)) return; // Prevent fetching if all movies are loaded
// Dispatch scroll event to parent component if (currentPage >= Math.ceil(documentCount / query.perPage)) {
onPageScroll(); setIsfull(true);
}} return;
onEndReachedThreshold={1.5} }
initialNumToRender={query.perPage} setIsfull(false);
showsVerticalScrollIndicator={false} // Dispatch scroll event to parent component
renderItem={({ item }) => <MovieCard movie={item} />} onPageScroll();
ListFooterComponent={() => ( }}
<>{loading && <Text style={styles.loading}>Loading...</Text>}</> onEndReachedThreshold={1.5}
)} initialNumToRender={query.perPage}
/> showsVerticalScrollIndicator={false}
renderItem={({ item }) => <MovieCard movie={item} />}
ListFooterComponent={() => (
<>
{!isFull ? (
<>
<Text style={styles.loading}>Loading...</Text>
<ActivityIndicator size="large" color="#00ff00" />
</>
) : (
<>
{movies.length > 4 && (
<Text style={styles.loading}>--- No more movies ---</Text>
)}
</>
)}
</>
)}
/>
) : (
<Text style={styles.loading}>No Movies Found</Text>
)}
</View> </View>
); );
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
movieItems: { movieItems: {
justifyContent: "center", justifyContent: 'center',
flexGrow: 1 / 2, flexGrow: 1 / 2,
backgroundColor: "white", backgroundColor: 'white',
},
movieFlatList: {
width: '100%',
height: '100%',
marginBottom: 4,
}, },
movieList: { movieList: {
marginTop: 4, marginTop: 4,
width: "95%", width: '95%',
height: "95%", height: '95%',
display: "flex", alignItems: 'center',
alignItems: "center", textAlign: 'center',
textAlign: "center", flex: 1,
}, },
loading: { loading: {
fontWeight: "bold", flex: 1,
marginBottom: 8, fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
textAlign: 'center',
}, },
}); });
......
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from 'react';
import { StyleSheet } from "react-native"; import { StyleSheet } from 'react-native';
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from 'react-redux';
import FilterPane, { FilterPaneProps } from "../components/FilterPane"; import FilterPane, { FilterPaneProps } from '../components/FilterPane';
import MovieTable, { MovieTableProps } from "../components/MovieTable"; import MovieTable, { MovieTableProps } from '../components/MovieTable';
import { View } from "../components/Themed"; import { View } from '../components/Themed';
import { import {
FilterKeys, FilterKeys,
FilterValues, FilterValues,
SortDirection, SortDirection,
SortKeys, SortKeys,
} from "../constants/filterOptions/interface"; } from '../constants/filterOptions/interface';
import { fetchMovies } from "../store/ducks/movies/actions"; import { fetchMovies } from '../store/ducks/movies/actions';
import { import {
FetchMovieParams, FetchMovieParams,
initialQuery, initialQuery,
MovieEntity, MovieEntity,
} from "../store/ducks/movies/types"; } from '../store/ducks/movies/types';
import { ApplicationState } from "../store/interface"; import { ApplicationState } from '../store/interface';
import { RootTabScreenProps } from "../types"; import { RootTabScreenProps } from '../types';
export default function MovieTableScreen({ export default function MovieTableScreen({
navigation, navigation,
}: RootTabScreenProps<"Movies">) { }: RootTabScreenProps<'Movies'>) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [currentPage, setCurrentPage] = useState<number>(1); const [currentPage, setCurrentPage] = useState<number>(1);
const [movies, setMovies] = useState<MovieEntity[]>([]); const [movies, setMovies] = useState<MovieEntity[]>([]);
const [query, setQuery] = useState<FetchMovieParams>(initialQuery); const [query, setQuery] = useState<FetchMovieParams>(initialQuery);
const [moviesLoading, setMoviesLoading] = useState<boolean>(false);
const { data, loading, error } = useSelector( const { data, loading, error } = useSelector(
({ movies }: ApplicationState) => movies ({ movies }: ApplicationState) => movies
...@@ -50,6 +51,7 @@ export default function MovieTableScreen({ ...@@ -50,6 +51,7 @@ export default function MovieTableScreen({
}); });
setMovies([...moviesToAdd]); setMovies([...moviesToAdd]);
} }
setMoviesLoading(false);
// Reset loaded content when component unmounts // Reset loaded content when component unmounts
return () => { return () => {
setMovies([]); setMovies([]);
...@@ -60,6 +62,7 @@ export default function MovieTableScreen({ ...@@ -60,6 +62,7 @@ export default function MovieTableScreen({
// Update the query state with new values // Update the query state with new values
// Reset pagination increments and loaded movies, if reset is true // Reset pagination increments and loaded movies, if reset is true
function updateQuery(newQuery: FetchMovieParams, reset = true) { function updateQuery(newQuery: FetchMovieParams, reset = true) {
setMoviesLoading(true);
let { page, ...other } = newQuery; let { page, ...other } = newQuery;
if (reset) { if (reset) {
setMovies([]); setMovies([]);
...@@ -117,10 +120,11 @@ export default function MovieTableScreen({ ...@@ -117,10 +120,11 @@ export default function MovieTableScreen({
// Map properties to the movie table component // Map properties to the movie table component
const mapStateToMovieTableProps: MovieTableProps = { const mapStateToMovieTableProps: MovieTableProps = {
path: "/screens/TabOneScreen.tsx", path: '/screens/TabOneScreen.tsx',
query, query,
movies, movies,
currentPage, currentPage,
moviesLoading,
onPageScroll: useCallback(() => handlePageScroll(), [query]), onPageScroll: useCallback(() => handlePageScroll(), [query]),
}; };
...@@ -135,19 +139,18 @@ export default function MovieTableScreen({ ...@@ -135,19 +139,18 @@ export default function MovieTableScreen({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
display: "flex", flexDirection: 'column',
flexDirection: "column", alignItems: 'center',
alignItems: "center", justifyContent: 'center',
justifyContent: "center", width: '100%',
width: "100%",
}, },
title: { title: {
fontSize: 20, fontSize: 20,
fontWeight: "bold", fontWeight: 'bold',
}, },
separator: { separator: {
marginVertical: 30, marginVertical: 30,
height: 1, height: 1,
width: "90%", width: '90%',
}, },
}); });
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment