Skip to content

Commit

Permalink
feat: add refresh control to refetch list (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
hoangvu12 authored Nov 30, 2023
1 parent dfc3987 commit 9e067c7
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 70 deletions.
13 changes: 11 additions & 2 deletions src/screens/anime/airing-schedule/screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useGraphQL } from '@/hooks/use-graphql';
import DetailsCard from '@/screens/search/components/details-card';
import { ActivityIndicator, Button, groupBy, Text, View } from '@/ui';
import Pressable from '@/ui/core/pressable';
import RefreshControl from '@/ui/core/refresh-control';
import colors from '@/ui/theme/colors';

dayjs.extend(duration);
Expand Down Expand Up @@ -80,7 +81,12 @@ const AiringScheduleScreen = () => {
return days;
}, []);

const { data: schedules, isLoading } = useGraphQL(document, {
const {
data: schedules,
isLoading,
isRefetching,
refetch,
} = useGraphQL(document, {
airingAt_greater,
airingAt_lesser,
});
Expand Down Expand Up @@ -205,7 +211,7 @@ const AiringScheduleScreen = () => {
ItemSeparatorComponent={() => <View className="w-4" />}
/>

{isLoading ? (
{isLoading || isRefetching ? (
<ActivityIndicator
size={48}
color={colors.primary[500]}
Expand All @@ -218,6 +224,9 @@ const AiringScheduleScreen = () => {
data={Object.entries(schedulesWithTime)?.filter(([, list]) => {
return list.length > 0;
})}
refreshControl={
<RefreshControl refreshing={isRefetching} onRefresh={refetch} />
}
renderItem={({ item }) => {
const [time, list] = item;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useAtomValue, useSetAtom } from 'jotai/react';
import { RotateCwIcon } from 'lucide-react-native';
import React, { useEffect, useMemo } from 'react';
import { Else, If, Then } from 'react-if';

import { type FragmentType, graphql, useFragment } from '@/gql';
import { getWatchedEpisode } from '@/storage/episode';
import { currentModuleIdAtom } from '@/store';
import { ActivityIndicator, colors, Text, View } from '@/ui';
import Pressable from '@/ui/core/pressable';

import useEpisodes from '../hooks/use-episodes';
import { episodeChunkAtom, sectionEpisodesAtom } from '../store';
Expand Down Expand Up @@ -37,7 +39,7 @@ const EpisodeContainer: React.FC<EpisodeContainerProps> = ({
const media = useFragment(EpisodeContainerFragment, mediaFragment);
const currentModuleId = useAtomValue(currentModuleIdAtom);

const { data, isLoading } = useEpisodes(media);
const { data, isLoading, isRefetching, refetch } = useEpisodes(media);
const setSectionEpisodes = useSetAtom(sectionEpisodesAtom);
const setEpisodeChunk = useSetAtom(episodeChunkAtom);

Expand Down Expand Up @@ -89,12 +91,20 @@ const EpisodeContainer: React.FC<EpisodeContainerProps> = ({
<View>
<EpisodeLayoutSelector />
</View>

<Pressable
onPress={() => {
refetch();
}}
>
<RotateCwIcon size={24} color="white" />
</Pressable>
</View>

{currentModuleId ? (
<If condition={isLoading}>
<If condition={isLoading || isRefetching}>
<Then>
<View>
<View className="mt-8">
<ActivityIndicator color={colors.primary[500]} size={48} />
</View>
</Then>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useNavigation } from '@react-navigation/native';
import { FlashList } from '@shopify/flash-list';
import { useAtomValue } from 'jotai/react';
import React, { useMemo } from 'react';

Expand Down Expand Up @@ -71,8 +72,10 @@ const EpisodeLayoutContainer: React.FC<{
) : null}

{layoutMode === 'details' ? (
<View className="space-y-4">
{episodes.map((episode) => {
<FlashList
data={episodes}
estimatedItemSize={161}
renderItem={({ item: episode }) => {
const episodeNumber = parseInt(episode.number, 10);

return (
Expand All @@ -91,23 +94,25 @@ const EpisodeLayoutContainer: React.FC<{
}
/>
);
})}
</View>
}}
keyExtractor={(item) => item.id}
ItemSeparatorComponent={() => <View className="my-3" />}
/>
) : null}

{layoutMode === 'card' ? (
<View className="flex flex-row flex-wrap justify-between">
{episodes.map((episode, index) => {
<FlashList
data={episodes}
numColumns={2}
renderItem={({ item: episode, index }) => {
const episodeNumber = parseInt(episode.number, 10);

return (
<View
style={{
width: width / 2 - PADDING - SPACE_BETWEEN,
marginBottom:
index < episodes.length - 2 ? SPACE_BETWEEN * 2 : 0,
width: width / 2 - SPACE_BETWEEN - PADDING,
marginLeft: index % 2 !== 0 ? SPACE_BETWEEN : 0,
}}
key={episode.id}
>
<EpisodeCard
onPress={() => {
Expand All @@ -124,8 +129,17 @@ const EpisodeLayoutContainer: React.FC<{
/>
</View>
);
})}
</View>
}}
keyExtractor={(item) => item.id}
ItemSeparatorComponent={() => (
<View
style={{
margin: SPACE_BETWEEN,
}}
/>
)}
estimatedItemSize={130}
/>
) : null}
</React.Fragment>
);
Expand Down
105 changes: 60 additions & 45 deletions src/screens/anime/recently-watched/screen.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { FlashList } from '@shopify/flash-list';
import React from 'react';
import { Else, If, Then } from 'react-if';

import { useFragment } from '@/gql';
import useScreenSize from '@/hooks/use-screen-size';
import { useWatched } from '@/hooks/use-watched';
import { ActivityIndicator, Text, View } from '@/ui';
import { Card, CardFragment } from '@/ui/card';
import RefreshControl from '@/ui/core/refresh-control';
import colors from '@/ui/theme/colors';

const SPACE_BETWEEN = 32;
Expand All @@ -15,62 +17,75 @@ const LIST_PADDING = 16;
const RecentlyWatchedScreen = () => {
const { width } = useScreenSize();

const { data, isLoading } = useWatched(0);

if (isLoading)
return (
<View className="flex-1 items-center justify-center">
<ActivityIndicator size={48} color={colors.primary[500]} />
</View>
);

if (!data?.length) {
return (
<View className="flex-1 items-center justify-center">
<Text className="text-center">You haven't watched anything yet?</Text>
</View>
);
}
const { data, isLoading, refetch, isRefetching } = useWatched(0);

return (
<View
className="h-full w-full flex-1 p-4"
style={{ marginLeft: LIST_PADDING, flex: 1 }}
>
<Text className="mb-8 text-3xl" weight="semibold">
<View className="h-full w-full flex-1 p-4" style={{ flex: 1 }}>
<Text className="mb-4 text-2xl" weight="semibold">
Recently watched
</Text>

<FlashList
numColumns={2}
data={data}
renderItem={({ item, index }) => {
return (
<If condition={isLoading || isRefetching}>
<Then>
<ActivityIndicator size={48} color={colors.primary[500]} />
</Then>

<Else>
<If condition={!data?.length}>
<Then>
<Text className="text-center">You haven't watched anything.</Text>
</Then>
</If>

<Else>
<View
style={{
width:
width / 2 -
CONTAINER_PADDING -
LIST_PADDING -
SPACE_BETWEEN / 2,
marginLeft:
index % 2 !== 0 ? SPACE_BETWEEN - LIST_PADDING * 2 : 0,
marginBottom: SPACE_BETWEEN,
marginLeft: LIST_PADDING / 2 + SPACE_BETWEEN / 2,
flex: 1,
}}
>
<Card
media={item.media}
containerProps={{ className: 'w-full' }}
<FlashList
numColumns={2}
data={data}
renderItem={({ item, index }) => {
return (
<View
style={{
width:
width / 2 -
CONTAINER_PADDING -
LIST_PADDING -
SPACE_BETWEEN / 2,
marginLeft:
index % 2 !== 0
? SPACE_BETWEEN - LIST_PADDING * 2
: 0,
marginBottom: SPACE_BETWEEN,
}}
>
<Card
media={item.media}
containerProps={{ className: 'w-full' }}
/>
</View>
);
}}
refreshControl={
<RefreshControl
refreshing={isRefetching}
onRefresh={refetch}
/>
}
estimatedItemSize={294}
keyExtractor={(item) =>
// eslint-disable-next-line react-hooks/rules-of-hooks
useFragment(CardFragment, item.media).id.toString()
}
/>
</View>
);
}}
estimatedItemSize={294}
keyExtractor={(item) =>
// eslint-disable-next-line react-hooks/rules-of-hooks
useFragment(CardFragment, item.media).id.toString()
}
/>
</Else>
</Else>
</If>
</View>
);
};
Expand Down
13 changes: 12 additions & 1 deletion src/screens/search/components/details-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@ import React from 'react';

import { type FragmentType, useFragment } from '@/gql';
import { View } from '@/ui';
import RefreshControl from '@/ui/core/refresh-control';

import { DetailsCardFragment } from './details-card';
import DetailsCard from './details-card';

interface DetailsListProps {
mediaList: readonly FragmentType<typeof DetailsCardFragment>[];
onLoadMore: () => void;
refetch: () => void;
isRefetching: boolean;
}

const DetailsList: React.FC<DetailsListProps> = ({ mediaList, onLoadMore }) => {
const DetailsList: React.FC<DetailsListProps> = ({
mediaList,
onLoadMore,
isRefetching,
refetch,
}) => {
return (
<FlashList
data={mediaList}
Expand All @@ -24,6 +32,9 @@ const DetailsList: React.FC<DetailsListProps> = ({ mediaList, onLoadMore }) => {

return media.id.toString();
}}
refreshControl={
<RefreshControl refreshing={isRefetching} onRefresh={refetch} />
}
onEndReached={onLoadMore}
onEndReachedThreshold={0.05}
ListFooterComponentStyle={{ paddingBottom: 300 }}
Expand Down
13 changes: 12 additions & 1 deletion src/screens/search/components/grid-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { type FragmentType, useFragment } from '@/gql';
import useScreenSize from '@/hooks/use-screen-size';
import { View } from '@/ui';
import { Card, CARD_WIDTH, CardFragment } from '@/ui/card';
import RefreshControl from '@/ui/core/refresh-control';

interface GridListProps {
mediaList: readonly FragmentType<typeof CardFragment>[];
onLoadMore: () => void;
refetch: () => void;
isRefetching: boolean;
}

const SPACE_BETWEEN = 32;
Expand All @@ -17,7 +20,12 @@ const LIST_PADDING = 16;
const CARD_HEIGHT = CARD_WIDTH * (3 / 2);
const CARD_TITLE_HEIGHT = 60;

const GridList: React.FC<GridListProps> = ({ mediaList, onLoadMore }) => {
const GridList: React.FC<GridListProps> = ({
mediaList,
onLoadMore,
isRefetching,
refetch,
}) => {
const { width } = useScreenSize();

return (
Expand All @@ -44,6 +52,9 @@ const GridList: React.FC<GridListProps> = ({ mediaList, onLoadMore }) => {
<Card media={item} containerProps={{ className: 'w-full' }} />
</View>
)}
refreshControl={
<RefreshControl refreshing={isRefetching} onRefresh={refetch} />
}
estimatedItemSize={294}
onEndReached={onLoadMore}
onEndReachedThreshold={0.05}
Expand Down
Loading

0 comments on commit 9e067c7

Please sign in to comment.