diff options
author | Ashm Walia <40498934+ashmgarv@users.noreply.github.com> | 2020-10-27 17:36:03 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-27 20:36:03 -0400 |
commit | 62d6fe2bca4bdd1a48cfe54e4c0c3fe590c3b750 (patch) | |
tree | 3afb2c723a10a2649c298e226bc31e10cec92d5a /src | |
parent | 795ba089207571ec13226f2d07c149c8697763ce (diff) |
[HOT FIX] Refactor to make things clean and make the app work (#82)
* Refactored
* Final refactor with an issue
* It works
* Whoops
Diffstat (limited to 'src')
-rw-r--r-- | src/components/profile/Content.tsx | 110 | ||||
-rw-r--r-- | src/components/profile/ProfilePreview.tsx | 5 | ||||
-rw-r--r-- | src/routes/authentication/AuthProvider.tsx | 67 | ||||
-rw-r--r-- | src/routes/viewProfile/ProfileProvider.tsx | 76 | ||||
-rw-r--r-- | src/screens/onboarding/ProfileOnboarding.tsx | 1 | ||||
-rw-r--r-- | src/screens/onboarding/RegistrationOne.tsx | 6 | ||||
-rw-r--r-- | src/screens/profile/FollowersListScreen.tsx | 41 | ||||
-rw-r--r-- | src/services/UserFollowServices.ts | 18 | ||||
-rw-r--r-- | src/services/UserProfileService.ts | 30 |
9 files changed, 225 insertions, 129 deletions
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 8d69b3b0..689fcaf7 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,21 +1,17 @@ import AsyncStorage from '@react-native-community/async-storage'; import React, {useCallback, useEffect, useState} from 'react'; -import {Alert, LayoutChangeEvent, StyleSheet, View} from 'react-native'; +import {LayoutChangeEvent, StyleSheet, View} from 'react-native'; import Animated from 'react-native-reanimated'; import {AuthContext, ProfileContext} from '../../routes/'; -import {MomentType, ProfilePreviewType} from 'src/types'; -import {defaultMoments, MOMENTS_ENDPOINT} from '../../constants'; +import {MomentType} from 'src/types'; +import {defaultMoments} from '../../constants'; import {SCREEN_HEIGHT} from '../../utils'; import TaggsBar from '../taggs/TaggsBar'; import {Moment} from '../moments'; import ProfileBody from './ProfileBody'; import ProfileCutout from './ProfileCutout'; import ProfileHeader from './ProfileHeader'; -import { - loadFollowers, - loadFollowing, - followOrUnfollowUser, -} from '../../services'; +import {followOrUnfollowUser} from '../../services'; interface ContentProps { y: Animated.Value<number>; @@ -24,23 +20,19 @@ interface ContentProps { const Content: React.FC<ContentProps> = ({y, isProfileView}) => { const [profileBodyHeight, setProfileBodyHeight] = useState(0); - const {newMomentsAvailable, updateMoments, user} = isProfileView + const {user, moments, followers, following, updateFollowers} = isProfileView ? React.useContext(ProfileContext) : React.useContext(AuthContext); const {logout} = React.useContext(AuthContext); - const [imagesList, setImagesList] = useState<MomentType[]>([]); + const {user: loggedInUser} = React.useContext(AuthContext); + + /** + * States + */ const [imagesMap, setImagesMap] = useState<Map<string, MomentType[]>>( new Map(), ); - const [followed, setFollowed] = React.useState<boolean>(false); - const [followers, setFollowers] = React.useState<Array<ProfilePreviewType>>( - [], - ); - const [following, setFollowing] = React.useState<Array<ProfilePreviewType>>( - [], - ); - const {user: loggedInUser} = React.useContext(AuthContext); /** * If own profile is being viewed then do not show the follow button. @@ -52,11 +44,11 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { setProfileBodyHeight(height); }; - const {userId, username} = user; + const {userId} = user; const createImagesMap = useCallback(() => { var map = new Map(); - imagesList.forEach(function (imageObject) { + moments.forEach(function (imageObject) { var moment_category = imageObject.moment_category; if (map.has(moment_category)) { map.get(moment_category).push(imageObject); @@ -66,78 +58,29 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { }); setImagesMap(map); - }, [imagesList]); + }, [moments]); useEffect(() => { if (!userId) { return; } - - const retrieveMoments = async () => { - try { - const token = await AsyncStorage.getItem('token'); - const response = await fetch(MOMENTS_ENDPOINT + '?user_id=' + userId, { - method: 'GET', - headers: { - Authorization: 'Token ' + token, - }, - }); - const status = response.status; - if (status === 200) { - const data = await response.json(); - setImagesList(data); - updateMoments(!newMomentsAvailable); - } else { - console.log('Could not load moments!'); - } - } catch (err) { - console.log(err); - } - }; - - if (newMomentsAvailable) { - retrieveMoments(); - createImagesMap(); - } - }, [userId, createImagesMap, updateMoments, newMomentsAvailable]); + createImagesMap(); + }, [createImagesMap]); /** - * This hook is called just on the load of profile. + * This hook is called load of profile and when you push update the followers list. */ useEffect(() => { - const updateFollowedValue = async () => { - const token = await AsyncStorage.getItem('token'); - if (!token) { - logout(); - return; - } - - const listFollowers: ProfilePreviewType[] = await loadFollowers( - userId, - token, - ); - - const listFollowing: ProfilePreviewType[] = await loadFollowing( - userId, - token, - ); - - /** - * Check if the logged in user actually follows the user being viewed. - */ - const isActuallyFollowed = listFollowers.some( - (follower) => follower.username === loggedInUser.username, - ); - - if (followed != isActuallyFollowed) { - setFollowed(isActuallyFollowed); - } - setFollowers(listFollowers); - setFollowing(listFollowing); - }; - - updateFollowedValue(); - }, []); + if (!userId) { + return; + } + const isActuallyFollowed = followers.some( + (follower) => follower.username === loggedInUser.username, + ); + if (followed != isActuallyFollowed) { + setFollowed(isActuallyFollowed); + } + }, [followers]); /** * Handles a click on the follow / unfollow button. @@ -156,6 +99,7 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { ); if (isUpdatedSuccessful) { setFollowed(!followed); + updateFollowers(true); } }; diff --git a/src/components/profile/ProfilePreview.tsx b/src/components/profile/ProfilePreview.tsx index ec0be50c..6848f993 100644 --- a/src/components/profile/ProfilePreview.tsx +++ b/src/components/profile/ProfilePreview.tsx @@ -39,7 +39,9 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({ style, }) => { const navigation = useNavigation(); - const {loadProfile, updateMoments} = useContext(ProfileContext); + const {loadProfile, updateMoments, updateFollowers} = useContext( + ProfileContext, + ); const [avatarURI, setAvatarURI] = useState<string | null>(null); const [user, setUser] = useState<UserType>(NO_USER); useEffect(() => { @@ -131,6 +133,7 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({ //Set new moments to true makes sure that we download the moment for the user being viewed again. loadProfile(user.id, user.username); updateMoments(true); + updateFollowers(true); if (!isComment) { navigation.push('Profile', { isProfileView: true, diff --git a/src/routes/authentication/AuthProvider.tsx b/src/routes/authentication/AuthProvider.tsx index 5bd4278d..68182ee3 100644 --- a/src/routes/authentication/AuthProvider.tsx +++ b/src/routes/authentication/AuthProvider.tsx @@ -7,12 +7,16 @@ import { loadProfileInfo, loadRecentlySearchedUsers, loadSocialPosts, + loadMoments, + loadFollowers, + loadFollowing, } from '../../services'; import { ProfilePreviewType, ProfileType, SocialAccountType, UserType, + MomentType, } from '../../types'; interface AuthContextProps { @@ -27,6 +31,11 @@ interface AuthContextProps { newMomentsAvailable: boolean; updateMoments: (value: boolean) => void; socialsNeedUpdate: (_: string[]) => void; + moments: MomentType[]; + followers: ProfilePreviewType[]; + following: ProfilePreviewType[]; + followersNeedUpdate: boolean; + updateFollowers: (value: boolean) => void; } const NO_USER: UserType = { @@ -58,6 +67,11 @@ export const AuthContext = createContext<AuthContextProps>({ updateMoments: () => {}, socialAccounts: NO_SOCIAL_ACCOUNTS, socialsNeedUpdate: () => {}, + moments: [], + followers: [], + following: [], + followersNeedUpdate: true, + updateFollowers: () => {}, }); /** @@ -79,6 +93,10 @@ const AuthProvider: React.FC = ({children}) => { const [socialsNeedUpdate, setSocialsNeedUpdate] = useState<string[]>([ ...INTEGRATED_SOCIAL_LIST, ]); + const [moments, setMoments] = useState<Array<MomentType>>([]); + const [followers, setFollowers] = useState<Array<ProfilePreviewType>>([]); + const [following, setFollowing] = useState<Array<ProfilePreviewType>>([]); + const [followersNeedUpdate, setFollowersNeedUpdate] = useState<boolean>(true); const {userId} = user; useEffect(() => { if (!userId) { @@ -104,6 +122,48 @@ const AuthProvider: React.FC = ({children}) => { }, [userId]); useEffect(() => { + const loadNewMoments = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + setUser(NO_USER); + return; + } + const newMoments = await loadMoments(userId, token); + if (newMoments) { + setMoments(newMoments); + } + setNewMomentsAvailable(false); + } catch (error) { + console.log(error); + } + }; + if (newMomentsAvailable && userId) { + loadNewMoments(); + } + }, [newMomentsAvailable, userId, moments]); + + useEffect(() => { + const loadNewFollowers = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + setUser(NO_USER); + return; + } + loadFollowers(userId, token, setFollowers); + loadFollowing(userId, token, setFollowing); + setFollowersNeedUpdate(false); + } catch (error) { + console.log(error); + } + }; + if (followersNeedUpdate && userId) { + loadNewFollowers(); + } + }, [followersNeedUpdate, userId, followers, following]); + + useEffect(() => { if (socialsNeedUpdate.length > 0 && userId) { for (let social of socialsNeedUpdate) { loadSocialPosts(userId, social).then((accountData) => { @@ -125,6 +185,10 @@ const AuthProvider: React.FC = ({children}) => { cover, newMomentsAvailable, socialAccounts, + moments, + followers, + following, + followersNeedUpdate, login: (id, username) => { setUser({...user, userId: id, username}); }, @@ -146,6 +210,9 @@ const AuthProvider: React.FC = ({children}) => { socialsNeedUpdate: (socials: string[]) => { setSocialsNeedUpdate(socials); }, + updateFollowers: (value) => { + setFollowersNeedUpdate(value); + }, }}> {children} </AuthContext.Provider> diff --git a/src/routes/viewProfile/ProfileProvider.tsx b/src/routes/viewProfile/ProfileProvider.tsx index 0600b65b..f9b29dc6 100644 --- a/src/routes/viewProfile/ProfileProvider.tsx +++ b/src/routes/viewProfile/ProfileProvider.tsx @@ -1,13 +1,23 @@ import AsyncStorage from '@react-native-community/async-storage'; import React, {createContext, useEffect, useState} from 'react'; +import {Value} from 'react-native-reanimated'; import {INTEGRATED_SOCIAL_LIST} from '../../constants'; import { loadAvatar, loadCover, loadProfileInfo, loadSocialPosts, + loadMoments, + loadFollowers, + loadFollowing, } from '../../services'; -import {ProfileType, SocialAccountType, UserType} from '../../types'; +import { + ProfileType, + SocialAccountType, + ProfilePreviewType, + UserType, + MomentType, +} from '../../types'; interface ProfileContextProps { user: UserType; @@ -19,6 +29,11 @@ interface ProfileContextProps { updateMoments: (value: boolean) => void; socialAccounts: Record<string, SocialAccountType>; socialsNeedUpdate: (_: string[]) => void; + moments: MomentType[]; + followers: ProfilePreviewType[]; + following: ProfilePreviewType[]; + followersNeedUpdate: boolean; + updateFollowers: (value: boolean) => void; } const NO_USER: UserType = { userId: '', @@ -46,6 +61,11 @@ export const ProfileContext = createContext<ProfileContextProps>({ updateMoments: () => {}, socialAccounts: NO_SOCIAL_ACCOUNTS, socialsNeedUpdate: () => {}, + moments: [], + followers: [], + following: [], + followersNeedUpdate: true, + updateFollowers: () => {}, }); /** @@ -60,6 +80,10 @@ const ProfileProvider: React.FC = ({children}) => { const [socialAccounts, setSocialAccounts] = useState< Record<string, SocialAccountType> >(NO_SOCIAL_ACCOUNTS); + const [moments, setMoments] = useState<Array<MomentType>>([]); + const [followers, setFollowers] = useState<Array<ProfilePreviewType>>([]); + const [following, setFollowing] = useState<Array<ProfilePreviewType>>([]); + const [followersNeedUpdate, setFollowersNeedUpdate] = useState<boolean>(true); // Default update all integrated social lists on start const [socialsNeedUpdate, setSocialsNeedUpdate] = useState<string[]>([ ...INTEGRATED_SOCIAL_LIST, @@ -70,7 +94,6 @@ const ProfileProvider: React.FC = ({children}) => { if (!userId) { return; } - const loadData = async () => { try { const token = await AsyncStorage.getItem('token'); @@ -89,6 +112,48 @@ const ProfileProvider: React.FC = ({children}) => { }, [userId]); useEffect(() => { + const loadNewMoments = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + setUser(NO_USER); + return; + } + const newMoments = await loadMoments(userId, token); + if (newMoments) { + setMoments(newMoments); + } + setNewMomentsAvailable(false); + } catch (error) { + console.log(error); + } + }; + if (newMomentsAvailable && userId) { + loadNewMoments(); + } + }, [newMomentsAvailable, userId]); + + useEffect(() => { + const loadNewFollowers = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + setUser(NO_USER); + return; + } + loadFollowers(userId, token, setFollowers); + loadFollowing(userId, token, setFollowing); + setFollowersNeedUpdate(false); + } catch (error) { + console.log(error); + } + }; + if (followersNeedUpdate && userId) { + loadNewFollowers(); + } + }, [followersNeedUpdate, userId, followers, following]); + + useEffect(() => { if (socialsNeedUpdate.length > 0 && userId) { for (let social of socialsNeedUpdate) { loadSocialPosts(userId, social).then((accountData) => { @@ -110,6 +175,10 @@ const ProfileProvider: React.FC = ({children}) => { cover, newMomentsAvailable, socialAccounts, + moments, + followers, + following, + followersNeedUpdate, loadProfile: (id, username) => { setUser({...user, userId: id, username}); }, @@ -119,6 +188,9 @@ const ProfileProvider: React.FC = ({children}) => { socialsNeedUpdate: (socials: string[]) => { setSocialsNeedUpdate(socials); }, + updateFollowers: (value) => { + setFollowersNeedUpdate(value); + }, }}> {children} </ProfileContext.Provider> diff --git a/src/screens/onboarding/ProfileOnboarding.tsx b/src/screens/onboarding/ProfileOnboarding.tsx index bbabbb56..0d379a1a 100644 --- a/src/screens/onboarding/ProfileOnboarding.tsx +++ b/src/screens/onboarding/ProfileOnboarding.tsx @@ -20,7 +20,6 @@ import { BirthDatePicker, } from '../../components'; import {OnboardingStackParams} from '../../routes/onboarding'; -import {AuthContext} from '../../routes/authentication'; import ImagePicker from 'react-native-image-crop-picker'; import { EDIT_PROFILE_ENDPOINT, diff --git a/src/screens/onboarding/RegistrationOne.tsx b/src/screens/onboarding/RegistrationOne.tsx index e0db0755..a901d477 100644 --- a/src/screens/onboarding/RegistrationOne.tsx +++ b/src/screens/onboarding/RegistrationOne.tsx @@ -145,7 +145,7 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { <Text style={styles.formHeader}>ENTER PHONE NUMBER</Text> </View> <TaggInput - maxLength = {12} // currently only support US phone numbers + maxLength={12} // currently only support US phone numbers accessibilityHint="Enter your phone number." accessibilityLabel="Phone number input field." placeholder="Phone Number" @@ -154,7 +154,7 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { autoCapitalize="none" returnKeyType="next" keyboardType="phone-pad" - onChangeText={handlePhoneUpdate} + onChangeText={handlePhoneUpdate} blurOnSubmit={false} ref={phoneRef} valid={form.isValidPhone} @@ -210,4 +210,4 @@ const styles = StyleSheet.create({ }, }); -export default RegistrationOne;
\ No newline at end of file +export default RegistrationOne; diff --git a/src/screens/profile/FollowersListScreen.tsx b/src/screens/profile/FollowersListScreen.tsx index 21778929..4d14ef67 100644 --- a/src/screens/profile/FollowersListScreen.tsx +++ b/src/screens/profile/FollowersListScreen.tsx @@ -1,16 +1,13 @@ -import React, {useRef, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {RouteProp} from '@react-navigation/native'; import {TabsGradient, Followers, CenteredView} from '../../components'; import Animated from 'react-native-reanimated'; import {AuthContext, ProfileContext} from '../../routes/'; -import {FOLLOWERS_ENDPOINT, FOLLOWING_ENDPOINT} from '../../constants'; -import AsyncStorage from '@react-native-community/async-storage'; import {ProfilePreviewType} from '../../types'; import {ScrollView} from 'react-native-gesture-handler'; -import {StatusBarHeight, SCREEN_HEIGHT} from '../../utils'; +import {SCREEN_HEIGHT} from '../../utils'; import {StyleSheet, View} from 'react-native'; import {ProfileStackParams} from '../../routes'; -import { loadFollowers, loadFollowing } from '../../services/UserFollowServices'; type FollowersListScreenRouteProp = RouteProp< ProfileStackParams, @@ -22,7 +19,7 @@ interface FollowersListScreenProps { const FollowersListScreen: React.FC<FollowersListScreenProps> = ({route}) => { const {isProfileView, isFollowers} = route.params; - const {user} = isProfileView + const {user, followers, following} = isProfileView ? React.useContext(ProfileContext) : React.useContext(AuthContext); const y = Animated.useValue(0); @@ -30,31 +27,8 @@ const FollowersListScreen: React.FC<FollowersListScreenProps> = ({route}) => { const top = Animated.useValue(-SCREEN_HEIGHT); useEffect(() => { - const loadResults = async (q: string) => { - try { - const token = await AsyncStorage.getItem('token'); - - if (!token) { - return; - } - - const result: ProfilePreviewType[] = isFollowers ? await loadFollowers( - user.userId, - token, - ) : await loadFollowing( - user.userId, - token, - ); - setResult(result); - - } catch (error) { - console.log(error); - setResult([]); - } - }; - loadResults(user.userId); - - }, []); + setResult(isFollowers ? followers : following); + }, [followers, following]); return ( <CenteredView> @@ -64,7 +38,10 @@ const FollowersListScreen: React.FC<FollowersListScreenProps> = ({route}) => { stickyHeaderIndices={[4]} contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={false}> - <Followers {...{result}} sectionTitle={isFollowers ? "Followers" : "Following"} /> + <Followers + {...{result}} + sectionTitle={isFollowers ? 'Followers' : 'Following'} + /> </ScrollView> <TabsGradient /> </View> diff --git a/src/services/UserFollowServices.ts b/src/services/UserFollowServices.ts index 508c1387..105124bc 100644 --- a/src/services/UserFollowServices.ts +++ b/src/services/UserFollowServices.ts @@ -10,7 +10,11 @@ import { import {ProfilePreviewType} from 'src/types'; -export const loadFollowers = async (userId: string, token: string) => { +export const loadFollowers = async ( + userId: string, + token: string, + callback: Function, +) => { try { const response = await fetch(FOLLOWERS_ENDPOINT + `?user_id=${userId}`, { method: 'GET', @@ -20,17 +24,20 @@ export const loadFollowers = async (userId: string, token: string) => { }); if (response.status === 200) { const body = await response.json(); - return body; + callback(body); } else { throw new Error(await response.json()); } } catch (error) { console.log(error); } - return []; }; -export const loadFollowing = async (userId: string, token: string) => { +export const loadFollowing = async ( + userId: string, + token: string, + callback: Function, +) => { try { const response = await fetch(FOLLOWING_ENDPOINT + `?user_id=${userId}`, { method: 'GET', @@ -40,14 +47,13 @@ export const loadFollowing = async (userId: string, token: string) => { }); if (response.status === 200) { const body = await response.json(); - return body; + callback(body); } else { throw new Error(await response.json()); } } catch (error) { console.log(error); } - return []; }; export const followOrUnfollowUser = async ( diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts index 31383f67..a2d53dbb 100644 --- a/src/services/UserProfileService.ts +++ b/src/services/UserProfileService.ts @@ -3,7 +3,7 @@ import AsyncStorage from '@react-native-community/async-storage'; import {Alert} from 'react-native'; import RNFetchBlob from 'rn-fetch-blob'; -import {SocialAccountType} from 'src/types'; +import {SocialAccountType, MomentType} from 'src/types'; import { AVATAR_PHOTO_ENDPOINT, COVER_PHOTO_ENDPOINT, @@ -11,6 +11,7 @@ import { GET_IG_POSTS_ENDPOINT, GET_TWITTER_POSTS_ENDPOINT, PROFILE_INFO_ENDPOINT, + MOMENTS_ENDPOINT, } from '../constants'; export const loadProfileInfo = async ( @@ -119,6 +120,33 @@ export const loadSocialPosts: ( return accountData; }; +export const loadMoments: ( + userId: string, + token: string, +) => Promise<MomentType[]> = async (userId, token) => { + let moments: MomentType[] = []; + try { + const response = await fetch(MOMENTS_ENDPOINT + '?user_id=' + userId, { + method: 'GET', + headers: { + Authorization: 'Token ' + token, + }, + }); + const status = response.status; + if (status === 200) { + const data = await response.json(); + moments = data; + } else { + console.log('Could not load moments!'); + return []; + } + } catch (err) { + console.log(err); + return []; + } + return moments; +}; + export const loadRecentlySearchedUsers = async (callback: Function) => { try { const asyncCache = await AsyncStorage.getItem('@recently_searched_users'); |