Skip to content

Commit 27f47ea

Browse files
committed
feat: for you home heaeder title
1 parent eadb0d5 commit 27f47ea

File tree

11 files changed

+260
-15
lines changed

11 files changed

+260
-15
lines changed

app/(tabs)/_layout.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import SVG from '@/assets/svg';
2+
import ForYouTitle from '@/components/atoms/ForYouTitle';
23
import TabBarIcon from '@/components/atoms/TabBarIcon';
34
import Typography from '@/components/atoms/Typography';
45
import { Tabs } from 'expo-router';
@@ -13,7 +14,8 @@ export default function AuthStackLayout() {
1314
tabBarActiveTintColor: '#FFFFFF',
1415
tabBarLabelStyle: {
1516
fontFamily: 'sf-pro-rounded-medium'
16-
}
17+
},
18+
headerTransparent: true
1719
}}
1820
>
1921
<Tabs.Screen
@@ -22,7 +24,8 @@ export default function AuthStackLayout() {
2224
title: 'Home',
2325
tabBarIcon: ({ focused }) => (
2426
<TabBarIcon Icon={SVG.Home} focused={focused} width={20} height={21} />
25-
)
27+
),
28+
headerTitle: () => <ForYouTitle />
2629
}}
2730
/>
2831
<Tabs.Screen

app/(tabs)/home.tsx

+100-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,102 @@
1-
import { View, Text } from "react-native";
1+
import { Text, SafeAreaView, View, FlatList, ViewabilityConfig, ViewToken } from 'react-native';
2+
import { useHeaderHeight } from '@react-navigation/elements';
3+
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
4+
import { useInfiniteQuery } from '@tanstack/react-query';
5+
import getNextQuestion from '@/query/getNextQuestion';
6+
import { Image } from 'expo-image';
7+
import { useCallback, useEffect, useState } from 'react';
8+
import { cssInterop } from 'nativewind';
9+
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '@/utils/sizeMatters';
10+
11+
const blurhash =
12+
'|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[';
213

314
export default function Home() {
4-
return <View>
5-
<Text>Home Screen</Text>
6-
</View>
7-
}
15+
const headerHeight = useHeaderHeight();
16+
const tabBarHeight = useBottomTabBarHeight();
17+
const [currentPage, setCurrentPage] = useState(0);
18+
19+
// Tanstack Query has a convenient useQuery hook that works for infinite paginating.
20+
// This comes in handle in particular here since the GET multiple choice questions
21+
// endpoint doesn't support pages (getting more than one per request)
22+
const { data, error, fetchNextPage, status, isFetching, isFetchingNextPage, isLoading } =
23+
useInfiniteQuery({
24+
queryKey: ['questions'],
25+
queryFn: getNextQuestion,
26+
initialPageParam: 0,
27+
getNextPageParam: (lastPage, allPages, lastPageParam) => {
28+
if (lastPage.length === 0) {
29+
return undefined;
30+
}
31+
return lastPageParam + 1;
32+
},
33+
getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
34+
if (firstPageParam <= 1) {
35+
return undefined;
36+
}
37+
return firstPageParam - 1;
38+
}
39+
});
40+
41+
const currentQuestion = data?.pages[currentPage][0];
42+
43+
// Responsible for making sure we have the next 5 pages of data as the index changes
44+
useEffect(() => {
45+
if (isFetching) return;
46+
if (currentPage + 5 <= (data?.pageParams?.length || 0)) return;
47+
fetchNextPage();
48+
}, [currentPage, data?.pageParams?.length]);
49+
50+
const onViewableItemsChanged = useCallback(
51+
({
52+
viewableItems,
53+
changed
54+
}: {
55+
viewableItems: Array<ViewToken>;
56+
changed: Array<ViewToken>;
57+
}) => {
58+
if (viewableItems.length === 0) return;
59+
const { index } = viewableItems[0];
60+
if (index === null) return;
61+
setCurrentPage(index);
62+
},
63+
[]
64+
);
65+
66+
console.log('data', data?.pages.length);
67+
return (
68+
<View className="flex-1">
69+
<FlatList
70+
data={data?.pages || []}
71+
renderItem={({ item }) => {
72+
return (
73+
<Image
74+
style={{ width: SCREEN_WIDTH, height: SCREEN_HEIGHT - tabBarHeight }}
75+
source={item[0].image}
76+
placeholder={blurhash}
77+
contentFit="cover"
78+
transition={1000}
79+
/>
80+
);
81+
}}
82+
// Because the API is random and we are not deduping response from it. We will combine the id and index to ensure uniqueness
83+
keyExtractor={(item, index) => item[0].id.toString() + index}
84+
snapToAlignment="start"
85+
snapToInterval={SCREEN_HEIGHT - tabBarHeight}
86+
getItemLayout={(data, index) => ({
87+
length: SCREEN_HEIGHT - tabBarHeight,
88+
offset: (SCREEN_HEIGHT - tabBarHeight) * index,
89+
index
90+
})}
91+
disableIntervalMomentum
92+
onViewableItemsChanged={onViewableItemsChanged}
93+
viewabilityConfig={{
94+
itemVisiblePercentThreshold: 50
95+
}}
96+
showsVerticalScrollIndicator={false}
97+
/>
98+
99+
{/* <SafeAreaView style={{ marginTop: headerHeight }}></SafeAreaView> */}
100+
</View>
101+
);
102+
}

