aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Chen <ivan@thetaggid.com>2021-02-20 12:27:45 -0500
committerGitHub <noreply@github.com>2021-02-20 12:27:45 -0500
commiteae63e4a336785ae45cd01c4448a8444a7793613 (patch)
tree08aeb9ce90be7ca69b56923bab6c34871b3a793a /src
parent4b8130932b943afe9fdf63c611f1897622ab795e (diff)
parent82fc3c7ded1022a31cd532d469457d77f9214cc8 (diff)
Merge pull request #248 from IvanIFChen/tma258-sp-pagination-2
[TMA-258] SP Pagination
Diffstat (limited to 'src')
-rw-r--r--src/components/suggestedPeople/MutualFriends.tsx27
-rw-r--r--src/constants/api.ts4
-rw-r--r--src/constants/constants.ts2
-rw-r--r--src/screens/suggestedPeople/SuggestedPeopleScreen.tsx174
-rw-r--r--src/services/SuggestedPeopleService.ts34
-rw-r--r--src/store/initialStates.ts1
-rw-r--r--src/types/types.ts1
7 files changed, 174 insertions, 69 deletions
diff --git a/src/components/suggestedPeople/MutualFriends.tsx b/src/components/suggestedPeople/MutualFriends.tsx
index f99279c0..fdda104a 100644
--- a/src/components/suggestedPeople/MutualFriends.tsx
+++ b/src/components/suggestedPeople/MutualFriends.tsx
@@ -1,29 +1,24 @@
import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
+import {normalize} from 'react-native-elements';
import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler';
-import {useSelector} from 'react-redux';
-import {ScreenType} from '../../types';
import {BottomDrawer, TabsGradient} from '../../components';
-import {RootState} from '../../store/rootReducer';
+import {ProfilePreviewType, ScreenType} from '../../types';
import {isIPhoneX, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
import {ProfilePreview} from '../profile';
-import {normalize} from 'react-native-elements';
-const MutualFriends: React.FC = () => {
- // Requires user id of profile being viewed
- const userXId = '53a7df9c-c3b2-4b1c-b197-7b1149ecfc8d';
-
- // Fetch mutual friends of user X
- let {friends} = userXId
- ? useSelector((state: RootState) => state.userX[ScreenType.Search][userXId])
- : useSelector((state: RootState) => state.friends);
+interface MutualFriendsProps {
+ user: ProfilePreviewType;
+ friends: ProfilePreviewType[];
+}
+const MutualFriends: React.FC<MutualFriendsProps> = ({
+ user,
+ friends,
+}): JSX.Element => {
// Getting list of first 4 friends to display on suggested people screen
const friendsPreview = friends.slice(0, 4);
- // Extract username of user whose profile is being viewed
- const username = '@' + '12345678901234';
-
// Count to be displayed after + symbol
const count = friends.length - friendsPreview.length;
@@ -61,7 +56,7 @@ const MutualFriends: React.FC = () => {
<View style={styles.headerTextContainer}>
<Text style={styles.headerTitle}>Mutual Friends</Text>
<Text style={styles.headerDescription} numberOfLines={2}>
- {username} and you are both friends with
+ @{user.username} and you are both friends with
</Text>
</View>
</View>
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 6e2b28ec..57c26824 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -34,8 +34,8 @@ export const WAITLIST_USER_ENDPOINT: string = API_URL + 'waitlist-user/';
export const COMMENT_THREAD_ENDPOINT: string = API_URL + 'reply/';
// Suggested People
-export const SP_UPDATE_PICTURE: string = API_URL + 'suggested_people/update_picture/';
-export const SP_BASE_ENDPOINT: string = API_URL + 'suggested_people/';
+export const SP_USERS_ENDPOINT: string = API_URL + 'suggested_people/';
+export const SP_UPDATE_PICTURE_ENDPOINT: string = API_URL + 'suggested_people/update_picture/';
// Register as FCM device
export const FCM_ENDPOINT: string = API_URL + 'fcm/';
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index fbf03744..6e2c9e1c 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -184,3 +184,5 @@ export const EXPLORE_SECTION_TITLES: ExploreSectionType[] = [
export const SP_WIDTH = 375;
export const SP_HEIGHT = 812;
+
+export const SP_PAGE_SIZE = 5;
diff --git a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
index ba9c36a7..b9dee55a 100644
--- a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
+++ b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
@@ -1,22 +1,31 @@
import {useFocusEffect, useNavigation} from '@react-navigation/native';
-import React, {useCallback} from 'react';
+import React, {useCallback, useEffect, useState} from 'react';
import {
+ FlatList,
+ ListRenderItemInfo,
+ RefreshControl,
StatusBar,
StyleSheet,
Text,
- TouchableOpacity,
View,
} from 'react-native';
import {Image} from 'react-native-animatable';
import Animated from 'react-native-reanimated';
-import {SafeAreaView} from 'react-native-safe-area-context';
-import {useSelector} from 'react-redux';
+import {useDispatch, useSelector, useStore} from 'react-redux';
import {TabsGradient, TaggsBar} from '../../components';
import {MutualFriends} from '../../components/suggestedPeople';
+import {SP_PAGE_SIZE} from '../../constants';
import SuggestedPeopleOnboardingStackScreen from '../../routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen';
+import {getSuggestedPeople} from '../../services/SuggestedPeopleService';
+import {resetScreenType} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import {ScreenType} from '../../types';
-import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {
+ ProfilePreviewType,
+ ScreenType,
+ SuggestedPeopleDataType,
+} from '../../types';
+import {fetchUserX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {userXInStore} from './../../utils/';
/**
* Bare bones for suggested people consisting of:
@@ -24,17 +33,64 @@ import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
*/
const SuggestedPeopleScreen: React.FC = () => {
+ const y = Animated.useValue(0);
+ const navigation = useNavigation();
+ const state: RootState = useStore().getState();
+ const dispatch = useDispatch();
+ const screenType = ScreenType.SuggestedPeople;
const {suggested_people_linked} = useSelector(
(state: RootState) => state.user.profile,
) ?? {suggested_people_linked: -1};
- const y = Animated.useValue(0);
+ const [people, setPeople] = useState<SuggestedPeopleDataType[]>([]);
+ const [page, setPage] = useState(0);
+ const [refreshing, setRefreshing] = useState(false);
+ const [hideStatusBar, setHideStatusBar] = useState(false);
- // Can be removed once firstname, username props are received
- const firstName = 'Sarah';
+ // loads data and append it to users based on current page
+ useEffect(() => {
+ // console.log('current page', page);
+ loadMore(false);
+ }, [page]);
- // Adviced to maintain username as a variable here to append @ symbol for maintainability
- const username = '@' + 'sarahmiller';
- const navigation = useNavigation();
+ const loadMore = (resetData: boolean) => {
+ const loadNextPage = async () =>
+ await getSuggestedPeople(SP_PAGE_SIZE, page * SP_PAGE_SIZE);
+ loadNextPage().then((newUsers) => {
+ if (resetData) {
+ setPeople([]);
+ setPage(0);
+ }
+ loadUserDataToStore(newUsers.map((ppl) => ppl.user));
+ setPeople(people.concat(newUsers));
+ });
+ };
+
+ const loadUserDataToStore = async (users: ProfilePreviewType[]) => {
+ const loadUserData = async (user: ProfilePreviewType) => {
+ if (!userXInStore(state, screenType, user.id)) {
+ await fetchUserX(
+ dispatch,
+ {userId: user.id, username: user.username},
+ screenType,
+ );
+ }
+ };
+ await Promise.all(users.map((user) => loadUserData(user)));
+ };
+ const resetPage = () => {
+ const reset = async () => {
+ await dispatch(resetScreenType(screenType));
+ loadMore(true);
+ };
+ setRefreshing(true);
+ reset().then(() => {
+ setRefreshing(false);
+ });
+ };
+
+ const onRefresh = useCallback(() => {
+ resetPage();
+ }, []);
useFocusEffect(
useCallback(() => {
@@ -48,60 +104,88 @@ const SuggestedPeopleScreen: React.FC = () => {
}, [navigation, suggested_people_linked]),
);
- const mainContent = () => (
- <SafeAreaView>
- <StatusBar barStyle={'light-content'} />
- <Image
- // !!! Displaying Sarah Miller's image
- source={require('../../assets/images/sarah_miller_full.jpeg')}
- style={styles.image}
- />
- <View style={styles.mainContainer}>
- <Text style={styles.title}>Suggested People</Text>
- <View style={styles.body}>
- <View style={styles.marginManager}>
+ // const onViewableItemsChanged = useCallback(
+ // ({viewableItems}: {viewableItems: ViewToken[]}) => {
+ // setHideStatusBar(viewableItems[0].index !== 0);
+ // },
+ // [],
+ // );
+
+ const SPBody = ({
+ item,
+ }: {
+ item: ListRenderItemInfo<SuggestedPeopleDataType>;
+ }) => {
+ const data = item.item;
+ const firstItem = item.index === 0;
+ return (
+ <>
+ <StatusBar barStyle={'light-content'} hidden={hideStatusBar} />
+ <Image
+ source={{
+ uri: data.suggested_people_url,
+ }}
+ style={styles.image}
+ />
+ <View style={styles.mainContainer}>
+ <Text style={styles.title}>{firstItem && 'Suggested People'}</Text>
+ <View style={styles.body}>
<View style={styles.addUserContainer}>
<View style={styles.nameInfoContainer}>
- <Text style={styles.firstName}>{firstName}</Text>
- <Text style={styles.username}>{username}</Text>
+ <Text style={styles.firstName}>{data.user.first_name}</Text>
+ <Text style={styles.username}>{'@' + data.user.username}</Text>
</View>
- <TouchableOpacity
+ {/* TODO: Finish me ?! */}
+ {/* <TouchableOpacity
activeOpacity={0.5}
+ // TODO: Call function to Add Friend
onPress={() => console.log('Call add friend function')}>
<View style={styles.addButton}>
<Text style={styles.addButtonTitle}>{'Add Friend'}</Text>
</View>
- </TouchableOpacity>
+ </TouchableOpacity> */}
</View>
- </View>
- <TaggsBar
- y={y}
- userXId={undefined}
- profileBodyHeight={0}
- screenType={ScreenType.SuggestedPeople}
- whiteRing={true}
- />
- <View style={styles.marginManager}>
- <MutualFriends />
+ <TaggsBar
+ y={y}
+ userXId={data.user.id}
+ profileBodyHeight={0}
+ screenType={ScreenType.SuggestedPeople}
+ />
+ <MutualFriends user={data.user} friends={data.mutual_friends} />
</View>
</View>
- </View>
- <TabsGradient />
- </SafeAreaView>
- );
+ </>
+ );
+ };
return suggested_people_linked === 0 ? (
<SuggestedPeopleOnboardingStackScreen />
) : (
- mainContent()
+ <>
+ <FlatList
+ data={people}
+ renderItem={(item) => <SPBody item={item} />}
+ keyExtractor={(item, index) => index.toString()}
+ showsVerticalScrollIndicator={false}
+ onEndReached={() => setPage(page + 1)}
+ // onViewableItemsChanged={onViewableItemsChanged}
+ refreshControl={
+ <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
+ }
+ pagingEnabled
+ />
+ <TabsGradient />
+ </>
);
};
const styles = StyleSheet.create({
mainContainer: {
flexDirection: 'column',
- width: SCREEN_WIDTH,
- height: isIPhoneX() ? SCREEN_HEIGHT * 0.85 : SCREEN_HEIGHT * 0.88,
+ width: SCREEN_WIDTH * 0.9,
+ height: SCREEN_HEIGHT,
+ paddingVertical: '15%',
+ paddingBottom: '20%',
justifyContent: 'space-between',
alignSelf: 'center',
},
diff --git a/src/services/SuggestedPeopleService.ts b/src/services/SuggestedPeopleService.ts
index 525c68b1..34a31662 100644
--- a/src/services/SuggestedPeopleService.ts
+++ b/src/services/SuggestedPeopleService.ts
@@ -1,10 +1,10 @@
import AsyncStorage from '@react-native-community/async-storage';
import {
EDIT_PROFILE_ENDPOINT,
- SP_BASE_ENDPOINT,
- SP_UPDATE_PICTURE,
+ SP_UPDATE_PICTURE_ENDPOINT,
+ SP_USERS_ENDPOINT
} from '../constants';
-import {SuggestedPeopleDataType} from '../types';
+import { SuggestedPeopleDataType } from '../types';
export const sendSuggestedPeopleLinked = async (
userId: string,
@@ -39,7 +39,7 @@ export const sendSuggestedPeoplePhoto = async (photoUri: string) => {
name: 'sp_photo.jpg',
type: 'image/jpg',
});
- const response = await fetch(SP_UPDATE_PICTURE, {
+ const response = await fetch(SP_UPDATE_PICTURE_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
@@ -54,10 +54,33 @@ export const sendSuggestedPeoplePhoto = async (photoUri: string) => {
}
};
+export const getSuggestedPeople = async (limit: number, offset: number) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const url = `${SP_USERS_ENDPOINT}?limit=${limit}&offset=${offset}`;
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ });
+ if (response.status !== 200) {
+ throw 'Non-200 response';
+ }
+ const data = await response.json();
+ const typedData: SuggestedPeopleDataType[] = data.results;
+ return typedData;
+ } catch (error) {
+ console.log('Error fetching SP user data');
+ console.log(error);
+ return [];
+ }
+};
+
export const getSuggestedPeopleProfile = async (userId: string) => {
try {
const token = await AsyncStorage.getItem('token');
- const response = await fetch(SP_BASE_ENDPOINT + userId + '/', {
+ const response = await fetch(SP_USERS_ENDPOINT + userId + '/', {
method: 'GET',
headers: {
Authorization: 'Token ' + token,
@@ -73,4 +96,3 @@ export const getSuggestedPeopleProfile = async (userId: string) => {
console.log('Error retrieving SP info');
return undefined;
}
-};
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 10fdad25..408de39e 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -128,6 +128,7 @@ export const EMPTY_SCREEN_TO_USERS_LIST: Record<
[ScreenType.Profile]: EMPTY_USERX_LIST,
[ScreenType.Search]: EMPTY_USERX_LIST,
[ScreenType.Notifications]: EMPTY_USERX_LIST,
+ [ScreenType.SuggestedPeople]: EMPTY_USERX_LIST,
};
export const INITIAL_CATEGORIES_STATE = {
diff --git a/src/types/types.ts b/src/types/types.ts
index 8e9e8a60..97471171 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -207,6 +207,7 @@ export type NotificationType = {
};
export type TypeOfComment = 'Comment' | 'Thread';
+
export type TypeOfNotification =
// notification_object is undefined
| 'DFT'