diff options
author | Ashm Walia <40498934+ashmgarv@users.noreply.github.com> | 2020-11-09 12:16:44 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-09 15:16:44 -0500 |
commit | f347ef180ece9235380f2225243beddaececa949 (patch) | |
tree | 02ae7739ae16f68fb47a574420c1c497fb5f06ac /src | |
parent | d7ed9541f47c22d93c43a32baf3bf33d68d823c8 (diff) |
[FOR MASS REVIEW] Multiple contexts(Searched user gets replaced) (#97)
* First commit towards clean code
* Tested things
* Some final touch
* View updates posts
* Cleaned up followers / following
* You won't believe but it works
* Pass avatar uri via props
* Small change
* Small change
* Removed unnecessary jargon
Co-authored-by: Ivan Chen <ivan@thetaggid.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/App.tsx | 10 | ||||
-rw-r--r-- | src/components/common/AvatarTitle.tsx | 10 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 4 | ||||
-rw-r--r-- | src/components/profile/FollowCount.tsx | 7 | ||||
-rw-r--r-- | src/components/profile/ProfileHeader.tsx | 1 | ||||
-rw-r--r-- | src/components/profile/ProfilePreview.tsx | 31 | ||||
-rw-r--r-- | src/components/taggs/Tagg.tsx | 12 | ||||
-rw-r--r-- | src/components/taggs/TaggsBar.tsx | 3 | ||||
-rw-r--r-- | src/routes/profile/Profile.tsx | 7 | ||||
-rw-r--r-- | src/routes/profile/ProfileStack.tsx | 12 | ||||
-rw-r--r-- | src/routes/viewProfile/ProfileProvider.tsx | 41 | ||||
-rw-r--r-- | src/screens/profile/FollowersListScreen.tsx | 19 | ||||
-rw-r--r-- | src/screens/profile/ProfileScreen.tsx | 43 | ||||
-rw-r--r-- | src/screens/profile/SocialMediaTaggs.tsx | 22 |
14 files changed, 115 insertions, 107 deletions
diff --git a/src/App.tsx b/src/App.tsx index 6d51da34..2e6865fd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,13 @@ import React from 'react'; import {NavigationContainer} from '@react-navigation/native'; -import Routes, {AuthProvider, ProfileProvider} from './routes'; +import Routes, {AuthProvider} from './routes'; const App = () => { return ( <AuthProvider> - <ProfileProvider> - <NavigationContainer> - <Routes /> - </NavigationContainer> - </ProfileProvider> + <NavigationContainer> + <Routes /> + </NavigationContainer> </AuthProvider> ); }; diff --git a/src/components/common/AvatarTitle.tsx b/src/components/common/AvatarTitle.tsx index f3105f70..65ae7486 100644 --- a/src/components/common/AvatarTitle.tsx +++ b/src/components/common/AvatarTitle.tsx @@ -3,15 +3,13 @@ import {Image, StyleSheet, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import {TAGGS_GRADIENT} from '../../constants'; import {AuthContext, ProfileContext} from '../../routes/'; +import {loadAvatar} from '../../services'; +import AsyncStorage from '@react-native-community/async-storage'; type AvatarTitleProps = { - isProfileView: boolean; + avatar: string; }; - -const AvatarTitle: React.FC<AvatarTitleProps> = ({isProfileView}) => { - const {avatar} = isProfileView - ? React.useContext(ProfileContext) - : React.useContext(AuthContext); +const AvatarTitle: React.FC<AvatarTitleProps> = ({avatar}) => { return ( <View style={[styles.container]}> <LinearGradient diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index f86d8331..7afc3fbc 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -23,6 +23,7 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { const {user, moments, followers, following, updateFollowers} = isProfileView ? React.useContext(ProfileContext) : React.useContext(AuthContext); + const {logout} = React.useContext(AuthContext); const { user: loggedInUser, @@ -71,7 +72,7 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { }, [createImagesMap]); /** - * This hook is called load of profile and when you push update the followers list. + * This hook is called on load of profile and when you update the followers list. */ useEffect(() => { if (!userId) { @@ -87,6 +88,7 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { /** * Handles a click on the follow / unfollow button. + * updateFollowers and updateLoggedInUerFollowers to make sure that we update followers list / count for both the users in context. */ const handleFollowUnfollow = async () => { const token = await AsyncStorage.getItem('token'); diff --git a/src/components/profile/FollowCount.tsx b/src/components/profile/FollowCount.tsx index 80d56de4..3e270428 100644 --- a/src/components/profile/FollowCount.tsx +++ b/src/components/profile/FollowCount.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {View, Text, StyleSheet, ViewProps} from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; import {useNavigation} from '@react-navigation/native'; +import {AuthContext, ProfileContext} from '../../routes'; interface FollowCountProps extends ViewProps { mode: 'followers' | 'following'; @@ -15,6 +16,10 @@ const FollowCount: React.FC<FollowCountProps> = ({ count, isProfileView, }) => { + const {followers, following} = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); + const navigation = useNavigation(); const displayed: string = count < 5e3 @@ -28,8 +33,8 @@ const FollowCount: React.FC<FollowCountProps> = ({ <TouchableOpacity onPress={() => navigation.push('FollowersListScreen', { - isProfileView: isProfileView, isFollowers: mode === 'followers', + list: mode === 'followers' ? followers : following, }) }> <View style={[styles.container, style]}> diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 0b56a787..6f11e806 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -5,7 +5,6 @@ import FollowCount from './FollowCount'; import {View, Text, StyleSheet} from 'react-native'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {AuthContext, ProfileContext} from '../../routes/'; -import {ProfilePreviewType} from 'src/types'; type ProfileHeaderProps = { isProfileView: boolean; diff --git a/src/components/profile/ProfilePreview.tsx b/src/components/profile/ProfilePreview.tsx index 7bbc3fb6..9c953e7d 100644 --- a/src/components/profile/ProfilePreview.tsx +++ b/src/components/profile/ProfilePreview.tsx @@ -13,7 +13,6 @@ import RNFetchBlob from 'rn-fetch-blob'; import AsyncStorage from '@react-native-community/async-storage'; import {AVATAR_PHOTO_ENDPOINT} from '../../constants'; import {UserType} from '../../types'; -import {ProfileContext} from '../../routes/viewProfile'; const NO_USER: UserType = { userId: '', username: '', @@ -25,8 +24,8 @@ const NO_USER: UserType = { * If isComment is true then it means that we are not displaying this tile as a part of search results. * And hence we do not cache the search results. * On the other hand, if isComment is false, then we should update the search cache. (This cache needs to be revamped to clear outdated results.) - * In either case, we load the ProfileContext with data and set the getNewMoments flag to true (Which ensures that everything that needs to be displayed on a user's profile is set). - * Finally, We navigate to Profile if we are on the Search Stack. Else we navigate to ProfileView. + * In either case, we update the userBeingVisited in our AuthContext (Which can be used to make api calls later on to fetch user specific data). + * Finally, We navigate to Profile. */ interface ProfilePreviewProps extends ViewProps { @@ -39,9 +38,6 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({ style, }) => { const navigation = useNavigation(); - const {loadProfile, updateMoments, updateFollowers} = useContext( - ProfileContext, - ); const [avatarURI, setAvatarURI] = useState<string | null>(null); const [user, setUser] = useState<UserType>(NO_USER); useEffect(() => { @@ -128,21 +124,14 @@ const ProfilePreview: React.FC<ProfilePreviewProps> = ({ } } - //Load user profile and set new moments to true, navigate to Profile - //Load user profile makes sure that we actually load profile of the user the logged in user want to view - //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, - }); - } else { - navigation.push('ProfileView', { - isProfileView: true, - }); - } + /** + * Navigate to profile of the user selected + */ + navigation.push('Profile', { + isProfileView: true, + username: user.username, + userId: user.id, + }); } catch (e) { console.log(e); } diff --git a/src/components/taggs/Tagg.tsx b/src/components/taggs/Tagg.tsx index 9418405d..d9c35b27 100644 --- a/src/components/taggs/Tagg.tsx +++ b/src/components/taggs/Tagg.tsx @@ -1,5 +1,5 @@ import {useNavigation} from '@react-navigation/native'; -import React, {Fragment, useState} from 'react'; +import React, {Fragment, useContext, useState} from 'react'; import {Alert, Linking, StyleSheet, TouchableOpacity, View} from 'react-native'; import PurpleRingPlus from '../../assets/icons/purple_ring+.svg'; import PurpleRing from '../../assets/icons/purple_ring.svg'; @@ -16,6 +16,7 @@ import { registerNonIntegratedSocialLink, } from '../../services'; import {SocialIcon, SocialLinkModal} from '../common'; +import {AuthContext, ProfileContext} from '../../routes'; interface TaggProps { social: string; @@ -39,6 +40,11 @@ const Tagg: React.FC<TaggProps> = ({ const navigation = useNavigation(); const [modalVisible, setModalVisible] = useState(false); const youMayPass = isLinked || isProfileView; + const { + profile: {name}, + socialAccounts, + avatar, + } = isProfileView ? useContext(ProfileContext) : useContext(AuthContext); /* case isProfileView: @@ -64,6 +70,10 @@ const Tagg: React.FC<TaggProps> = ({ navigation.push('SocialMediaTaggs', { socialMediaType: social, isProfileView: isProfileView, + userId: userId, + name: name, + accountData: socialAccounts[social], + avatar: avatar, }); } else { getNonIntegratedURL(social, userId).then((socialURL) => { diff --git a/src/components/taggs/TaggsBar.tsx b/src/components/taggs/TaggsBar.tsx index f2622011..aac68e99 100644 --- a/src/components/taggs/TaggsBar.tsx +++ b/src/components/taggs/TaggsBar.tsx @@ -79,7 +79,8 @@ const TaggsBar: React.FC<TaggsBarProps> = ({ * TODO : Figure out a better way to get the updates social posts for the profile being visited. * Have an event triggered from ProfileProvider based on which we could make a call to backedn to get updated posts. */ - socialsNeedUpdate(INTEGRATED_SOCIAL_LIST); + //We may need the line below in future ? + // socialsNeedUpdate(INTEGRATED_SOCIAL_LIST); loadData(); } }, [isProfileView, taggsNeedUpdate, user.userId]); diff --git a/src/routes/profile/Profile.tsx b/src/routes/profile/Profile.tsx index abf967c6..bffa22ce 100644 --- a/src/routes/profile/Profile.tsx +++ b/src/routes/profile/Profile.tsx @@ -15,7 +15,6 @@ import {RouteProp} from '@react-navigation/native'; * What will be the First Screen of the stack depends on value of isProfileView (Search if its true else Profile) * Trying to explain the purpose of each route on the stack (ACTUALLY A STACK) * Profile : To display the logged in user's profile when isProfileView is false, else displays profile of any user the logged in user wants to view. - * ProfileView : To display profile of a commenter / any user who has commented on a photo. * When you click on the profile icon after looking at a user's profile, the stack is reset and you come back to the top of the stack (First screen : Profile in this case) * Search : To display the search screen. Search for a user on this screen, click on a result tile and navigate to the same (isProfileView = true). * When you click on the search icon after looking at a user's profile, the stack gets reset and you come back to the top of the stack (First screen : Search in this case) @@ -90,12 +89,6 @@ const Profile: React.FC<ProfileStackProps> = ({route}) => { initialParams={{isProfileView: isProfileView}} /> <ProfileStack.Screen - name="ProfileView" - component={ProfileScreen} - options={{headerShown: false}} - initialParams={{isProfileView: isProfileView}} - /> - <ProfileStack.Screen name="MomentCommentsScreen" component={MomentCommentsScreen} options={{headerShown: false}} diff --git a/src/routes/profile/ProfileStack.tsx b/src/routes/profile/ProfileStack.tsx index b535d90d..cba646f8 100644 --- a/src/routes/profile/ProfileStack.tsx +++ b/src/routes/profile/ProfileStack.tsx @@ -1,15 +1,20 @@ import {createStackNavigator} from '@react-navigation/stack'; -import {MomentType} from '../../types'; +import {MomentType, ProfilePreviewType, SocialAccountType} from '../../types'; export type ProfileStackParams = { Search: undefined; Profile: { isProfileView: boolean; + username: string; + userId: string; }; SocialMediaTaggs: { socialMediaType: string; socialMediaHandle: string; isProfileView: boolean; + name: string; + accountData: SocialAccountType; + avatar: string; }; CaptionScreen: { title: string; @@ -23,12 +28,9 @@ export type ProfileStackParams = { isProfileView: boolean; moment_id: string; }; - ProfileView: { - isProfileView: boolean; - }; FollowersListScreen: { - isProfileView: boolean; isFollowers: boolean; + list: ProfilePreviewType[]; }; }; diff --git a/src/routes/viewProfile/ProfileProvider.tsx b/src/routes/viewProfile/ProfileProvider.tsx index f9b29dc6..f2d27a84 100644 --- a/src/routes/viewProfile/ProfileProvider.tsx +++ b/src/routes/viewProfile/ProfileProvider.tsx @@ -22,11 +22,8 @@ import { interface ProfileContextProps { user: UserType; profile: ProfileType; - loadProfile: (userId: string, username: string) => void; avatar: string | null; cover: string | null; - newMomentsAvailable: boolean; - updateMoments: (value: boolean) => void; socialAccounts: Record<string, SocialAccountType>; socialsNeedUpdate: (_: string[]) => void; moments: MomentType[]; @@ -54,11 +51,8 @@ const NO_SOCIAL_ACCOUNTS: Record<string, SocialAccountType> = { export const ProfileContext = createContext<ProfileContextProps>({ user: NO_USER, profile: NO_PROFILE, - loadProfile: () => {}, avatar: null, cover: null, - newMomentsAvailable: true, - updateMoments: () => {}, socialAccounts: NO_SOCIAL_ACCOUNTS, socialsNeedUpdate: () => {}, moments: [], @@ -70,9 +64,20 @@ export const ProfileContext = createContext<ProfileContextProps>({ /** * This is the context provider for user profiles that the logged in user wants to see + * The ProfileProviderProps is used to initialise data as soon as the component is initialised. */ -const ProfileProvider: React.FC = ({children}) => { - const [user, setUser] = useState<UserType>(NO_USER); + +type ProfileProviderProps = { + uname: string; + uId: string; +}; + +const ProfileProvider: React.FC<ProfileProviderProps> = ({ + children, + uId, + uname, +}) => { + const [user, setUser] = useState<UserType>({userId: uId, username: uname}); const [profile, setProfile] = useState<ProfileType>(NO_PROFILE); const [avatar, setAvatar] = useState<string | null>(null); const [cover, setCover] = useState<string | null>(null); @@ -157,8 +162,17 @@ const ProfileProvider: React.FC = ({children}) => { if (socialsNeedUpdate.length > 0 && userId) { for (let social of socialsNeedUpdate) { loadSocialPosts(userId, social).then((accountData) => { - socialAccounts[social] = accountData; - setSocialAccounts(socialAccounts); + /** + * Please use the following syntax when updating an object, fixing this problem broke our head. LOLs + * ref1 : https://stackoverflow.com/questions/56423256/set-dynamic-key-in-state-via-usestate-react-hooks + * ref2: https://stackoverflow.com/questions/43638938/updating-an-object-with-setstate-in-react/43639228 + * The spread operator {...} helps us make a simple copy of the object + * And form there on we can use the [] to specify the dynamically constructed key and set its value. + */ + setSocialAccounts((prevSocialAccounts) => ({ + ...prevSocialAccounts, + [social]: accountData, + })); console.log('Updated posts data', social); }); } @@ -173,18 +187,11 @@ const ProfileProvider: React.FC = ({children}) => { profile, avatar, cover, - newMomentsAvailable, socialAccounts, moments, followers, following, followersNeedUpdate, - loadProfile: (id, username) => { - setUser({...user, userId: id, username}); - }, - updateMoments: (value) => { - setNewMomentsAvailable(value); - }, socialsNeedUpdate: (socials: string[]) => { setSocialsNeedUpdate(socials); }, diff --git a/src/screens/profile/FollowersListScreen.tsx b/src/screens/profile/FollowersListScreen.tsx index 4d14ef67..bad264a1 100644 --- a/src/screens/profile/FollowersListScreen.tsx +++ b/src/screens/profile/FollowersListScreen.tsx @@ -1,9 +1,6 @@ -import React, {useEffect, useState} from 'react'; +import React 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 {ProfilePreviewType} from '../../types'; import {ScrollView} from 'react-native-gesture-handler'; import {SCREEN_HEIGHT} from '../../utils'; import {StyleSheet, View} from 'react-native'; @@ -18,17 +15,7 @@ interface FollowersListScreenProps { } const FollowersListScreen: React.FC<FollowersListScreenProps> = ({route}) => { - const {isProfileView, isFollowers} = route.params; - const {user, followers, following} = isProfileView - ? React.useContext(ProfileContext) - : React.useContext(AuthContext); - const y = Animated.useValue(0); - const [result, setResult] = useState<Array<ProfilePreviewType>>([]); - const top = Animated.useValue(-SCREEN_HEIGHT); - - useEffect(() => { - setResult(isFollowers ? followers : following); - }, [followers, following]); + const {isFollowers, list} = route.params; return ( <CenteredView> @@ -39,7 +26,7 @@ const FollowersListScreen: React.FC<FollowersListScreenProps> = ({route}) => { contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={false}> <Followers - {...{result}} + result={list} sectionTitle={isFollowers ? 'Followers' : 'Following'} /> </ScrollView> diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx index 7d11fa2a..9579a696 100644 --- a/src/screens/profile/ProfileScreen.tsx +++ b/src/screens/profile/ProfileScreen.tsx @@ -3,7 +3,7 @@ import {StatusBar} from 'react-native'; import Animated from 'react-native-reanimated'; import {Content, Cover, TabsGradient} from '../../components'; import {RouteProp} from '@react-navigation/native'; -import {ProfileStackParams} from '../../routes/profile'; +import {ProfileStackParams, ProfileProvider} from '../../routes/'; /** * Profile Screen for a user's profile @@ -17,15 +17,40 @@ interface ProfileOnboardingProps { } const ProfileScreen: React.FC<ProfileOnboardingProps> = ({route}) => { - const {isProfileView} = route.params; + const {isProfileView, username, userId} = route.params; const y = Animated.useValue(0); - return ( - <> - <StatusBar /> - <Cover {...{y, isProfileView}} /> - <Content {...{y, isProfileView}} /> - <TabsGradient /> - </> + + const profileView = () => { + return ( + <> + <StatusBar /> + <Cover {...{y, isProfileView}} /> + <Content {...{y, isProfileView}} /> + <TabsGradient /> + </> + ); + }; + + /** + * Every profile to have it's own ProfileContext if a profile is being visited by the logged in user. + * Pass userid and username of the user whose profile needs to be viewed and the information gets loaded to the ProfileContext. + * Now this profile context is local to every user being viewed. + * This comes with BENEFITS and CAVEATS + * BENEFITS : + * 1 - The app now remembers which user was visited last. + * 2 - The Search and Profile stacks (and potentially more stacks in the future) now have totally unrelated stacks of profiles being visited. + * 3 - This will help us manage the navigation stack better if we introduce a feature to go back to the last profile visited. + * CAVEATS : + * 1 - Since the ProfileContext is not global, whenever we navigate to some component that is not in the child tree of the ProfileProvider, we would not be able to use anything in the context (It would basically have default values). + * 2 - For example, the followers list (FollowersListScreen) and profile picture on the Taggs screen (AvatarTile) now no longer have access to the ProfileContext. + * 3 - Components like these now have to have data passed in via props, instead of using the context. + */ + return isProfileView ? ( + <ProfileProvider uId={userId} uname={username}> + {profileView()} + </ProfileProvider> + ) : ( + profileView() ); }; diff --git a/src/screens/profile/SocialMediaTaggs.tsx b/src/screens/profile/SocialMediaTaggs.tsx index a0321341..d67f3a1a 100644 --- a/src/screens/profile/SocialMediaTaggs.tsx +++ b/src/screens/profile/SocialMediaTaggs.tsx @@ -11,7 +11,7 @@ import { TwitterTaggPost, } from '../../components'; import {AVATAR_GRADIENT} from '../../constants'; -import {AuthContext, ProfileContext, ProfileStackParams} from '../../routes'; +import {ProfileStackParams} from '../../routes'; import {SimplePostType, TwitterPostType} from '../../types'; import {AvatarHeaderHeight, SCREEN_HEIGHT} from '../../utils'; @@ -34,29 +34,21 @@ const SocialMediaTaggs: React.FC<SocialMediaTaggsProps> = ({ route, navigation, }) => { - const {socialMediaType, isProfileView} = route.params; - const context = isProfileView - ? React.useContext(ProfileContext) - : React.useContext(AuthContext); - const { - profile: {name}, - socialAccounts, - } = context; - - let accountData = socialAccounts[socialMediaType]; /** - * Skip setting the header while defining routes and instead let the component itself handle it. - * This lets us pass props dynamically to the header. + * Since access to the loaded context is lost, we need the previous screen to pass in a whole lot of data to this screen. + * This calls for a better state management system such as Redux which allows to have a central repo for the app state. */ + const {socialMediaType, name, accountData, avatar} = route.params; + useEffect(() => { navigation.setOptions({ headerTitle: () => { - return <AvatarTitle isProfileView={isProfileView} />; + return <AvatarTitle avatar={avatar} />; }, headerStyle: {height: AvatarHeaderHeight}, }); - }, [isProfileView, navigation]); + }, [avatar, navigation]); return ( <LinearGradient |