Skip to content

Commit e5af760

Browse files
authored
Merge pull request #6 from piashcse/enhance-loading
- Simplify loading with footer indicator
2 parents 6aa0cf4 + b2e5e28 commit e5af760

File tree

15 files changed

+121
-115
lines changed

15 files changed

+121
-115
lines changed

src/components/loading/Loading.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ import styles from './LoadingStyle';
44
import {COLOR} from '../../constant/Colors';
55

66
const Loading = () => {
7-
return (<View style={styles.containerLoading}>
8-
<View style={styles.containerIndicator}>
9-
<ActivityIndicator size={'large'} color={COLOR.primaryColor}/>
10-
<Text style={styles.loadingTextStyle}>Loading...</Text>
11-
</View>
12-
</View>);
7+
return (
8+
<View style={styles.containerLoading}>
9+
<View style={styles.containerIndicator}>
10+
<ActivityIndicator size={'large'} color={COLOR.primaryColor} />
11+
<Text style={styles.loadingTextStyle}>Loading...</Text>
12+
</View>
13+
</View>
14+
);
1315
};
1416

15-
export default Loading
17+
const FooterLoading = () => {
18+
return <ActivityIndicator size={'large'} color={COLOR.primaryColor} />;
19+
};
20+
21+
export {
22+
Loading,
23+
FooterLoading,
24+
};

src/components/movie-item/MovieItemComponent.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import React, {useState} from 'react';
2-
import {FlatList, Image, View, TouchableOpacity, ImageBackground} from "react-native";
2+
import {FlatList, Image, View, TouchableOpacity, ImageBackground, FlatListProps} from "react-native";
33
import styles from "./MovieItem.Style.ts";
44
import {Constants} from "../../constant/AppConstants";
55
import {MovieItem} from "../../types/MovieItem";
66