app/_layout.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,26 @@ import useCachedResources from '@/hooks/useCachedResources';
33
import { Slot, SplashScreen } from 'expo-router';
44
import { SafeAreaProvider } from 'react-native-safe-area-context';
55
import '../global.css';
6+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
67

78
// Prevent the splash screen from auto-hiding before asset loading is complete.
89
SplashScreen.preventAutoHideAsync();
910

11+
// Create the Tanstack Query Client
12+
const queryClient = new QueryClient();
13+
1014
export default function RootLayout() {
1115
const { appIsReady, onLayoutRootView } = useCachedResources();
1216

1317
if (!appIsReady) return null;
1418

1519
return (
16-
<SafeAreaProvider onLayout={onLayoutRootView}>
17-
<ThemeProvider>
18-
<Slot />
19-
</ThemeProvider>
20-
</SafeAreaProvider>
20+
<QueryClientProvider client={queryClient}>
21+
<SafeAreaProvider onLayout={onLayoutRootView}>
22+
<ThemeProvider>
23+
<Slot />
24+
</ThemeProvider>
25+
</SafeAreaProvider>
26+
</QueryClientProvider>
2127
);
2228
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Typography from '../Typography';
2+
import { View } from 'react-native';
3+
4+
export default function ForYouTitle() {
5+
return (
6+
<View className="flex items-center justify-center gap-[5px]">
7+
<Typography font="semiBold" size={'16'} className="text-center">
8+
For You
9+
</Typography>
10+
<View className="h-[4px] w-[30px] bg-white"></View>
11+
</View>
12+
);
13+
}

components/atoms/Typography/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export const variants = tv({
2626
thin: 'font-sf-pro-rounded-thin'
2727
},
2828
size: {
29-
'10': 'text-10'
29+
'10': 'text-10',
30+
'16': 'text-16'
3031
}
3132
}
3233
});

package-lock.json

+82
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"dependencies": {
2525
"@expo/vector-icons": "^13.0.0",
2626
"@react-navigation/native": "^6.0.2",
27+
"@tanstack/react-query": "^5.17.1",
28+
"axios": "^1.6.4",
2729
"expo": "~49.0.15",
2830
"expo-dev-client": "~2.4.12",
2931
"expo-font": "~11.4.0",
@@ -44,7 +46,8 @@
4446
"react-native-screens": "~3.22.0",
4547
"react-native-svg": "13.9.0",
4648
"react-native-web": "~0.19.6",
47-
"tailwind-variants": "^0.1.19"
49+
"tailwind-variants": "^0.1.19",
50+
"expo-image": "~1.3.5"
4851
},
4952
"devDependencies": {
5053
"@babel/core": "^7.20.0",

query/getNextQuestion.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import axios from 'axios';
2+
3+
type MultipleChoiceQuestion = {
4+
type: 'mcq';
5+
id: number;
6+
playlist: string;
7+
description: string;
8+
image?: string;
9+
question: string;
10+
options: Option[];
11+
user: {
12+
name: string;
13+
avatar?: string;
14+
};
15+
};
16+
17+
type Option = {
18+
id: 'A' | 'B' | 'C';
19+
answer: string;
20+
};
21+
22+
// Each GET request returns a new MC question
23+
export default async function getNextQuestion() {
24+
const response = await axios.get<MultipleChoiceQuestion>(
25+
'https://cross-platform.rp.devfactory.com/for_you'
26+
);
27+
return [response.data];
28+
}

tailwind.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ module.exports = {
1919
// letterSpacing: '-0.01'
2020
// }
2121
// ],
22-
10: ['10px']
22+
10: ['10px'],
23+
12: ['12px'],
24+
14: ['14px'],
25+
16: ['16px']
2326
},
2427
fontFamily: {
2528
// Example custom font. Requires custom font to be loaded via expo-font. See useCachedResources.ts and README.md.

utils/platform.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Platform } from 'react-native';
2+
3+
export default {
4+
isAndroid: Platform.OS === 'android',
5+
isIOS: Platform.OS === 'ios',
6+
isWeb: Platform.OS === 'web'
7+
};

utils/sizeMatters.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { Dimensions } from 'react-native';
2+
3+
export const SCREEN_WIDTH = Dimensions.get('window').width;
4+
export const SCREEN_HEIGHT = Dimensions.get('window').height;

0 commit comments

Comments
 (0)