diff options
Diffstat (limited to 'src')
23 files changed, 410 insertions, 180 deletions
diff --git a/src/App.tsx b/src/App.tsx index 2e6865fd..6d51da34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,15 @@ import React from 'react'; import {NavigationContainer} from '@react-navigation/native'; -import Routes, {AuthProvider} from './routes'; +import Routes, {AuthProvider, ProfileProvider} from './routes'; const App = () => { return ( <AuthProvider> - <NavigationContainer> - <Routes /> - </NavigationContainer> + <ProfileProvider> + <NavigationContainer> + <Routes /> + </NavigationContainer> + </ProfileProvider> </AuthProvider> ); }; diff --git a/src/components/common/AvatarTitle.tsx b/src/components/common/AvatarTitle.tsx index 8c82dca9..e9998113 100644 --- a/src/components/common/AvatarTitle.tsx +++ b/src/components/common/AvatarTitle.tsx @@ -2,13 +2,19 @@ import React from 'react'; import {Image, StyleSheet} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import {AVATAR_DIM, AVATAR_GRADIENT_DIM, TAGGS_GRADIENT} from '../../constants'; -import {AuthContext} from '../../routes/authentication'; +import {AuthContext, ProfileContext} from '../../routes/'; /** * An image component that returns the <Image> of the icon for a specific social media platform. */ -const AvatarTitle: React.FC = () => { - const {avatar} = React.useContext(AuthContext); + +type AvatarTitleProps = { + isProfileView: boolean; +}; +const AvatarTitle: React.FC<AvatarTitleProps> = ({isProfileView}) => { + const {avatar} = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); return ( <LinearGradient colors={[TAGGS_GRADIENT.start, TAGGS_GRADIENT.end]} diff --git a/src/components/profile/Avatar.tsx b/src/components/profile/Avatar.tsx index a0f7596c..aca3bf4d 100644 --- a/src/components/profile/Avatar.tsx +++ b/src/components/profile/Avatar.tsx @@ -1,13 +1,16 @@ import React from 'react'; import {Image, StyleSheet} from 'react-native'; -import {AuthContext} from '../../routes/authentication'; +import {AuthContext, ProfileContext} from '../../routes/'; const PROFILE_DIM = 100; interface AvatarProps { style: object; + isProfileView: boolean; } -const Avatar: React.FC<AvatarProps> = ({style}) => { - const {avatar} = React.useContext(AuthContext); +const Avatar: React.FC<AvatarProps> = ({style, isProfileView}) => { + const {avatar} = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); return ( <Image style={[styles.image, style]} diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index a3b9e74a..8d368747 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,5 +1,6 @@ import React, {useState} from 'react'; import {LayoutChangeEvent, StyleSheet, View} from 'react-native'; +import {Text} from 'react-native-animatable'; import Animated from 'react-native-reanimated'; import {defaultMoments} from '../../constants'; import {SCREEN_HEIGHT} from '../../utils'; @@ -11,8 +12,9 @@ import ProfileHeader from './ProfileHeader'; interface ContentProps { y: Animated.Value<number>; + isProfileView: boolean; } -const Content: React.FC<ContentProps> = ({y}) => { +const Content: React.FC<ContentProps> = ({y, isProfileView}) => { const [profileBodyHeight, setProfileBodyHeight] = useState(0); const onLayout = (e: LayoutChangeEvent) => { const {height} = e.nativeEvent.layout; @@ -26,15 +28,19 @@ const Content: React.FC<ContentProps> = ({y}) => { scrollEventThrottle={1} stickyHeaderIndices={[2, 4]}> <ProfileCutout> - <ProfileHeader /> + <ProfileHeader {...{isProfileView}} /> </ProfileCutout> - <ProfileBody {...{onLayout}} /> - <TaggsBar {...{y, profileBodyHeight}} /> - <View style={styles.momentsContainer}> - {defaultMoments.map((title, index) => ( - <Moment key={index} title={title} images={[]} /> - ))} - </View> + <ProfileBody {...{onLayout, isProfileView}} /> + <TaggsBar {...{y, profileBodyHeight, isProfileView}} /> + {!isProfileView ? ( + <View style={styles.momentsContainer}> + {defaultMoments.map((title, index) => ( + <Moment key={index} title={title} images={[]} /> + ))} + </View> + ) : ( + <React.Fragment /> + )} </Animated.ScrollView> ); }; diff --git a/src/components/profile/Cover.tsx b/src/components/profile/Cover.tsx index 01199f06..37ecb9bd 100644 --- a/src/components/profile/Cover.tsx +++ b/src/components/profile/Cover.tsx @@ -2,14 +2,17 @@ import React from 'react'; import {Image, StyleSheet} from 'react-native'; import Animated from 'react-native-reanimated'; import {IMAGE_WIDTH, COVER_HEIGHT} from '../../constants'; -import {AuthContext} from '../../routes/authentication'; +import {AuthContext, ProfileContext} from '../../routes/'; const {interpolate, Extrapolate} = Animated; interface CoverProps { y: Animated.Value<number>; + isProfileView: boolean; } -const Cover: React.FC<CoverProps> = ({y}) => { - const {cover} = React.useContext(AuthContext); +const Cover: React.FC<CoverProps> = ({y, isProfileView}) => { + const {cover} = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); const scale: Animated.Node<number> = interpolate(y, { inputRange: [-COVER_HEIGHT, 0], outputRange: [1.5, 1.25], diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx index e8d8de62..53b86708 100644 --- a/src/components/profile/ProfileBody.tsx +++ b/src/components/profile/ProfileBody.tsx @@ -1,15 +1,18 @@ import React from 'react'; import {StyleSheet, View, Text, LayoutChangeEvent} from 'react-native'; -import {AuthContext} from '../../routes/authentication'; +import {AuthContext, ProfileContext} from '../../routes/'; interface ProfileBodyProps { onLayout: (event: LayoutChangeEvent) => void; + isProfileView: boolean; } -const ProfileBody: React.FC<ProfileBodyProps> = ({onLayout}) => { +const ProfileBody: React.FC<ProfileBodyProps> = ({onLayout, isProfileView}) => { const { profile, user: {username}, - } = React.useContext(AuthContext); + } = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); const {biography, website} = profile; return ( <View onLayout={onLayout} style={styles.container}> diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index ec382357..62c103fd 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -4,16 +4,22 @@ import Avatar from './Avatar'; import FollowCount from './FollowCount'; import {View, Text, StyleSheet} from 'react-native'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import {AuthContext} from '../../routes/authentication'; +import {AuthContext, ProfileContext} from '../../routes/'; -const ProfileHeader: React.FC = () => { +type ProfileHeaderProps = { + isProfileView: boolean; +}; + +const ProfileHeader: React.FC<ProfileHeaderProps> = ({isProfileView}) => { const { profile: {name}, - } = React.useContext(AuthContext); + } = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); return ( <View style={styles.container}> <View style={styles.row}> - <Avatar style={styles.avatar} /> + <Avatar style={styles.avatar} isProfileView={isProfileView} /> <View style={styles.header}> <Text style={styles.name}>{name}</Text> <View style={styles.row}> diff --git a/src/components/search/SearchResult.tsx b/src/components/search/SearchResult.tsx index 952f08f7..cc960308 100644 --- a/src/components/search/SearchResult.tsx +++ b/src/components/search/SearchResult.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useState, useContext} from 'react'; import {ProfilePreviewType} from '../../types'; import { View, @@ -8,10 +8,12 @@ import { ViewProps, TouchableOpacity, } from 'react-native'; +import {useNavigation} from '@react-navigation/native'; 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: '', @@ -24,6 +26,8 @@ const SearchResult: React.FC<SearchResultProps> = ({ profilePreview: {username, first_name, last_name, id}, style, }) => { + const navigation = useNavigation(); + const {loadProfile} = useContext(ProfileContext); const [avatarURI, setAvatarURI] = useState<string | null>(null); const [user, setUser] = useState<UserType>(NO_USER); useEffect(() => { @@ -38,7 +42,7 @@ const SearchResult: React.FC<SearchResultProps> = ({ const response = await RNFetchBlob.config({ fileCache: true, appendExt: 'jpg', - }).fetch('GET', AVATAR_PHOTO_ENDPOINT + `${id}`, { + }).fetch('GET', AVATAR_PHOTO_ENDPOINT + `${id}/`, { Authorization: 'Token ' + token, }); const status = response.info().status; @@ -66,7 +70,7 @@ const SearchResult: React.FC<SearchResultProps> = ({ * Cache maintains 10 recently searched users, popping off the oldest one if * needed to make space. */ - const addToRecentlyStored = async () => { + const addToRecentlyStoredAndNavigateToProfile = async () => { let user: ProfilePreviewType = { id, username, @@ -95,6 +99,15 @@ const SearchResult: React.FC<SearchResultProps> = ({ } else { recentlySearchedList = [user]; } + + //Load user profile and navigate to ProfileView + //Load user profile makes sure that we actually load profile of the user the logged in user want to view + //Not sure if we should make this call before caching the search results ?? + loadProfile(user.id, user.username); + navigation.navigate('Profile', { + isProfileView: true, + }); + try { let recentlySearchedListString = JSON.stringify(recentlySearchedList); await AsyncStorage.setItem( @@ -111,7 +124,7 @@ const SearchResult: React.FC<SearchResultProps> = ({ return ( <TouchableOpacity - onPress={addToRecentlyStored} + onPress={addToRecentlyStoredAndNavigateToProfile} style={[styles.container, style]}> <Image style={styles.avatar} diff --git a/src/components/taggs/Tagg.tsx b/src/components/taggs/Tagg.tsx index 341a713a..d6cffee5 100644 --- a/src/components/taggs/Tagg.tsx +++ b/src/components/taggs/Tagg.tsx @@ -1,12 +1,15 @@ import {useNavigation} from '@react-navigation/native'; import React from 'react'; -import {StyleSheet, TouchableOpacity, View, ViewProps} from 'react-native'; +import {StyleSheet, TouchableOpacity, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import {TAGGS_GRADIENT} from '../../constants'; -interface TaggProps extends ViewProps {} +interface TaggProps { + style: object; + isProfileView: boolean; +} -const Tagg: React.FC<TaggProps> = ({style}) => { +const Tagg: React.FC<TaggProps> = ({style, isProfileView}) => { const navigation = useNavigation(); return ( @@ -14,6 +17,7 @@ const Tagg: React.FC<TaggProps> = ({style}) => { onPress={() => navigation.navigate('SocialMediaTaggs', { socialMediaType: 'Instagram', + isProfileView: isProfileView, }) }> <LinearGradient diff --git a/src/components/taggs/TaggsBar.tsx b/src/components/taggs/TaggsBar.tsx index 1022c4fc..933f355d 100644 --- a/src/components/taggs/TaggsBar.tsx +++ b/src/components/taggs/TaggsBar.tsx @@ -10,11 +10,18 @@ const {View, ScrollView, interpolate, Extrapolate} = Animated; interface TaggsBarProps { y: Animated.Value<number>; profileBodyHeight: number; + isProfileView: boolean; } -const TaggsBar: React.FC<TaggsBarProps> = ({y, profileBodyHeight}) => { +const TaggsBar: React.FC<TaggsBarProps> = ({ + y, + profileBodyHeight, + isProfileView, +}) => { const taggs: Array<JSX.Element> = []; for (let i = 0; i < 10; i++) { - taggs.push(<Tagg key={i} style={styles.tagg} />); + taggs.push( + <Tagg key={i} style={styles.tagg} isProfileView={isProfileView} />, + ); } const shadowOpacity: Animated.Node<number> = interpolate(y, { inputRange: [ diff --git a/src/routes/authentication/AuthProvider.tsx b/src/routes/authentication/AuthProvider.tsx index e5956eb2..6f577a73 100644 --- a/src/routes/authentication/AuthProvider.tsx +++ b/src/routes/authentication/AuthProvider.tsx @@ -1,20 +1,19 @@ import React, {useEffect} from 'react'; import {createContext, useState} from 'react'; -import RNFetchBlob from 'rn-fetch-blob'; -import AsyncStorage from '@react-native-community/async-storage'; import { UserType, ProfileType, InstagramPostType, ProfilePreviewType, } from '../../types'; +import AsyncStorage from '@react-native-community/async-storage'; import { - PROFILE_INFO_ENDPOINT, - AVATAR_PHOTO_ENDPOINT, - COVER_PHOTO_ENDPOINT, - GET_IG_POSTS_ENDPOINT, -} from '../../constants'; -import {Alert} from 'react-native'; + loadProfileInfo, + loadAvatar, + loadCover, + loadInstaPosts, + loadRecentlySearchedUsers, +} from '../../services'; interface AuthContextProps { user: UserType; @@ -63,96 +62,6 @@ const AuthProvider: React.FC = ({children}) => { if (!userId) { return; } - const loadProfileInfo = async (token: string) => { - try { - const response = await fetch(PROFILE_INFO_ENDPOINT + `${userId}/`, { - method: 'GET', - headers: { - Authorization: 'Token ' + token, - }, - }); - const status = response.status; - if (status === 200) { - const info = await response.json(); - let {name, biography, website} = info; - setProfile({name, biography, website}); - } - } catch (error) { - Alert.alert( - 'Something went wrong! ðŸ˜', - "Would you believe me if I told you that I don't know what happened?", - ); - } - }; - const loadAvatar = async (token: string) => { - try { - const response = await RNFetchBlob.config({ - fileCache: true, - appendExt: 'jpg', - }).fetch('GET', AVATAR_PHOTO_ENDPOINT + `${userId}/`, { - Authorization: 'Token ' + token, - }); - const status = response.info().status; - if (status === 200) { - setAvatar(response.path()); - } else { - setAvatar(''); - } - } catch (error) { - console.log(error); - } - }; - const loadCover = async (token: string) => { - try { - let response = await RNFetchBlob.config({ - fileCache: true, - appendExt: 'jpg', - }).fetch('GET', COVER_PHOTO_ENDPOINT + `${userId}/`, { - Authorization: 'Token ' + token, - }); - const status = response.info().status; - if (status === 200) { - setCover(response.path()); - } else { - setCover(''); - } - } catch (error) { - console.log(error); - } - }; - const loadInstaPosts = async (token: string) => { - try { - const response = await fetch(GET_IG_POSTS_ENDPOINT + `${userId}/`, { - method: 'GET', - headers: { - Authorization: 'Token ' + token, - }, - }); - const status = response.status; - if (status === 200) { - let ig_posts = await response.json(); - setInstaPosts(ig_posts); - } else { - setInstaPosts([]); - } - } catch (error) { - console.log(error); - Alert.alert( - 'Something went wrong! ðŸ˜', - "Would you believe me if I told you that I don't know what happened?", - ); - } - }; - const loadRecentlySearchedUsers = async () => { - try { - const asyncCache = await AsyncStorage.getItem( - '@recently_searched_users', - ); - asyncCache != null ? setRecentSearches(JSON.parse(asyncCache)) : null; - } catch (e) { - console.log(e); - } - }; const loadData = async () => { try { @@ -161,11 +70,11 @@ const AuthProvider: React.FC = ({children}) => { setUser(NO_USER); return; } - loadProfileInfo(token); - loadAvatar(token); - loadCover(token); - loadInstaPosts(token); - loadRecentlySearchedUsers(); + loadProfileInfo(token, userId, setProfile); + loadAvatar(token, userId, setAvatar); + loadCover(token, userId, setCover); + loadInstaPosts(token, userId, setInstaPosts); + loadRecentlySearchedUsers(setRecentSearches); } catch (err) { console.log(err); } diff --git a/src/routes/index.ts b/src/routes/index.ts index c06845aa..7e8a84ce 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,5 +1,7 @@ export {default as AuthProvider} from './authentication'; +export {default as ProfileProvider} from './viewProfile'; export * from './authentication'; +export * from './viewProfile'; export * from './onboarding'; export * from './profile'; export {default} from './Routes'; diff --git a/src/routes/profile/Profile.tsx b/src/routes/profile/Profile.tsx index eaf5f3aa..b39b726e 100644 --- a/src/routes/profile/Profile.tsx +++ b/src/routes/profile/Profile.tsx @@ -1,14 +1,36 @@ import React from 'react'; +import { + ProfileScreen, + CaptionScreen, + SocialMediaTaggs, + SearchScreen, +} from '../../screens'; +import {RouteProp} from '@react-navigation/native'; +import {ProfileStack, ProfileStackParams} from './ProfileStack'; import {AvatarTitle} from '../../components'; -import {ProfileScreen, CaptionScreen, SocialMediaTaggs} from '../../screens'; -import {ProfileStack} from './ProfileStack'; -const Profile: React.FC = () => { +type ProfileScreenRouteProps = RouteProp<ProfileStackParams, 'Profile'>; + +interface ProfileScreenProps { + route: ProfileScreenRouteProps; +} + +const Profile: React.FC<ProfileScreenProps> = ({route}) => { + const {isProfileView} = route.params; return ( <ProfileStack.Navigator - initialRouteName="Profile" + initialRouteName={!isProfileView ? `Profile` : `Search`} screenOptions={{headerShown: false}}> - <ProfileStack.Screen name="Profile" component={ProfileScreen} /> + <ProfileStack.Screen + name="Profile" + component={ProfileScreen} + initialParams={{isProfileView: isProfileView}} + /> + {isProfileView ? ( + <ProfileStack.Screen name="Search" component={SearchScreen} /> + ) : ( + <React.Fragment /> + )} <ProfileStack.Screen name="SocialMediaTaggs" component={SocialMediaTaggs} @@ -17,10 +39,14 @@ const Profile: React.FC = () => { headerTransparent: true, headerBackTitleVisible: false, headerTintColor: 'white', - headerTitle: () => <AvatarTitle />, + headerTitle: () => <AvatarTitle isProfileView={isProfileView} />, }} /> - <ProfileStack.Screen name="CaptionScreen" component={CaptionScreen} /> + {!isProfileView ? ( + <ProfileStack.Screen name="CaptionScreen" component={CaptionScreen} /> + ) : ( + <React.Fragment /> + )} </ProfileStack.Navigator> ); }; diff --git a/src/routes/profile/ProfileStack.tsx b/src/routes/profile/ProfileStack.tsx index c1da67c1..63ab9a10 100644 --- a/src/routes/profile/ProfileStack.tsx +++ b/src/routes/profile/ProfileStack.tsx @@ -1,10 +1,14 @@ import {createStackNavigator} from '@react-navigation/stack'; export type ProfileStackParams = { - Profile: undefined; + Search: undefined; + Profile: { + isProfileView: boolean; + }; SocialMediaTaggs: { socialMediaType: string; socialMediaHandle: string; + isProfileView: boolean; }; CaptionScreen: {title: string; image: object}; }; diff --git a/src/routes/tabs/NavigationBar.tsx b/src/routes/tabs/NavigationBar.tsx index 456e923f..2852b565 100644 --- a/src/routes/tabs/NavigationBar.tsx +++ b/src/routes/tabs/NavigationBar.tsx @@ -1,7 +1,7 @@ import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'; import React from 'react'; import {NavigationIcon} from '../../components'; -import {Home, Notifications, SearchScreen, Upload} from '../../screens'; +import {Home, Notifications, Upload} from '../../screens'; import Profile from '../profile'; const Tabs = createBottomTabNavigator(); @@ -57,10 +57,18 @@ const NavigationBar: React.FC = () => { }, }}> <Tabs.Screen name="Home" component={Home} /> - <Tabs.Screen name="Search" component={SearchScreen} /> + <Tabs.Screen + name="Search" + component={Profile} + initialParams={{isProfileView: true}} + /> <Tabs.Screen name="Upload" component={Upload} /> <Tabs.Screen name="Notifications" component={Notifications} /> - <Tabs.Screen name="Profile" component={Profile} /> + <Tabs.Screen + name="Profile" + component={Profile} + initialParams={{isProfileView: false}} + /> </Tabs.Navigator> ); }; diff --git a/src/routes/viewProfile/ProfileProvider.tsx b/src/routes/viewProfile/ProfileProvider.tsx new file mode 100644 index 00000000..1af7917d --- /dev/null +++ b/src/routes/viewProfile/ProfileProvider.tsx @@ -0,0 +1,91 @@ +import React, {useEffect} from 'react'; +import {createContext, useState} from 'react'; +import AsyncStorage from '@react-native-community/async-storage'; +import {UserType, ProfileType, InstagramPostType} from '../../types'; + +import { + loadProfileInfo, + loadAvatar, + loadCover, + loadInstaPosts, + loadRecentlySearchedUsers, +} from '../../services'; + +interface ProfileContextProps { + user: UserType; + profile: ProfileType; + loadProfile: (userId: string, username: string) => void; + avatar: string | null; + cover: string | null; + instaPosts: Array<InstagramPostType>; +} +const NO_USER: UserType = { + userId: '', + username: '', +}; +const NO_PROFILE: ProfileType = { + biography: '', + website: '', + name: '', +}; +export const ProfileContext = createContext<ProfileContextProps>({ + user: NO_USER, + profile: NO_PROFILE, + loadProfile: () => {}, + avatar: null, + cover: null, + instaPosts: [], +}); + +/** + * This is the context provider for user profiles that the logged in user wants to see + */ +const ProfileProvider: React.FC = ({children}) => { + const [user, setUser] = useState<UserType>(NO_USER); + const [profile, setProfile] = useState<ProfileType>(NO_PROFILE); + const [avatar, setAvatar] = useState<string | null>(null); + const [cover, setCover] = useState<string | null>(null); + const [instaPosts, setInstaPosts] = useState<Array<InstagramPostType>>([]); + + const {userId} = user; + useEffect(() => { + if (!userId) { + return; + } + + const loadData = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + setUser(NO_USER); + return; + } + loadProfileInfo(token, userId, setProfile); + loadAvatar(token, userId, setAvatar); + loadCover(token, userId, setCover); + loadInstaPosts(token, userId, setInstaPosts); + } catch (err) { + console.log(err); + } + }; + loadData(); + }, [userId]); + + return ( + <ProfileContext.Provider + value={{ + user, + profile, + avatar, + cover, + instaPosts, + loadProfile: (id, username) => { + setUser({...user, userId: id, username}); + }, + }}> + {children} + </ProfileContext.Provider> + ); +}; + +export default ProfileProvider; diff --git a/src/routes/viewProfile/index.ts b/src/routes/viewProfile/index.ts new file mode 100644 index 00000000..7035ce4a --- /dev/null +++ b/src/routes/viewProfile/index.ts @@ -0,0 +1,2 @@ +export * from './ProfileProvider'; +export {default} from './ProfileProvider'; diff --git a/src/screens/onboarding/InvitationCodeVerification.tsx b/src/screens/onboarding/InvitationCodeVerification.tsx index fd5f828b..8a3d5982 100644 --- a/src/screens/onboarding/InvitationCodeVerification.tsx +++ b/src/screens/onboarding/InvitationCodeVerification.tsx @@ -10,7 +10,7 @@ import { LoadingIndicator, } from '../../components'; -import {VERIFY_INVITATION_CODE_ENDPOUNT} from "../../constants" +import {VERIFY_INVITATION_CODE_ENDPOUNT} from '../../constants'; import {Text} from 'react-native-animatable'; import { @@ -53,18 +53,19 @@ const InvitationCodeVerification: React.FC<InvitationCodeVerificationProps> = ({ }); const handleInvitationCodeVerification = async () => { - if(value.length === 6){ - try { - let verifyInviteCodeResponse = await fetch(VERIFY_INVITATION_CODE_ENDPOUNT + value + '/', { - method: 'DELETE', - }); + if (value.length === 6) { + try { + let verifyInviteCodeResponse = await fetch( + VERIFY_INVITATION_CODE_ENDPOUNT + value + '/', + { + method: 'DELETE', + }, + ); if (verifyInviteCodeResponse.status == 200) { navigation.navigate('RegistrationOne'); } else { - Alert.alert( - 'Invalid invitation code 🤔', - ); + Alert.alert('Invalid invitation code 🤔'); } } catch (error) { Alert.alert( @@ -76,9 +77,8 @@ const InvitationCodeVerification: React.FC<InvitationCodeVerificationProps> = ({ description: error, }; } - } - else{ - Alert.alert("The code entered is not valid!"); + } else { + Alert.alert('The code entered is not valid!'); } }; @@ -97,7 +97,8 @@ const InvitationCodeVerification: React.FC<InvitationCodeVerificationProps> = ({ <KeyboardAvoidingView behavior="padding" style={styles.form}> <Text style={styles.formHeader}>Enter the code</Text> <Text style={styles.description}> - Please enter the invitation code provided to you by us / your friend. (Use all caps.) + Please enter the invitation code provided to you by us / your friend. + (Use all caps.) </Text> <CodeField ref={ref} diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index e3040509..53c47a6d 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -42,14 +42,14 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { setCaption(caption); }; - const checkImageUploadStatus = (statusMap : object) => { - for(let [key, value] of Object.entries(statusMap)){ - if (value != "Success"){ + const checkImageUploadStatus = (statusMap: object) => { + for (let [key, value] of Object.entries(statusMap)) { + if (value != 'Success') { return false; } } return true; - } + }; const handleShare = async () => { try { @@ -68,7 +68,7 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { }); request.append('moment', title); request.append('user_id', userId); - request.append('captions', JSON.stringify({'image':caption})); + request.append('captions', JSON.stringify({image: caption})); let response = await fetch(MOMENTS_UPLOAD_ENDPOINT, { method: 'POST', headers: { diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx index cc388ffd..7d11fa2a 100644 --- a/src/screens/profile/ProfileScreen.tsx +++ b/src/screens/profile/ProfileScreen.tsx @@ -2,20 +2,28 @@ import React from 'react'; import {StatusBar} from 'react-native'; import Animated from 'react-native-reanimated'; import {Content, Cover, TabsGradient} from '../../components'; -import {AuthContext} from '../../routes/authentication'; +import {RouteProp} from '@react-navigation/native'; +import {ProfileStackParams} from '../../routes/profile'; /** - * Profile Screen for a user's logged in profile + * Profile Screen for a user's profile * including posts, messaging, and settings */ -const ProfileScreen: React.FC = () => { - const {user} = React.useContext(AuthContext); + +type ProfileScreenRouteProps = RouteProp<ProfileStackParams, 'Profile'>; + +interface ProfileOnboardingProps { + route: ProfileScreenRouteProps; +} + +const ProfileScreen: React.FC<ProfileOnboardingProps> = ({route}) => { + const {isProfileView} = route.params; const y = Animated.useValue(0); return ( <> <StatusBar /> - <Cover {...{y, user}} /> - <Content {...{y, user}} /> + <Cover {...{y, isProfileView}} /> + <Content {...{y, isProfileView}} /> <TabsGradient /> </> ); diff --git a/src/screens/profile/SocialMediaTaggs.tsx b/src/screens/profile/SocialMediaTaggs.tsx index 9e4f2aea..ddbebcea 100644 --- a/src/screens/profile/SocialMediaTaggs.tsx +++ b/src/screens/profile/SocialMediaTaggs.tsx @@ -4,7 +4,7 @@ import {ScrollView, StatusBar, StyleSheet, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import {AVATAR_GRADIENT} from '../../constants'; import {SocialMediaInfo, TabsGradient, TaggsFeed} from '../../components'; -import {AuthContext, ProfileStackParams} from '../../routes'; +import {AuthContext, ProfileStackParams, ProfileContext} from '../../routes'; import {headerBarHeightWithImage, SCREEN_HEIGHT} from '../../utils'; type SocialMediaTaggsRouteProp = RouteProp< @@ -27,17 +27,21 @@ interface SocialMediaTaggsProps { * + date posted * + dark background */ -const SocialMediaTaggs: React.FC<SocialMediaTaggsProps> = () => { +const SocialMediaTaggs: React.FC<SocialMediaTaggsProps> = ({route}) => { + const {isProfileView} = route.params; + const context = isProfileView + ? React.useContext(ProfileContext) + : React.useContext(AuthContext); const { user, profile: {name}, - } = React.useContext(AuthContext); + } = context; // TODO: We should use the passed-in socialmedia type/handle instead. // Currently don't have an intuitive way of doing so, for now, // just grabbing from user's AuthContext. // const {socialMediaType, socialMediaHandle} = route.params; - const {instaPosts} = React.useContext(AuthContext); + const {instaPosts} = context; const socialMediaType = 'Instagram'; const socialMediaHandle = instaPosts[0].username; diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts new file mode 100644 index 00000000..4c3af06a --- /dev/null +++ b/src/services/UserProfileService.ts @@ -0,0 +1,121 @@ +//Abstracted common profile api calls out here + +import {Alert} from 'react-native'; +import { + PROFILE_INFO_ENDPOINT, + AVATAR_PHOTO_ENDPOINT, + COVER_PHOTO_ENDPOINT, + GET_IG_POSTS_ENDPOINT, +} from '../constants'; + +import AsyncStorage from '@react-native-community/async-storage'; +import RNFetchBlob from 'rn-fetch-blob'; + +export const loadProfileInfo = async ( + token: string, + userId: string, + callback: Function, +) => { + try { + const response = await fetch(PROFILE_INFO_ENDPOINT + `${userId}/`, { + method: 'GET', + headers: { + Authorization: 'Token ' + token, + }, + }); + const status = response.status; + if (status === 200) { + const info = await response.json(); + let {name, biography, website} = info; + callback({name, biography, website}); + } + } catch (error) { + Alert.alert( + 'Something went wrong! ðŸ˜', + "Would you believe me if I told you that I don't know what happened?", + ); + } +}; + +export const loadAvatar = async ( + token: string, + userId: string, + callback: Function, +) => { + try { + const response = await RNFetchBlob.config({ + fileCache: true, + appendExt: 'jpg', + }).fetch('GET', AVATAR_PHOTO_ENDPOINT + `${userId}/`, { + Authorization: 'Token ' + token, + }); + const status = response.info().status; + if (status === 200) { + callback(response.path()); + } else { + callback(''); + } + } catch (error) { + console.log(error); + } +}; + +export const loadCover = async ( + token: string, + userId: string, + callback: Function, +) => { + try { + let response = await RNFetchBlob.config({ + fileCache: true, + appendExt: 'jpg', + }).fetch('GET', COVER_PHOTO_ENDPOINT + `${userId}/`, { + Authorization: 'Token ' + token, + }); + const status = response.info().status; + if (status === 200) { + callback(response.path()); + } else { + callback(''); + } + } catch (error) { + console.log(error); + } +}; + +export const loadInstaPosts = async ( + token: string, + userId: string, + callback: Function, +) => { + try { + const response = await fetch(GET_IG_POSTS_ENDPOINT + `${userId}/`, { + method: 'GET', + headers: { + Authorization: 'Token ' + token, + }, + }); + const status = response.status; + if (status === 200) { + let ig_posts = await response.json(); + callback(ig_posts); + } else { + callback([]); + } + } catch (error) { + console.log(error); + Alert.alert( + 'Something went wrong! ðŸ˜', + "Would you believe me if I told you that I don't know what happened?", + ); + } +}; + +export const loadRecentlySearchedUsers = async (callback: Function) => { + try { + const asyncCache = await AsyncStorage.getItem('@recently_searched_users'); + asyncCache != null ? callback(JSON.parse(asyncCache)) : null; + } catch (e) { + console.log(e); + } +}; diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 00000000..5cd06cfe --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1 @@ +export * from './UserProfileService'; |