7-
interface MovieItemProps {
7+
interface MovieItemProps extends Omit<FlatListProps<MovieItem>, 'data' | 'renderItem'> {
88
movies: Array<MovieItem>;
99
onPress: (item: MovieItem) => void;
10-
loadMoreData: () => void
10+
loadMoreData: () => void;
1111
}
1212

13-
const MovieItemComponent = (props: MovieItemProps) => {
14-
const {movies, onPress, loadMoreData} = props;
13+
const MovieItemComponent = ({ movies, onPress, loadMoreData, ...rest }: MovieItemProps) => {
1514
const [isLoading, setIsLoading] = useState(true)
1615
const movieItem = ({item}: { item: MovieItem }) => {
1716
return (<TouchableOpacity style={styles.movieItemContainer} onPress={() => onPress(item)}>
@@ -41,6 +40,7 @@ const MovieItemComponent = (props: MovieItemProps) => {
4140
keyExtractor={(item, index) => index.toString()}
4241
onEndReachedThreshold={0.5}
4342
onEndReached={loadMoreData}
43+
{...rest}
4444
/>
4545
</View>);
4646
}

src/components/tvseries-item/TvSeriesItemComponent.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import React, {useState} from 'react';
2-
import {FlatList, Image, View, TouchableOpacity, ImageBackground} from "react-native";
2+
import {FlatList, Image, View, TouchableOpacity, ImageBackground, FlatListProps} from "react-native";
33
import styles from "./TvSeriesItem.Style.ts";
44
import {Constants} from "../../constant/AppConstants";
55
import {TvSeriesItem} from "../../types/TvSeriesItem.ts";
66

7-
interface TvSeriesItemProps {
7+
interface TvSeriesItemProps extends Omit<FlatListProps<TvSeriesItem>, 'data' | 'renderItem'> {
88
tvSeries: Array<TvSeriesItem>;
99
onPress: (item: TvSeriesItem) => void;
10-
loadMoreData: () => void
10+
loadMoreData: () => void;
1111
}
1212

13-
const MovieItemComponent = (props: TvSeriesItemProps) => {
14-
const {tvSeries, onPress, loadMoreData} = props;
13+
const MovieItemComponent = ({ tvSeries, onPress, loadMoreData, ...rest }: TvSeriesItemProps) => {
1514
const [isLoading, setIsLoading] = useState(true)
1615
const TvSeriesItem = ({item}: { item: TvSeriesItem }) => {
1716
return (<TouchableOpacity style={styles.movieItemContainer} onPress={() => onPress(item)}>
@@ -41,6 +40,7 @@ const MovieItemComponent = (props: TvSeriesItemProps) => {
4140
keyExtractor={(item, index) => index.toString()}
4241
onEndReachedThreshold={0.5}
4342
onEndReached={loadMoreData}
43+
{...rest}
4444
/>
4545
</View>);
4646
}

src/navigation/AppNavigation.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ const MovieBottomNavigation = () => {
4949
<MovieBottomTab.Screen name="NowPlaying" component={NowPlayingMovie} options={{
5050
tabBarLabel: 'NowPlaying',
5151
headerShown: false,
52-
tabBarIcon: ({color, size}) => (<MaterialIcons name="home" color={color} size={size}/>),
52+
tabBarIcon: ({color, size}) => (<MaterialIcons name="movie" color={color} size={size}/>),
5353
}}/>
5454
<MovieBottomTab.Screen name="Popular" component={PopularMovie} options={{
5555
tabBarLabel: 'Popular',
5656
headerShown: false,
57-
tabBarIcon: ({color, size}) => (<MaterialIcons name="timeline" color={color} size={size}/>),
57+
tabBarIcon: ({color, size}) => (<MaterialIcons name="favorite" color={color} size={size}/>),
5858
}}/>
5959
<MovieBottomTab.Screen name="TopRated" component={TopRatedMovie} options={{
6060
tabBarLabel: 'Top Rated',
@@ -73,7 +73,7 @@ const TvSeriesBottomNavigation = () => {
7373
<TvSeriesBottomTab.Screen name="AiringTodayTvSeries" component={AiringTodayTvSeries} options={{
7474
tabBarLabel: 'AiringToday',
7575
headerShown: false,
76-
tabBarIcon: ({color, size}) => (<MaterialIcons name="home" color={color} size={size}/>),
76+
tabBarIcon: ({color, size}) => (<MaterialIcons name="movie" color={color} size={size}/>),
7777
}} />
7878
<TvSeriesBottomTab.Screen name="OnTheAirTvSeries" component={OnTheAirTvSeries} options={{
7979
tabBarLabel: 'OnTheAir',
@@ -83,7 +83,7 @@ const TvSeriesBottomNavigation = () => {
8383
<TvSeriesBottomTab.Screen name="PopularTvSeries" component={PopularTvSeries} options={{
8484
tabBarLabel: 'Popular',
8585
headerShown: false,
86-
tabBarIcon: ({color, size}) => (<MaterialIcons name="star" color={color} size={size}/>),
86+
tabBarIcon: ({color, size}) => (<MaterialIcons name="favorite" color={color} size={size}/>),
8787
}}/>
8888
<TvSeriesBottomTab.Screen name="TopRatedTvSeries" component={UpComingTvSeries} options={{
8989
tabBarLabel: 'TopRated ',

src/redux/query/RTKQuery.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,20 @@ export const nowPlayingMovieApi = createApi({
1313
reducerPath: 'nowPlayingMovieApi',
1414
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.themoviedb.org/3/' }),
1515
endpoints: (builder) => ({
16-
getNowPlayingMovie: builder.query<MovieItem[], number>({
16+
nowPlayingMovie: builder.query<MovieItem[], number>({
1717
query: (page) => `movie/now_playing?page=${page}&language=en-US&api_key=${Constants.API_KEY}`,
1818
transformResponse: (response: MovieResult) => response.results,
1919
}),
2020
}),
2121
})
2222

23-
export const { useGetNowPlayingMovieQuery } = nowPlayingMovieApi;
23+
export const { useNowPlayingMovieQuery } = nowPlayingMovieApi;
2424

2525
export const popularMovieApi = createApi({
2626
reducerPath: 'popularMovieApi',
2727
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.themoviedb.org/3/' }),
2828
endpoints: (builder) => ({
29-
getPopularMovie: builder.query<MovieItem[], number>({
29+
popularMovie: builder.query<MovieItem[], number>({
3030
query: (page ) => `movie/popular?page=${page}&language=en-US&api_key=${Constants.API_KEY}`,
3131
transformResponse: (response: MovieResult) => {
3232
console.log('transform ', response.results)
@@ -37,33 +37,33 @@ export const popularMovieApi = createApi({
3737
})
3838

3939

40-
export const {useGetPopularMovieQuery} = popularMovieApi;
40+
export const {usePopularMovieQuery} = popularMovieApi;
4141

4242
export const topRatedMovieApi = createApi({
4343
reducerPath: 'topRatedMovieApi',
4444
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.themoviedb.org/3/' }),
4545
endpoints: (builder) => ({
46-
getTopRatedMovie: builder.query<MovieItem[], number>({
46+
topRatedMovie: builder.query<MovieItem[], number>({
4747
query: (page) => `movie/top_rated?page=${page}&language=en-US&api_key=${Constants.API_KEY}`,
4848
transformResponse: (response: MovieResult) => response.results
4949
}),
5050
}),
5151
})
52-
export const {useGetTopRatedMovieQuery} = topRatedMovieApi;
52+
export const {useTopRatedMovieQuery} = topRatedMovieApi;
5353

5454

5555
export const upcomingMovieApi = createApi({
5656
reducerPath: 'upcomingMovieApi',
5757
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.themoviedb.org/3/' }),
5858
endpoints: (builder) => ({
59-
getUpcomingMovie: builder.query<MovieItem[], number>({
59+
upcomingMovie: builder.query<MovieItem[], number>({
6060
query: (page) => `movie/upcoming?page=${page}&language=en-US&api_key=${Constants.API_KEY}`,
6161
transformResponse: (response: MovieResult) => response.results
6262
}),
6363
}),
6464
})
6565

66-
export const {useGetUpcomingMovieQuery} = upcomingMovieApi;
66+
export const {useUpcomingMovieQuery} = upcomingMovieApi;
6767

6868
export const movieDetailApi = createApi({
6969
reducerPath: 'movieDetailApi',

src/screens/movie/movie-detail/MovieDetail.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import Loading from '../../../components/loading/Loading.tsx';
2+
import {Loading} from '../../../components/loading/Loading.tsx';
33
import styles from './MovieDetail.Style.ts'
44
import {FlatList, Image, Text, TouchableOpacity, View, ScrollView} from "react-native";
55
import {Constants} from "../../../constant/AppConstants.ts";
@@ -23,9 +23,9 @@ const MovieDetail = () => {
2323
const navigation = useNavigation<MovieDetailNavigationProp>();
2424
const route = useRoute<RouteProp<{ params: RouteParams }, 'params'>>();
2525
const { movieId } = route.params;
26-
const { data: movieDetail, isLoading: isLoadingMovieDetail } = useGetMovieDetailQuery(Number(movieId))
27-
const { data: similarMovies, isLoading: isLoadingSimilarMovies } = useGetSimilarMovieQuery(Number(movieId))
28-
const { data: castAndCrew, isLoading: isLoadingCastAndCrew } = useGetArtistAndCrewQuery(Number(movieId))
26+
const { data: movieDetail, isFetching: isLoadingMovieDetail } = useGetMovieDetailQuery(Number(movieId))
27+
const { data: similarMovies, isFetching: isLoadingSimilarMovies } = useGetSimilarMovieQuery(Number(movieId))
28+
const { data: castAndCrew, isFetching: isLoadingCastAndCrew } = useGetArtistAndCrewQuery(Number(movieId))
2929

3030
const isLoading = isLoadingMovieDetail || isLoadingSimilarMovies || isLoadingCastAndCrew
3131

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React, {useEffect, useState} from 'react';
2-
import Loading from '../../../components/loading/Loading.tsx';
32
import MovieItemComponent from '../../../components/movie-item/MovieItemComponent.tsx';
43
import {View} from 'react-native';
54
import styles from './NowPlayingMovie.Style.ts'
6-
import {useGetNowPlayingMovieQuery} from "../../../redux/query/RTKQuery.ts";
5+
import {useNowPlayingMovieQuery} from "../../../redux/query/RTKQuery.ts";
76
import {NavigationProp, useNavigation} from "@react-navigation/native";
87
import {MovieItem} from "../../../types/MovieItem.ts";
8+
import {FooterLoading, Loading} from "../../../components/loading/Loading.tsx";
99

10-
type RootStackParamList = {
10+
type RootStackParamList = {
1111
MovieDetail: { movieId: number };
1212
};
1313
type NowPlayingMovieNavigationProp = NavigationProp<RootStackParamList, 'MovieDetail'>;
@@ -16,29 +16,29 @@ const NowPlayingMovie = () => {
1616
const navigation = useNavigation<NowPlayingMovieNavigationProp>();
1717
const [page, setPage] = useState(1);
1818
const [movies, setMovies] = useState<Array<MovieItem>>([]);
19-
const {data = [], error, isLoading, isFetching, isSuccess} = useGetNowPlayingMovieQuery(page)
19+
const {data = [], error, isFetching, isSuccess} = useNowPlayingMovieQuery(page)
2020

2121
useEffect(() => {
22-
if (data && page > 1) {
23-
setMovies((prevMovies) => [...prevMovies, ...data]);
24-
}else {
25-
setMovies(data ?? []);
22+
if (data.length) {
23+
setMovies((prevMovies) => page === 1 ? data : [...prevMovies, ...data]);
2624
}
2725
}, [isSuccess]);
2826

2927
const loadMoreMovies = () => {
30-
if (!isFetching && !isLoading && !error) {
28+
if (!isFetching && !error) {
3129
setPage((prevPage) => prevPage + 1);
3230
}
3331
};
3432

35-
if (isLoading) return <Loading/>;
33+
if (isFetching && page == 1) return <Loading/>;
3634

3735
return (<View style={styles.mainView}>
3836
<MovieItemComponent
3937
movies={movies}
4038
onPress={(item) => navigation.navigate('MovieDetail', {movieId: item.id})}
41-
loadMoreData={loadMoreMovies}/>
39+
loadMoreData={loadMoreMovies}
40+
ListFooterComponent={isFetching && page > 1 ? <FooterLoading/> : null}
41+
/>
4242
</View>);
4343
}
4444
export default NowPlayingMovie
Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, {useEffect, useState} from 'react';
2-
import Loading from '../../../components/loading/Loading.tsx';
32
import MovieItemComponent from '../../../components/movie-item/MovieItemComponent.tsx';
43
import styles from './PopularMovie.Style.ts'
54
import {View} from "react-native";
6-
import {useGetPopularMovieQuery} from "../../../redux/query/RTKQuery.ts";
5+
import {usePopularMovieQuery} from "../../../redux/query/RTKQuery.ts";
76
import {NavigationProp, useNavigation} from "@react-navigation/native";
87
import {MovieItem} from "../../../types/MovieItem.ts";
8+
import {FooterLoading, Loading} from "../../../components/loading/Loading.tsx";
99

1010
type RootStackParamList = {
1111
MovieDetail: { movieId: number };
@@ -16,28 +16,29 @@ const PopularMovie = () => {
1616
const navigation = useNavigation<PopularMovieNavigationProp>();
1717
const [page, setPage] = useState(1);
1818
const [movies, setMovies] = useState<Array<MovieItem>>([]);
19-
const {data = [], error, isLoading, isFetching, isSuccess} = useGetPopularMovieQuery(page)
19+
const {data = [], error, isFetching, isSuccess} = usePopularMovieQuery(page)
2020

2121
useEffect(() => {
22-
if (data && page > 1) {
23-
setMovies((prevMovies) => [...prevMovies, ...data]);
24-
}else {
25-
setMovies(data ?? []);
22+
if (data.length) {
23+
setMovies((prevMovies) => page === 1 ? data : [...prevMovies, ...data]);
2624
}
2725
}, [isSuccess]);
2826

2927
const loadMoreMovies = () => {
30-
if (!isFetching && !isLoading && !error) {
28+
if (!isFetching && !error) {
3129
setPage((prevPage) => prevPage + 1);
3230
}
3331
};
3432

35-
if (isLoading) return <Loading/>;
33+
if (isFetching && page == 1) return <Loading/>;
34+
3635
return (<View style={styles.mainView}>
3736
<MovieItemComponent
3837
movies={movies}
3938
onPress={(item) => navigation.navigate('MovieDetail', {movieId: item.id})}
40-
loadMoreData={loadMoreMovies}/>
39+
loadMoreData={loadMoreMovies}
40+
ListFooterComponent={isFetching && page > 1 ? <FooterLoading/> : null}
41+
/>
4142
</View>);
4243
}
4344
export default PopularMovie
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, {useEffect, useState} from 'react';
2-
import Loading from '../../../components/loading/Loading.tsx';
2+
import {FooterLoading, Loading} from '../../../components/loading/Loading.tsx';
33
import MovieItemComponent from '../../../components/movie-item/MovieItemComponent.tsx';
44
import styles from './TopRatedMovie.Style.ts'
55
import {View} from "react-native";
6-
import { useGetTopRatedMovieQuery} from "../../../redux/query/RTKQuery.ts";
6+
import { useTopRatedMovieQuery} from "../../../redux/query/RTKQuery.ts";
77
import {NavigationProp, useNavigation} from "@react-navigation/native";
88
import {MovieItem} from "../../../types/MovieItem.ts";
99

@@ -16,29 +16,29 @@ const TopRatedMovie = () => {
1616
const navigation = useNavigation<TopRatedMovieNavigationProp>();
1717
const [page, setPage] = useState(1);
1818
const [movies, setMovies] = useState<Array<MovieItem>>([]);
19-
const {data = [], error, isLoading, isFetching, isSuccess} = useGetTopRatedMovieQuery(page);
19+
const {data = [], error, isFetching, isSuccess} = useTopRatedMovieQuery(page);
2020

2121
useEffect(() => {
22-
if (data && page > 1) {
23-
setMovies((prevMovies) => [...prevMovies, ...data]);
24-
}else {
25-
setMovies(data ?? []);
22+
if (data.length) {
23+
setMovies((prevMovies) => page === 1 ? data : [...prevMovies, ...data]);
2624
}
2725
}, [isSuccess]);
2826

2927
const loadMoreMovies = () => {
30-
if (!isFetching && !isLoading && !error) {
28+
if (!isFetching && !error) {
3129
setPage((prevPage) => prevPage + 1);
3230
}
3331
};
3432

35-
if (isLoading) return <Loading/>;
33+
if (isFetching && page == 1) return <Loading/>;
34+
3635
return (<View style={styles.mainView}>
3736
<MovieItemComponent
3837
movies={movies}
38+
onPress={(item) => navigation.navigate('MovieDetail', {movieId: item.id})}
3939
loadMoreData={loadMoreMovies}
40-
onPress={(item) => navigation.navigate('MovieDetail', {movieId: item.id})}/>
41-
{isLoading && <Loading/>}
40+
ListFooterComponent={isFetching && page > 1 ? <FooterLoading/> : null}
41+
/>
4242
</View>);
4343
}
4444
export default TopRatedMovie

0 commit comments

Comments
 (0)