aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeon Jiang <35908040+leonyjiang@users.noreply.github.com>2020-08-05 14:15:06 -0700
committerGitHub <noreply@github.com>2020-08-05 17:15:06 -0400
commit1279249ee9355f88913578f51e3b0bf7d99672f6 (patch)
tree4a72890af331ffc818fffc9fb5395a80efe2d7de
parentf9cf9b5d89d5e25b227814f0fc759257564cea89 (diff)
[TMA-122] User Profile Screen UI (#27)
* Fix yarn lint issues * Add react-native-svg to project * Create UserType & PostType * Create temporary Post component * Fix import cycle warning, update AuthContext * Update onboarding screen imports * Update config files * Add rn-fetch-blob package * Update types * Add profile fetching to AuthContext * Update post component * Import placeholder images from designs * Add profile UI components * Create screen offset constants * Add new api endpoints * Create screen layout utils * Create Profile screen UI * Remove some unused styling * Restructure ProfileScreen and fix animations * Add gradient back to screen * Update Moment circle styling
-rw-r--r--.watchmanconfig1
-rw-r--r--ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend-tvOS.xcscheme2
-rw-r--r--ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend.xcscheme2
-rw-r--r--ios/Podfile2
-rw-r--r--ios/Podfile.lock14
-rw-r--r--package.json4
-rw-r--r--src/App.tsx6
-rw-r--r--src/assets/images/avatar-placeholder.pngbin0 -> 2982 bytes
-rw-r--r--src/assets/images/avatar-placeholder@2x.pngbin0 -> 6245 bytes
-rw-r--r--src/assets/images/avatar-placeholder@3x.pngbin0 -> 7604 bytes
-rw-r--r--src/assets/images/cover-placeholder.pngbin0 -> 10081 bytes
-rw-r--r--src/assets/images/cover-placeholder@2x.pngbin0 -> 24052 bytes
-rw-r--r--src/assets/images/cover-placeholder@3x.pngbin0 -> 41741 bytes
-rw-r--r--src/components/common/GradientBackground.tsx17
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/common/post/Post.tsx26
-rw-r--r--src/components/common/post/PostHeader.tsx51
-rw-r--r--src/components/common/post/index.ts1
-rw-r--r--src/components/index.ts1
-rw-r--r--src/components/profile/Avatar.tsx31
-rw-r--r--src/components/profile/Content.tsx58
-rw-r--r--src/components/profile/Cover.tsx41
-rw-r--r--src/components/profile/Feed.tsx25
-rw-r--r--src/components/profile/FollowCount.tsx42
-rw-r--r--src/components/profile/Moment.tsx35
-rw-r--r--src/components/profile/MomentsBar.tsx75
-rw-r--r--src/components/profile/ProfileBody.tsx46
-rw-r--r--src/components/profile/ProfileCutout.tsx23
-rw-r--r--src/components/profile/ProfileHeader.tsx62
-rw-r--r--src/components/profile/index.ts7
-rw-r--r--src/constants/api.ts17
-rw-r--r--src/constants/constants.ts10
-rw-r--r--src/constants/index.ts1
-rw-r--r--src/routes/NavigationBar.tsx75
-rw-r--r--src/routes/OnboardingStack.tsx57
-rw-r--r--src/routes/Routes.tsx44
-rw-r--r--src/routes/authentication/AuthProvider.tsx122
-rw-r--r--src/routes/authentication/index.ts2
-rw-r--r--src/routes/index.ts10
-rw-r--r--src/routes/onboarding/Onboarding.tsx43
-rw-r--r--src/routes/onboarding/OnboardingStack.tsx13
-rw-r--r--src/routes/onboarding/index.ts2
-rw-r--r--src/routes/tabs/NavigationBar.tsx73
-rw-r--r--src/routes/tabs/index.ts1
-rw-r--r--src/screens/index.ts3
-rw-r--r--src/screens/main/Profile.tsx24
-rw-r--r--src/screens/main/index.ts1
-rw-r--r--src/screens/onboarding/Login.tsx21
-rw-r--r--src/screens/onboarding/ProfileOnboarding.tsx11
-rw-r--r--src/screens/onboarding/RegistrationOne.tsx6
-rw-r--r--src/screens/onboarding/RegistrationTwo.tsx6
-rw-r--r--src/screens/onboarding/Verification.tsx6
-rw-r--r--src/screens/onboarding/index.ts2
-rw-r--r--src/screens/profile/ProfileScreen.tsx26
-rw-r--r--src/screens/profile/index.ts1
-rw-r--r--src/types/index.ts1
-rw-r--r--src/types/types.ts14
-rw-r--r--src/utils/index.ts2
-rw-r--r--src/utils/screenDimensions.ts6
-rw-r--r--src/utils/statusBarHeight.ts19
-rw-r--r--yarn.lock106
61 files changed, 1049 insertions, 249 deletions
diff --git a/.watchmanconfig b/.watchmanconfig
deleted file mode 100644
index 9e26dfee..00000000
--- a/.watchmanconfig
+++ /dev/null
@@ -1 +0,0 @@
-{} \ No newline at end of file
diff --git a/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend-tvOS.xcscheme b/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend-tvOS.xcscheme
index 3fe395bc..a11b4188 100644
--- a/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend-tvOS.xcscheme
+++ b/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend-tvOS.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1130"
+ LastUpgradeVersion = "1160"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
diff --git a/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend.xcscheme b/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend.xcscheme
index 5c33f141..c3b9ba97 100644
--- a/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend.xcscheme
+++ b/ios/Frontend.xcodeproj/xcshareddata/xcschemes/Frontend.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1130"
+ LastUpgradeVersion = "1160"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
diff --git a/ios/Podfile b/ios/Podfile
index 6f9beedf..23f0e008 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -101,4 +101,4 @@ target 'Frontend-tvOS' do
inherit! :search_paths
# Pods for testing
end
-end \ No newline at end of file
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 061a9b9d..2e594aa9 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -296,6 +296,8 @@ PODS:
- React-cxxreact (= 0.62.2)
- React-jsi (= 0.62.2)
- ReactCommon/callinvoker (= 0.62.2)
+ - rn-fetch-blob (0.12.0):
+ - React-Core
- RNCMaskedView (0.1.10):
- React
- RNGestureHandler (1.6.1):
@@ -313,6 +315,8 @@ PODS:
- React
- RNScreens (2.9.0):
- React
+ - RNSVG (12.1.0):
+ - React
- TOCropViewController (2.5.3)
- Yoga (1.14.0)
- YogaKit (1.18.1):
@@ -367,11 +371,13 @@ DEPENDENCIES:
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
+ - rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
- "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
+ - RNSVG (from `../node_modules/react-native-svg`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@@ -443,6 +449,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/Vibration"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
+ rn-fetch-blob:
+ :path: "../node_modules/rn-fetch-blob"
RNCMaskedView:
:path: "../node_modules/@react-native-community/masked-view"
RNGestureHandler:
@@ -453,6 +461,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
+ RNSVG:
+ :path: "../node_modules/react-native-svg"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
@@ -494,15 +504,17 @@ SPEC CHECKSUMS:
React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
+ rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
RNImageCropPicker: f0557a908758c4a3f83978894ec7227651529b45
RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
+ RNSVG: ce9d996113475209013317e48b05c21ee988d42e
TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6
Yoga: 3ebccbdd559724312790e7742142d062476b698e
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: 7030158ba440c1ffa1ab55bb64e73d7d8c48256b
+PODFILE CHECKSUM: f5b485075b23881307c1dba4f8874d96683d3678
COCOAPODS: 1.9.3
diff --git a/package.json b/package.json
index d881c6f1..30456a10 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,9 @@
"react-native-reanimated": "^1.9.0",
"react-native-safe-area-context": "^3.0.6",
"react-native-screens": "^2.9.0",
- "react-promise-tracker": "^2.1.0"
+ "react-native-svg": "^12.1.0",
+ "react-promise-tracker": "^2.1.0",
+ "rn-fetch-blob": "^0.12.0"
},
"devDependencies": {
"@babel/core": "^7.6.2",
diff --git a/src/App.tsx b/src/App.tsx
index 5abf1ff4..2e6865fd 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,14 +1,14 @@
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
-import {Routes, AuthContextProvider} from './routes';
+import Routes, {AuthProvider} from './routes';
const App = () => {
return (
- <AuthContextProvider>
+ <AuthProvider>
<NavigationContainer>
<Routes />
</NavigationContainer>
- </AuthContextProvider>
+ </AuthProvider>
);
};
diff --git a/src/assets/images/avatar-placeholder.png b/src/assets/images/avatar-placeholder.png
new file mode 100644
index 00000000..313f384e
--- /dev/null
+++ b/src/assets/images/avatar-placeholder.png
Binary files differ
diff --git a/src/assets/images/avatar-placeholder@2x.png b/src/assets/images/avatar-placeholder@2x.png
new file mode 100644
index 00000000..d038441e
--- /dev/null
+++ b/src/assets/images/avatar-placeholder@2x.png
Binary files differ
diff --git a/src/assets/images/avatar-placeholder@3x.png b/src/assets/images/avatar-placeholder@3x.png
new file mode 100644
index 00000000..814472ec
--- /dev/null
+++ b/src/assets/images/avatar-placeholder@3x.png
Binary files differ
diff --git a/src/assets/images/cover-placeholder.png b/src/assets/images/cover-placeholder.png
new file mode 100644
index 00000000..e27eb9bd
--- /dev/null
+++ b/src/assets/images/cover-placeholder.png
Binary files differ
diff --git a/src/assets/images/cover-placeholder@2x.png b/src/assets/images/cover-placeholder@2x.png
new file mode 100644
index 00000000..4aafe9d4
--- /dev/null
+++ b/src/assets/images/cover-placeholder@2x.png
Binary files differ
diff --git a/src/assets/images/cover-placeholder@3x.png b/src/assets/images/cover-placeholder@3x.png
new file mode 100644
index 00000000..991f5d63
--- /dev/null
+++ b/src/assets/images/cover-placeholder@3x.png
Binary files differ
diff --git a/src/components/common/GradientBackground.tsx b/src/components/common/GradientBackground.tsx
index f363bd61..c1247ca2 100644
--- a/src/components/common/GradientBackground.tsx
+++ b/src/components/common/GradientBackground.tsx
@@ -5,20 +5,19 @@ import {
TouchableWithoutFeedback,
Keyboard,
ViewProps,
- SafeAreaView,
} from 'react-native';
interface GradientBackgroundProps extends ViewProps {}
const GradientBackground: React.FC<GradientBackgroundProps> = (props) => {
return (
- <LinearGradient
- locations={[0.89, 1]}
- colors={['transparent', 'rgba(0, 0, 0, 0.6)']}
- style={styles.container}>
- <TouchableWithoutFeedback accessible={false} onPress={Keyboard.dismiss}>
- <SafeAreaView {...props}>{props.children}</SafeAreaView>
- </TouchableWithoutFeedback>
- </LinearGradient>
+ <TouchableWithoutFeedback accessible={false} onPress={Keyboard.dismiss}>
+ <LinearGradient
+ locations={[0.89, 1]}
+ colors={['transparent', 'rgba(0, 0, 0, 0.6)']}
+ style={styles.container}>
+ {props.children}
+ </LinearGradient>
+ </TouchableWithoutFeedback>
);
};
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
index a1bcc558..826675ff 100644
--- a/src/components/common/index.ts
+++ b/src/components/common/index.ts
@@ -4,3 +4,4 @@ export {default as RadioCheckbox} from './RadioCheckbox';
export {default as TaggInput} from './TaggInput';
export {default as NavigationIcon} from './NavigationIcon';
export {default as GradientBackground} from './GradientBackground';
+export {default as Post} from './post';
diff --git a/src/components/common/post/Post.tsx b/src/components/common/post/Post.tsx
new file mode 100644
index 00000000..d6c5a7d6
--- /dev/null
+++ b/src/components/common/post/Post.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import {StyleSheet, View} from 'react-native';
+import {PostType} from '../../../types';
+import PostHeader from './PostHeader';
+import {SCREEN_WIDTH} from '../../../utils';
+
+interface PostProps {
+ post: PostType;
+}
+const Post: React.FC<PostProps> = ({post: {owner}}) => {
+ return (
+ <>
+ <PostHeader owner={owner} />
+ <View style={styles.image} />
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ image: {
+ width: SCREEN_WIDTH,
+ height: SCREEN_WIDTH,
+ backgroundColor: '#eee',
+ },
+});
+export default Post;
diff --git a/src/components/common/post/PostHeader.tsx b/src/components/common/post/PostHeader.tsx
new file mode 100644
index 00000000..8558d21d
--- /dev/null
+++ b/src/components/common/post/PostHeader.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import {UserType} from '../../../types';
+import {View, StyleSheet, Image, Text} from 'react-native';
+import {AuthContext} from '../../../routes/authentication';
+
+const AVATAR_DIM = 35;
+interface PostHeaderProps {
+ owner: UserType;
+}
+const PostHeader: React.FC<PostHeaderProps> = ({owner: {username}}) => {
+ const {avatar} = React.useContext(AuthContext);
+ return (
+ <View style={styles.container}>
+ <View style={styles.leftElem}>
+ <Image
+ style={styles.avatar}
+ source={
+ avatar
+ ? {uri: avatar}
+ : require('../../../assets/images/avatar-placeholder.png')
+ }
+ />
+ <Text style={styles.username}>{username}</Text>
+ </View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ padding: 10,
+ backgroundColor: 'white',
+ },
+ leftElem: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ avatar: {
+ width: AVATAR_DIM,
+ height: AVATAR_DIM,
+ borderRadius: AVATAR_DIM / 2,
+ marginRight: 10,
+ },
+ username: {
+ fontSize: 18,
+ },
+});
+
+export default PostHeader;
diff --git a/src/components/common/post/index.ts b/src/components/common/post/index.ts
new file mode 100644
index 00000000..033f8a8d
--- /dev/null
+++ b/src/components/common/post/index.ts
@@ -0,0 +1 @@
+export {default} from './Post';
diff --git a/src/components/index.ts b/src/components/index.ts
index 724b14ac..48b7df05 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,2 +1,3 @@
export * from './common';
export * from './onboarding';
+export * from './profile';
diff --git a/src/components/profile/Avatar.tsx b/src/components/profile/Avatar.tsx
new file mode 100644
index 00000000..a0f7596c
--- /dev/null
+++ b/src/components/profile/Avatar.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import {Image, StyleSheet} from 'react-native';
+import {AuthContext} from '../../routes/authentication';
+
+const PROFILE_DIM = 100;
+interface AvatarProps {
+ style: object;
+}
+const Avatar: React.FC<AvatarProps> = ({style}) => {
+ const {avatar} = React.useContext(AuthContext);
+ return (
+ <Image
+ style={[styles.image, style]}
+ source={
+ avatar
+ ? {uri: avatar}
+ : require('../../assets/images/avatar-placeholder.png')
+ }
+ />
+ );
+};
+
+const styles = StyleSheet.create({
+ image: {
+ height: PROFILE_DIM,
+ width: PROFILE_DIM,
+ borderRadius: PROFILE_DIM / 2,
+ },
+});
+
+export default Avatar;
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx
new file mode 100644
index 00000000..82b5fdc0
--- /dev/null
+++ b/src/components/profile/Content.tsx
@@ -0,0 +1,58 @@
+import React, {useState} from 'react';
+import {StyleSheet, LayoutChangeEvent} from 'react-native';
+import Animated from 'react-native-reanimated';
+const {ScrollView} = Animated;
+
+import {UserType} from '../../types';
+import ProfileCutout from './ProfileCutout';
+import ProfileHeader from './ProfileHeader';
+import ProfileBody from './ProfileBody';
+import MomentsBar from './MomentsBar';
+import Feed from './Feed';
+import LinearGradient from 'react-native-linear-gradient';
+import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+interface ContentProps {
+ y: Animated.Value<number>;
+ user: UserType;
+}
+const Content: React.FC<ContentProps> = ({y, user}) => {
+ const [profileBodyHeight, setProfileBodyHeight] = useState(0);
+ const onLayout = (e: LayoutChangeEvent) => {
+ const {height} = e.nativeEvent.layout;
+ setProfileBodyHeight(height);
+ };
+ return (
+ <ScrollView
+ style={styles.container}
+ onScroll={(e) => y.setValue(e.nativeEvent.contentOffset.y)}
+ showsVerticalScrollIndicator={false}
+ scrollEventThrottle={1}
+ stickyHeaderIndices={[2, 4]}>
+ <ProfileCutout>
+ <ProfileHeader />
+ </ProfileCutout>
+ <ProfileBody {...{onLayout}} />
+ <MomentsBar {...{y, profileBodyHeight}} />
+ <Feed {...{user}} />
+ <LinearGradient
+ locations={[0.89, 1]}
+ colors={['transparent', 'rgba(0, 0, 0, 0.6)']}
+ style={styles.gradient}
+ />
+ </ScrollView>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ gradient: {
+ height: SCREEN_HEIGHT,
+ width: SCREEN_WIDTH,
+ position: 'absolute',
+ },
+});
+
+export default Content;
diff --git a/src/components/profile/Cover.tsx b/src/components/profile/Cover.tsx
new file mode 100644
index 00000000..01199f06
--- /dev/null
+++ b/src/components/profile/Cover.tsx
@@ -0,0 +1,41 @@
+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';
+
+const {interpolate, Extrapolate} = Animated;
+interface CoverProps {
+ y: Animated.Value<number>;
+}
+const Cover: React.FC<CoverProps> = ({y}) => {
+ const {cover} = React.useContext(AuthContext);
+ const scale: Animated.Node<number> = interpolate(y, {
+ inputRange: [-COVER_HEIGHT, 0],
+ outputRange: [1.5, 1.25],
+ extrapolateRight: Extrapolate.CLAMP,
+ });
+ return (
+ <Animated.View style={[styles.container, {transform: [{scale}]}]}>
+ <Image
+ style={styles.image}
+ source={
+ cover
+ ? {uri: cover}
+ : require('../../assets/images/cover-placeholder.png')
+ }
+ />
+ </Animated.View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ position: 'absolute',
+ },
+ image: {
+ width: IMAGE_WIDTH,
+ height: COVER_HEIGHT,
+ },
+});
+export default Cover;
diff --git a/src/components/profile/Feed.tsx b/src/components/profile/Feed.tsx
new file mode 100644
index 00000000..6780f8c5
--- /dev/null
+++ b/src/components/profile/Feed.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import {PostType, UserType} from '../../types';
+import {Post} from '../common';
+
+interface FeedProps {
+ user: UserType;
+}
+const Feed: React.FC<FeedProps> = ({user}) => {
+ const posts: Array<PostType> = [];
+ const dummyPost: PostType = {
+ owner: user,
+ };
+ for (let i = 0; i < 20; i++) {
+ posts.push(dummyPost);
+ }
+ return (
+ <>
+ {posts.map((post, index) => (
+ <Post key={index} post={post} />
+ ))}
+ </>
+ );
+};
+
+export default Feed;
diff --git a/src/components/profile/FollowCount.tsx b/src/components/profile/FollowCount.tsx
new file mode 100644
index 00000000..72817e7a
--- /dev/null
+++ b/src/components/profile/FollowCount.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import {View, Text, StyleSheet, ViewProps} from 'react-native';
+
+interface FollowCountProps extends ViewProps {
+ mode: 'followers' | 'following';
+ count: number;
+}
+
+const FollowCount: React.FC<FollowCountProps> = ({style, mode, count}) => {
+ const displayed: string =
+ count < 5e3
+ ? `${count}`
+ : count < 1e5
+ ? `${(count / 1e3).toFixed(1)}k`
+ : count < 1e6
+ ? `${(count / 1e3).toFixed(0)}k`
+ : `${count / 1e6}m`;
+ return (
+ <View style={[styles.container, style]}>
+ <Text style={styles.count}>{displayed}</Text>
+ <Text style={styles.label}>
+ {mode === 'followers' ? 'Followers' : 'Following'}
+ </Text>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ },
+ count: {
+ fontWeight: '700',
+ fontSize: 18,
+ },
+ label: {
+ fontWeight: '400',
+ fontSize: 16,
+ },
+});
+
+export default FollowCount;
diff --git a/src/components/profile/Moment.tsx b/src/components/profile/Moment.tsx
new file mode 100644
index 00000000..eaf43fea
--- /dev/null
+++ b/src/components/profile/Moment.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import {View, StyleSheet, ViewProps} from 'react-native';
+import LinearGradient from 'react-native-linear-gradient';
+
+interface MomentProps extends ViewProps {}
+const Moment: React.FC<MomentProps> = ({style}) => {
+ return (
+ <LinearGradient
+ colors={['#9F00FF', '#27EAE9']}
+ useAngle={true}
+ angle={154.72}
+ angleCenter={{x: 0.5, y: 0.5}}
+ style={[styles.gradient, style]}>
+ <View style={styles.image} />
+ </LinearGradient>
+ );
+};
+
+const styles = StyleSheet.create({
+ gradient: {
+ width: 80,
+ height: 80,
+ borderRadius: 40,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ image: {
+ width: 72,
+ height: 72,
+ borderRadius: 37.5,
+ backgroundColor: 'pink',
+ },
+});
+
+export default Moment;
diff --git a/src/components/profile/MomentsBar.tsx b/src/components/profile/MomentsBar.tsx
new file mode 100644
index 00000000..dcc88d89
--- /dev/null
+++ b/src/components/profile/MomentsBar.tsx
@@ -0,0 +1,75 @@
+// @refresh react
+import React from 'react';
+import {StyleSheet} from 'react-native';
+import Animated from 'react-native-reanimated';
+import Moment from './Moment';
+import {PROFILE_CUTOUT_BOTTOM_Y} from '../../constants';
+import {StatusBarHeight} from '../../utils';
+
+const {View, ScrollView, interpolate, Extrapolate} = Animated;
+interface MomentsBarProps {
+ y: Animated.Value<number>;
+ profileBodyHeight: number;
+}
+const MomentsBar: React.FC<MomentsBarProps> = ({y, profileBodyHeight}) => {
+ const moments: Array<JSX.Element> = [];
+ for (let i = 0; i < 10; i++) {
+ moments.push(<Moment key={i} style={styles.moment} />);
+ }
+ const shadowOpacity: Animated.Node<number> = interpolate(y, {
+ inputRange: [
+ PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight,
+ PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight + 20,
+ ],
+ outputRange: [0, 0.2],
+ extrapolate: Extrapolate.CLAMP,
+ });
+ const paddingTop: Animated.Node<number> = interpolate(y, {
+ inputRange: [
+ 0,
+ PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight - 30,
+ PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight,
+ ],
+ outputRange: [20, 20, StatusBarHeight],
+ extrapolate: Extrapolate.CLAMP,
+ });
+ const paddingBottom: Animated.Node<number> = interpolate(y, {
+ inputRange: [
+ 0,
+ PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight - 30,
+ PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight,
+ ],
+ outputRange: [30, 30, 15],
+ extrapolate: Extrapolate.CLAMP,
+ });
+ return (
+ <View style={[styles.container, {shadowOpacity}]}>
+ <ScrollView
+ horizontal
+ showsHorizontalScrollIndicator={false}
+ style={{paddingTop, paddingBottom}}
+ contentContainerStyle={styles.contentContainer}>
+ {moments}
+ </ScrollView>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: 'white',
+ shadowColor: '#000',
+ shadowRadius: 10,
+ shadowOffset: {width: 0, height: 2},
+ zIndex: 1,
+ },
+ contentContainer: {
+ alignItems: 'center',
+ paddingHorizontal: 15,
+ },
+ moment: {
+ marginHorizontal: 14,
+ },
+});
+
+export default MomentsBar;
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
new file mode 100644
index 00000000..e8d8de62
--- /dev/null
+++ b/src/components/profile/ProfileBody.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import {StyleSheet, View, Text, LayoutChangeEvent} from 'react-native';
+import {AuthContext} from '../../routes/authentication';
+
+interface ProfileBodyProps {
+ onLayout: (event: LayoutChangeEvent) => void;
+}
+const ProfileBody: React.FC<ProfileBodyProps> = ({onLayout}) => {
+ const {
+ profile,
+ user: {username},
+ } = React.useContext(AuthContext);
+ const {biography, website} = profile;
+ return (
+ <View onLayout={onLayout} style={styles.container}>
+ <Text style={styles.username}>{`@${username}`}</Text>
+ <Text style={styles.biography}>{`${biography}`}</Text>
+ <Text style={styles.website}>{`${website}`}</Text>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ paddingVertical: 5,
+ paddingHorizontal: 20,
+ backgroundColor: 'white',
+ },
+ username: {
+ fontWeight: '600',
+ fontSize: 16,
+ marginBottom: 5,
+ },
+ biography: {
+ fontSize: 16,
+ lineHeight: 22,
+ marginBottom: 5,
+ },
+ website: {
+ fontSize: 16,
+ color: '#4E699C',
+ marginBottom: 5,
+ },
+});
+
+export default ProfileBody;
diff --git a/src/components/profile/ProfileCutout.tsx b/src/components/profile/ProfileCutout.tsx
new file mode 100644
index 00000000..c5deb06d
--- /dev/null
+++ b/src/components/profile/ProfileCutout.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import Svg, {Polygon} from 'react-native-svg';
+import {
+ PROFILE_CUTOUT_CORNER_Y,
+ PROFILE_CUTOUT_CORNER_X,
+ PROFILE_CUTOUT_TOP_Y,
+ PROFILE_CUTOUT_BOTTOM_Y,
+} from '../../constants';
+import {SCREEN_WIDTH} from '../../utils';
+
+const ProfileCutout: React.FC = ({children}) => {
+ return (
+ <Svg width={SCREEN_WIDTH} height={PROFILE_CUTOUT_BOTTOM_Y}>
+ <Polygon
+ points={`0,${PROFILE_CUTOUT_CORNER_Y} ${PROFILE_CUTOUT_CORNER_X},${PROFILE_CUTOUT_TOP_Y} ${SCREEN_WIDTH},${PROFILE_CUTOUT_TOP_Y} ${SCREEN_WIDTH},${PROFILE_CUTOUT_BOTTOM_Y}, 0,${PROFILE_CUTOUT_BOTTOM_Y}`}
+ fill={'white'}
+ />
+ {children}
+ </Svg>
+ );
+};
+
+export default ProfileCutout;
diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx
new file mode 100644
index 00000000..ec382357
--- /dev/null
+++ b/src/components/profile/ProfileHeader.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+
+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';
+
+const ProfileHeader: React.FC = () => {
+ const {
+ profile: {name},
+ } = React.useContext(AuthContext);
+ return (
+ <View style={styles.container}>
+ <View style={styles.row}>
+ <Avatar style={styles.avatar} />
+ <View style={styles.header}>
+ <Text style={styles.name}>{name}</Text>
+ <View style={styles.row}>
+ <FollowCount
+ style={styles.follows}
+ mode="followers"
+ count={318412}
+ />
+ <FollowCount style={styles.follows} mode="following" count={1036} />
+ </View>
+ </View>
+ </View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ top: SCREEN_HEIGHT / 2.4,
+ paddingHorizontal: SCREEN_WIDTH / 20,
+ marginBottom: SCREEN_HEIGHT / 10,
+ },
+ row: {
+ flexDirection: 'row',
+ },
+ header: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginTop: SCREEN_HEIGHT / 40,
+ marginLeft: SCREEN_WIDTH / 10,
+ marginBottom: SCREEN_HEIGHT / 50,
+ },
+ avatar: {
+ bottom: SCREEN_HEIGHT / 80,
+ },
+ name: {
+ fontSize: 20,
+ fontWeight: '700',
+ marginBottom: SCREEN_HEIGHT / 80,
+ },
+ follows: {
+ marginHorizontal: SCREEN_HEIGHT / 50,
+ },
+});
+
+export default ProfileHeader;
diff --git a/src/components/profile/index.ts b/src/components/profile/index.ts
new file mode 100644
index 00000000..2052ee5b
--- /dev/null
+++ b/src/components/profile/index.ts
@@ -0,0 +1,7 @@
+export {default as Cover} from './Cover';
+export {default as Content} from './Content';
+export {default as ProfileCutout} from './ProfileCutout';
+export {default as MomentsBar} from './MomentsBar';
+export {default as ProfileBody} from './ProfileBody';
+export {default as ProfileHeader} from './ProfileHeader';
+export {default as Feed} from './Feed';
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 657adf03..fd654c53 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -1,7 +1,10 @@
-export const API_ENDPOINT: string = 'http://127.0.0.1:8000/api/';
-export const LOGIN_ENDPOINT: string = 'http://127.0.0.1:8000/api/login/';
-export const LOGOUT_ENDPOINT: string = 'http://127.0.0.1:8000/api/logout/';
-export const REGISTER_ENDPOINT: string = 'http://127.0.0.1:8000/api/register/';
-export const SEND_OTP_ENDPOINT: string = 'http://127.0.0.1:8000/api/send-otp/';
-export const VERIFY_OTP_ENDPOINT: string =
- 'http://127.0.0.1:8000/api/verify-otp/';
+const BASE_URL: string = 'http://127.0.0.1:8000/';
+const API_URL: string = BASE_URL + 'api/';
+export const LOGIN_ENDPOINT: string = API_URL + 'login/';
+export const LOGOUT_ENDPOINT: string = API_URL + 'logout/';
+export const REGISTER_ENDPOINT: string = API_URL + 'register/';
+export const SEND_OTP_ENDPOINT: string = API_URL + 'send-otp/';
+export const VERIFY_OTP_ENDPOINT: string = API_URL + 'verify-otp/';
+export const PROFILE_INFO_ENDPOINT: string = API_URL + 'user-profile-info/';
+export const COVER_PHOTO_ENDPOINT: string = API_URL + 'large-profile-pic/';
+export const AVATAR_PHOTO_ENDPOINT: string = API_URL + 'small-profile-pic/';
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
new file mode 100644
index 00000000..f79c2c5b
--- /dev/null
+++ b/src/constants/constants.ts
@@ -0,0 +1,10 @@
+import {SCREEN_WIDTH, SCREEN_HEIGHT} from '../utils';
+
+export const PROFILE_CUTOUT_TOP_Y = SCREEN_HEIGHT / 2.3;
+export const PROFILE_CUTOUT_BOTTOM_Y = SCREEN_HEIGHT / 1.8;
+export const PROFILE_CUTOUT_CORNER_X = SCREEN_WIDTH / 2.9;
+export const PROFILE_CUTOUT_CORNER_Y = SCREEN_HEIGHT / 1.95;
+
+export const IMAGE_WIDTH = SCREEN_WIDTH;
+export const IMAGE_HEIGHT = SCREEN_WIDTH;
+export const COVER_HEIGHT = SCREEN_WIDTH * (7 / 5);
diff --git a/src/constants/index.ts b/src/constants/index.ts
index deb89e57..7fb47dc6 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -1,3 +1,4 @@
export * from './api';
+export * from './constants';
export * from './regex';
export * from './termsConditions';
diff --git a/src/routes/NavigationBar.tsx b/src/routes/NavigationBar.tsx
deleted file mode 100644
index 84c18e00..00000000
--- a/src/routes/NavigationBar.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React from 'react';
-import {ViewProps} from 'react-native';
-import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
-import {Fragment} from 'react';
-import {NavigationIcon} from '../components';
-import {Home, Notifications, Profile, Search, Upload} from '../screens/main';
-
-interface NavigationBarProps extends ViewProps {
- centered?: boolean;
-}
-
-const Tab = createBottomTabNavigator();
-
-const NavigationBar: React.FC<NavigationBarProps> = () => {
- return (
- <Fragment>
- <Tab.Navigator
- screenOptions={({route}) => ({
- tabBarIcon: ({focused}) => {
- if (route.name === 'Home') {
- return focused ? (
- <NavigationIcon tab="Home" disabled={false} />
- ) : (
- <NavigationIcon tab="Home" disabled={true} />
- );
- } else if (route.name === 'Search') {
- return focused ? (
- <NavigationIcon tab="Search" disabled={false} />
- ) : (
- <NavigationIcon tab="Search" disabled={true} />
- );
- } else if (route.name === 'Upload') {
- return focused ? (
- <NavigationIcon tab="Upload" disabled={false} />
- ) : (
- <NavigationIcon tab="Upload" disabled={true} />
- );
- } else if (route.name === 'Notifications') {
- return focused ? (
- <NavigationIcon tab="Notifications" disabled={false} />
- ) : (
- <NavigationIcon tab="Notifications" disabled={true} />
- );
- } else if (route.name === 'Profile') {
- return focused ? (
- <NavigationIcon tab="Profile" disabled={false} />
- ) : (
- <NavigationIcon tab="Profile" disabled={true} />
- );
- }
- },
- })}
- initialRouteName="Home"
- tabBarOptions={{
- showLabel: false,
- style: {
- backgroundColor: 'transparent',
- position: 'absolute',
- borderTopWidth: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
- }}>
- <Tab.Screen name="Home" component={Home} />
- <Tab.Screen name="Search" component={Search} />
- <Tab.Screen name="Upload" component={Upload} />
- <Tab.Screen name="Notifications" component={Notifications} />
- <Tab.Screen name="Profile" component={Profile} />
- </Tab.Navigator>
- </Fragment>
- );
-};
-
-export default NavigationBar;
diff --git a/src/routes/OnboardingStack.tsx b/src/routes/OnboardingStack.tsx
deleted file mode 100644
index 5e91fe9f..00000000
--- a/src/routes/OnboardingStack.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react';
-import {createStackNavigator} from '@react-navigation/stack';
-import {
- Login,
- RegistrationOne,
- RegistrationTwo,
- Verification,
- ProfileOnboarding,
-} from '../screens/onboarding';
-
-export type RootStackParamList = {
- Login: undefined;
- RegistrationOne: undefined;
- RegistrationTwo:
- | {firstName: string; lastName: string; email: string}
- | undefined;
- Verification: {username: string; email: string; userId: string};
- ProfileOnboarding: {username: string; userId: string};
-};
-
-const RootStack = createStackNavigator<RootStackParamList>();
-
-interface OnboardingStackProps {}
-
-const OnboardingStack: React.FC<OnboardingStackProps> = ({}) => {
- return (
- <RootStack.Navigator initialRouteName="Login">
- <RootStack.Screen
- name="Login"
- component={Login}
- options={{headerShown: false}}
- />
- <RootStack.Screen
- name="RegistrationOne"
- component={RegistrationOne}
- options={{headerShown: false}}
- />
- <RootStack.Screen
- name="RegistrationTwo"
- component={RegistrationTwo}
- options={{headerShown: false}}
- />
- <RootStack.Screen
- name="Verification"
- component={Verification}
- options={{headerShown: false}}
- />
- <RootStack.Screen
- name="ProfileOnboarding"
- component={ProfileOnboarding}
- options={{headerShown: false}}
- />
- </RootStack.Navigator>
- );
-};
-
-export default OnboardingStack;
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
index 43a51b90..92cd3dd2 100644
--- a/src/routes/Routes.tsx
+++ b/src/routes/Routes.tsx
@@ -1,42 +1,14 @@
import React from 'react';
-import {OnboardingStack, NavigationBar} from './';
-interface RoutesProps {}
-interface AuthProviderProps {}
+import {AuthContext} from './authentication';
+import NavigationBar from './tabs';
+import Onboarding from './onboarding';
-export const AuthContext = React.createContext<{
- user: boolean;
- login: () => void;
- logout: () => void;
-}>({
- user: false,
- login: () => {},
- logout: () => {},
-});
-
-export const AuthContextProvider: React.FC<AuthProviderProps> = ({
- children,
-}) => {
- const [loggedIn, setLoggedIn] = React.useState(false); // renders onboarding stack
- return (
- <AuthContext.Provider
- value={{
- user: loggedIn,
- login: () => {
- setLoggedIn(true);
- },
- logout: () => {
- setLoggedIn(false);
- },
- }}>
- {children}
- </AuthContext.Provider>
- );
-};
-
-const Routes: React.FC<RoutesProps> = ({}) => {
- const {user} = React.useContext(AuthContext);
- return user ? <NavigationBar /> : <OnboardingStack />;
+const Routes: React.FC = () => {
+ const {
+ user: {userId},
+ } = React.useContext(AuthContext);
+ return userId ? <NavigationBar /> : <Onboarding />;
};
export default Routes;
diff --git a/src/routes/authentication/AuthProvider.tsx b/src/routes/authentication/AuthProvider.tsx
new file mode 100644
index 00000000..bd5706f3
--- /dev/null
+++ b/src/routes/authentication/AuthProvider.tsx
@@ -0,0 +1,122 @@
+import React, {useEffect} from 'react';
+import {createContext, useState} from 'react';
+import RNFetchBlob from 'rn-fetch-blob';
+import {UserType, ProfileType} from '../../types';
+import {
+ PROFILE_INFO_ENDPOINT,
+ AVATAR_PHOTO_ENDPOINT,
+ COVER_PHOTO_ENDPOINT,
+} from '../../constants';
+
+interface AuthContextProps {
+ user: UserType;
+ profile: ProfileType;
+ login: (userId: string, username: string) => void;
+ logout: () => void;
+ avatar: string | null;
+ cover: string | null;
+}
+const NO_USER: UserType = {
+ userId: '',
+ username: '',
+};
+const NO_PROFILE: ProfileType = {
+ biography: '',
+ website: '',
+ name: '',
+};
+export const AuthContext = createContext<AuthContextProps>({
+ user: NO_USER,
+ profile: NO_PROFILE,
+ login: () => {},
+ logout: () => {},
+ avatar: null,
+ cover: null,
+});
+
+/**
+ * Authentication provider for the application.
+ */
+const AuthProvider: 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 {userId} = user;
+ useEffect(() => {
+ if (!userId) {
+ return;
+ }
+ const loadProfileInfo = async () => {
+ try {
+ const response = await fetch(PROFILE_INFO_ENDPOINT + `${userId}/`, {
+ method: 'GET',
+ });
+ const status = response.status;
+ if (status === 200) {
+ const info = await response.json();
+ let {name, biography, website} = info;
+ setProfile({name, biography, website});
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ const loadAvatar = async () => {
+ try {
+ const response = await RNFetchBlob.config({
+ fileCache: true,
+ appendExt: 'jpg',
+ }).fetch('GET', AVATAR_PHOTO_ENDPOINT + `${userId}/`);
+ const status = response.info().status;
+ if (status === 200) {
+ setAvatar(response.path());
+ } else {
+ setAvatar('');
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ const loadCover = async () => {
+ try {
+ let response = await RNFetchBlob.config({
+ fileCache: true,
+ appendExt: 'jpg',
+ }).fetch('GET', COVER_PHOTO_ENDPOINT + `${userId}/`);
+ const status = response.info().status;
+ if (status === 200) {
+ setCover(response.path());
+ } else {
+ setCover('');
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ loadProfileInfo();
+ loadAvatar();
+ loadCover();
+ }, [userId]);
+
+ return (
+ <AuthContext.Provider
+ value={{
+ user,
+ profile,
+ avatar,
+ cover,
+ login: (id, username) => {
+ setUser({...user, userId: id, username});
+ },
+ logout: () => {
+ setUser(NO_USER);
+ },
+ }}>
+ {children}
+ </AuthContext.Provider>
+ );
+};
+
+export default AuthProvider;
diff --git a/src/routes/authentication/index.ts b/src/routes/authentication/index.ts
new file mode 100644
index 00000000..9968ae93
--- /dev/null
+++ b/src/routes/authentication/index.ts
@@ -0,0 +1,2 @@
+export * from './AuthProvider';
+export {default} from './AuthProvider';
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 054a25c3..69697b20 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -1,6 +1,4 @@
-export {default as OnboardingStack} from './OnboardingStack';
-export * from './OnboardingStack';
-export {default as NavigationBar} from './NavigationBar';
-export * from './NavigationBar';
-export {default as Routes} from './Routes';
-export * from './Routes';
+export {default as AuthProvider} from './authentication';
+export * from './authentication';
+export * from './onboarding';
+export {default} from './Routes';
diff --git a/src/routes/onboarding/Onboarding.tsx b/src/routes/onboarding/Onboarding.tsx
new file mode 100644
index 00000000..d2bfbfd6
--- /dev/null
+++ b/src/routes/onboarding/Onboarding.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import {OnboardingStack} from './OnboardingStack';
+import {
+ Login,
+ RegistrationOne,
+ RegistrationTwo,
+ Verification,
+ ProfileOnboarding,
+} from '../../screens';
+
+const Onboarding: React.FC = () => {
+ return (
+ <OnboardingStack.Navigator initialRouteName="Login">
+ <OnboardingStack.Screen
+ name="Login"
+ component={Login}
+ options={{headerShown: false}}
+ />
+ <OnboardingStack.Screen
+ name="RegistrationOne"
+ component={RegistrationOne}
+ options={{headerShown: false}}
+ />
+ <OnboardingStack.Screen
+ name="RegistrationTwo"
+ component={RegistrationTwo}
+ options={{headerShown: false}}
+ />
+ <OnboardingStack.Screen
+ name="Verification"
+ component={Verification}
+ options={{headerShown: false}}
+ />
+ <OnboardingStack.Screen
+ name="ProfileOnboarding"
+ component={ProfileOnboarding}
+ options={{headerShown: false}}
+ />
+ </OnboardingStack.Navigator>
+ );
+};
+
+export default Onboarding;
diff --git a/src/routes/onboarding/OnboardingStack.tsx b/src/routes/onboarding/OnboardingStack.tsx
new file mode 100644
index 00000000..f9722d46
--- /dev/null
+++ b/src/routes/onboarding/OnboardingStack.tsx
@@ -0,0 +1,13 @@
+import {createStackNavigator} from '@react-navigation/stack';
+
+export type OnboardingStackParams = {
+ Login: undefined;
+ RegistrationOne: undefined;
+ RegistrationTwo:
+ | {firstName: string; lastName: string; email: string}
+ | undefined;
+ Verification: {username: string; email: string; userId: string};
+ ProfileOnboarding: {username: string; userId: string};
+};
+
+export const OnboardingStack = createStackNavigator<OnboardingStackParams>();
diff --git a/src/routes/onboarding/index.ts b/src/routes/onboarding/index.ts
new file mode 100644
index 00000000..66b0f3f4
--- /dev/null
+++ b/src/routes/onboarding/index.ts
@@ -0,0 +1,2 @@
+export * from './OnboardingStack';
+export {default} from './Onboarding';
diff --git a/src/routes/tabs/NavigationBar.tsx b/src/routes/tabs/NavigationBar.tsx
new file mode 100644
index 00000000..aca968c2
--- /dev/null
+++ b/src/routes/tabs/NavigationBar.tsx
@@ -0,0 +1,73 @@
+import React from 'react';
+import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
+import {NavigationIcon} from '../../components';
+import {
+ ProfileScreen,
+ Home,
+ Notifications,
+ Search,
+ Upload,
+} from '../../screens';
+
+const Tabs = createBottomTabNavigator();
+
+const NavigationBar: React.FC = () => {
+ return (
+ <Tabs.Navigator
+ screenOptions={({route}) => ({
+ tabBarIcon: ({focused}) => {
+ if (route.name === 'Home') {
+ return focused ? (
+ <NavigationIcon tab="Home" disabled={false} />
+ ) : (
+ <NavigationIcon tab="Home" disabled={true} />
+ );
+ } else if (route.name === 'Search') {
+ return focused ? (
+ <NavigationIcon tab="Search" disabled={false} />
+ ) : (
+ <NavigationIcon tab="Search" disabled={true} />
+ );
+ } else if (route.name === 'Upload') {
+ return focused ? (
+ <NavigationIcon tab="Upload" disabled={false} />
+ ) : (
+ <NavigationIcon tab="Upload" disabled={true} />
+ );
+ } else if (route.name === 'Notifications') {
+ return focused ? (
+ <NavigationIcon tab="Notifications" disabled={false} />
+ ) : (
+ <NavigationIcon tab="Notifications" disabled={true} />
+ );
+ } else if (route.name === 'Profile') {
+ return focused ? (
+ <NavigationIcon tab="Profile" disabled={false} />
+ ) : (
+ <NavigationIcon tab="Profile" disabled={true} />
+ );
+ }
+ },
+ })}
+ initialRouteName="Profile"
+ tabBarOptions={{
+ showLabel: false,
+ style: {
+ backgroundColor: 'transparent',
+ position: 'absolute',
+ borderTopWidth: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ },
+ }}>
+ <Tabs.Screen name="Home" component={Home} />
+ <Tabs.Screen name="Search" component={Search} />
+ <Tabs.Screen name="Upload" component={Upload} />
+ <Tabs.Screen name="Notifications" component={Notifications} />
+ <Tabs.Screen name="Profile" component={ProfileScreen} />
+ </Tabs.Navigator>
+ );
+};
+
+export default NavigationBar;
diff --git a/src/routes/tabs/index.ts b/src/routes/tabs/index.ts
new file mode 100644
index 00000000..8ea77e8f
--- /dev/null
+++ b/src/routes/tabs/index.ts
@@ -0,0 +1 @@
+export {default} from './NavigationBar';
diff --git a/src/screens/index.ts b/src/screens/index.ts
new file mode 100644
index 00000000..5dd3007a
--- /dev/null
+++ b/src/screens/index.ts
@@ -0,0 +1,3 @@
+export * from './main';
+export * from './onboarding';
+export * from './profile';
diff --git a/src/screens/main/Profile.tsx b/src/screens/main/Profile.tsx
deleted file mode 100644
index 3a6536e4..00000000
--- a/src/screens/main/Profile.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import {Text} from 'react-native-animatable';
-import {StyleSheet} from 'react-native';
-import {GradientBackground} from '../../components';
-
-/**
- * Profile Screen for a user's logged in profile
- * including posts, messaging, and settings
- */
-
-const Profile: React.FC = () => {
- return (
- <GradientBackground>
- <Text style={styles.text}> Profile Screen 🤩 </Text>
- </GradientBackground>
- );
-};
-const styles = StyleSheet.create({
- text: {
- justifyContent: 'center',
- backgroundColor: 'transparent',
- },
-});
-export default Profile;
diff --git a/src/screens/main/index.ts b/src/screens/main/index.ts
index 9bd00c57..fb1bf49b 100644
--- a/src/screens/main/index.ts
+++ b/src/screens/main/index.ts
@@ -1,5 +1,4 @@
export {default as Home} from './Home';
export {default as Notifications} from './Notifications';
-export {default as Profile} from './Profile';
export {default as Search} from './Search';
export {default as Upload} from './Upload';
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index 7b76e97c..5c569ec3 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -13,13 +13,14 @@ import {
Platform,
} from 'react-native';
-import {RootStackParamList, AuthContext} from '../../routes';
+import {OnboardingStackParams} from '../../routes/onboarding';
+import {AuthContext} from '../../routes/authentication';
import {Background, TaggInput, SubmitButton} from '../../components';
import {usernameRegex, LOGIN_ENDPOINT} from '../../constants';
-type VerificationScreenRouteProp = RouteProp<RootStackParamList, 'Login'>;
+type VerificationScreenRouteProp = RouteProp<OnboardingStackParams, 'Login'>;
type VerificationScreenNavigationProp = StackNavigationProp<
- RootStackParamList,
+ OnboardingStackParams,
'Login'
>;
interface LoginProps {
@@ -98,9 +99,9 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
};
/**
- * Handler for the Let's Start button or the Go button on the keyboard.
- Makes a POST request to the Django login API and presents Alerts based on the status codes that the backend returns.
- */
+ * Handler for the Let's Start button or the Go button on the keyboard.
+ Makes a POST request to the Django login API and presents Alerts based on the status codes that the backend returns.
+ */
const handleLogin = async () => {
if (!form.attemptedSubmit) {
setForm({
@@ -110,17 +111,19 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
}
try {
if (form.isValidUser && form.isValidPassword) {
+ const {username, password} = form;
let response = await fetch(LOGIN_ENDPOINT, {
method: 'POST',
body: JSON.stringify({
- username: form.username,
- password: form.password,
+ username,
+ password,
}),
});
let statusCode = response.status;
+ let data = await response.json();
if (statusCode === 200) {
- login();
+ login(data.UserID, username);
} else if (statusCode === 401) {
Alert.alert(
'Login failed 😔',
diff --git a/src/screens/onboarding/ProfileOnboarding.tsx b/src/screens/onboarding/ProfileOnboarding.tsx
index 6ce1ff80..9405ca52 100644
--- a/src/screens/onboarding/ProfileOnboarding.tsx
+++ b/src/screens/onboarding/ProfileOnboarding.tsx
@@ -10,17 +10,18 @@ import {
Alert,
View,
} from 'react-native';
-import {RootStackParamList, AuthContext} from '../../routes';
+import {OnboardingStackParams} from '../../routes/onboarding';
+import {AuthContext} from '../../routes/authentication';
import {Background} from '../../components';
import ImagePicker from 'react-native-image-crop-picker';
import {REGISTER_ENDPOINT} from '../../constants';
type ProfileOnboardingScreenRouteProp = RouteProp<
- RootStackParamList,
+ OnboardingStackParams,
'ProfileOnboarding'
>;
type ProfileOnboardingScreenNavigationProp = StackNavigationProp<
- RootStackParamList,
+ OnboardingStackParams,
'ProfileOnboarding'
>;
interface ProfileOnboardingProps {
@@ -150,10 +151,10 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({route}) => {
},
body: form,
});
- let data = await response.json();
let statusCode = response.status;
+ let data = await response.json();
if (statusCode === 200) {
- login();
+ login(userId, username);
} else if (statusCode === 400) {
Alert.alert('Profile update failed. 😔', `${data}`);
} else {
diff --git a/src/screens/onboarding/RegistrationOne.tsx b/src/screens/onboarding/RegistrationOne.tsx
index 3b9ddb3e..720fcaed 100644
--- a/src/screens/onboarding/RegistrationOne.tsx
+++ b/src/screens/onboarding/RegistrationOne.tsx
@@ -12,7 +12,7 @@ import {
KeyboardAvoidingView,
} from 'react-native';
-import {RootStackParamList} from '../../routes';
+import {OnboardingStackParams} from '../../routes';
import {
ArrowButton,
RegistrationWizard,
@@ -22,11 +22,11 @@ import {
import {nameRegex, emailRegex} from '../../constants';
type RegistrationScreenOneRouteProp = RouteProp<
- RootStackParamList,
+ OnboardingStackParams,
'RegistrationOne'
>;
type RegistrationScreenOneNavigationProp = StackNavigationProp<
- RootStackParamList,
+ OnboardingStackParams,
'RegistrationOne'
>;
interface RegistrationOneProps {
diff --git a/src/screens/onboarding/RegistrationTwo.tsx b/src/screens/onboarding/RegistrationTwo.tsx
index 09e217f6..b67c2403 100644
--- a/src/screens/onboarding/RegistrationTwo.tsx
+++ b/src/screens/onboarding/RegistrationTwo.tsx
@@ -14,7 +14,7 @@ import {
} from 'react-native';
import {usePromiseTracker, trackPromise} from 'react-promise-tracker';
-import {RootStackParamList} from '../../routes';
+import {OnboardingStackParams} from '../../routes';
import {
ArrowButton,
RegistrationWizard,
@@ -30,11 +30,11 @@ import {
} from '../../constants';
type RegistrationScreenTwoRouteProp = RouteProp<
- RootStackParamList,
+ OnboardingStackParams,
'RegistrationTwo'
>;
type RegistrationScreenTwoNavigationProp = StackNavigationProp<
- RootStackParamList,
+ OnboardingStackParams,
'RegistrationTwo'
>;
interface RegistrationTwoProps {
diff --git a/src/screens/onboarding/Verification.tsx b/src/screens/onboarding/Verification.tsx
index 197bc0ca..0676bb3a 100644
--- a/src/screens/onboarding/Verification.tsx
+++ b/src/screens/onboarding/Verification.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import {RootStackParamList} from '../../routes';
+import {OnboardingStackParams} from '../../routes';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import {Background, RegistrationWizard, SubmitButton} from '../../components';
@@ -23,11 +23,11 @@ import {
import {usePromiseTracker, trackPromise} from 'react-promise-tracker';
type VerificationScreenRouteProp = RouteProp<
- RootStackParamList,
+ OnboardingStackParams,
'Verification'
>;
type VerificationScreenNavigationProp = StackNavigationProp<
- RootStackParamList,
+ OnboardingStackParams,
'Verification'
>;
interface VerificationProps {
diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts
index 9b2f4cb0..7a9816e7 100644
--- a/src/screens/onboarding/index.ts
+++ b/src/screens/onboarding/index.ts
@@ -1,5 +1,5 @@
export {default as Login} from './Login';
+export {default as ProfileOnboarding} from './ProfileOnboarding';
export {default as RegistrationOne} from './RegistrationOne';
export {default as RegistrationTwo} from './RegistrationTwo';
export {default as Verification} from './Verification';
-export {default as ProfileOnboarding} from './ProfileOnboarding';
diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx
new file mode 100644
index 00000000..3d1ef2a8
--- /dev/null
+++ b/src/screens/profile/ProfileScreen.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import {Cover, Content} from '../../components';
+import Animated from 'react-native-reanimated';
+import {AuthContext} from '../../routes/authentication';
+import {StatusBar} from 'react-native';
+
+// destructure Value object from Animated
+const {Value} = Animated;
+
+/**
+ * Profile Screen for a user's logged in profile
+ * including posts, messaging, and settings
+ */
+const ProfileScreen: React.FC = () => {
+ const {user} = React.useContext(AuthContext);
+ const y = new Value(0);
+ return (
+ <>
+ <StatusBar />
+ <Cover {...{y, user}} />
+ <Content {...{y, user}} />
+ </>
+ );
+};
+
+export default ProfileScreen;
diff --git a/src/screens/profile/index.ts b/src/screens/profile/index.ts
new file mode 100644
index 00000000..0ade259d
--- /dev/null
+++ b/src/screens/profile/index.ts
@@ -0,0 +1 @@
+export {default as ProfileScreen} from './ProfileScreen';
diff --git a/src/types/index.ts b/src/types/index.ts
new file mode 100644
index 00000000..fcb073fe
--- /dev/null
+++ b/src/types/index.ts
@@ -0,0 +1 @@
+export * from './types';
diff --git a/src/types/types.ts b/src/types/types.ts
new file mode 100644
index 00000000..1a023932
--- /dev/null
+++ b/src/types/types.ts
@@ -0,0 +1,14 @@
+export interface UserType {
+ userId: string;
+ username: string;
+}
+
+export interface ProfileType {
+ name: string;
+ biography: string;
+ website: string;
+}
+
+export interface PostType {
+ owner: UserType;
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 00000000..5bc168e3
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,2 @@
+export * from './screenDimensions';
+export * from './statusBarHeight';
diff --git a/src/utils/screenDimensions.ts b/src/utils/screenDimensions.ts
new file mode 100644
index 00000000..56277ddc
--- /dev/null
+++ b/src/utils/screenDimensions.ts
@@ -0,0 +1,6 @@
+import {Dimensions} from 'react-native';
+
+const {width, height} = Dimensions.get('window');
+
+export const SCREEN_WIDTH = width;
+export const SCREEN_HEIGHT = height;
diff --git a/src/utils/statusBarHeight.ts b/src/utils/statusBarHeight.ts
new file mode 100644
index 00000000..4c68f9ee
--- /dev/null
+++ b/src/utils/statusBarHeight.ts
@@ -0,0 +1,19 @@
+import {Platform, StatusBar} from 'react-native';
+import {SCREEN_WIDTH, SCREEN_HEIGHT} from './screenDimensions';
+
+const X_WIDTH = 375;
+const X_HEIGHT = 812;
+const XSMAX_WIDTH = 414;
+const XSMAX_HEIGHT = 896;
+
+const isIPhoneX = () =>
+ Platform.OS === 'ios' && !Platform.isPad && !Platform.isTVOS
+ ? (SCREEN_WIDTH === X_WIDTH && SCREEN_HEIGHT === X_HEIGHT) ||
+ (SCREEN_WIDTH === XSMAX_WIDTH && SCREEN_HEIGHT === XSMAX_HEIGHT)
+ : false;
+
+export const StatusBarHeight = Platform.select({
+ ios: isIPhoneX() ? 44 : 20,
+ android: StatusBar.currentHeight,
+ default: 0,
+});
diff --git a/yarn.lock b/yarn.lock
index 178cff34..0c71817a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1621,6 +1621,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+base-64@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
+ integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=
+
base64-js@^1.1.2, base64-js@^1.2.3:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
@@ -1658,6 +1663,11 @@ bindings@^1.5.0:
dependencies:
file-uri-to-path "1.0.0"
+boolbase@^1.0.0, boolbase@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
bplist-creator@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.8.tgz#56b2a6e79e9aec3fc33bf831d09347d73794e79c"
@@ -2086,6 +2096,29 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
+css-select@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
+ integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^3.2.1"
+ domutils "^1.7.0"
+ nth-check "^1.0.2"
+
+css-tree@^1.0.0-alpha.39:
+ version "1.0.0-alpha.39"
+ resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
+ integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
+ dependencies:
+ mdn-data "2.0.6"
+ source-map "^0.6.1"
+
+css-what@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39"
+ integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==
+
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
@@ -2238,6 +2271,24 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-serializer@0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+ integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+ dependencies:
+ domelementtype "^2.0.1"
+ entities "^2.0.0"
+
+domelementtype@1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+ integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+ integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
domexception@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
@@ -2245,6 +2296,14 @@ domexception@^1.0.1:
dependencies:
webidl-conversions "^4.0.2"
+domutils@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+ integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
@@ -2287,6 +2346,11 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
+entities@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
+ integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
+
envinfo@^7.1.0:
version "7.5.1"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.1.tgz#93c26897225a00457c75e734d354ea9106a72236"
@@ -2966,6 +3030,18 @@ glob-parent@^5.0.0:
dependencies:
is-glob "^4.0.1"
+glob@7.0.6:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
+ integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@@ -4168,6 +4244,11 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
+mdn-data@2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
+ integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
+
merge-stream@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"
@@ -4464,7 +4545,7 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-minimatch@^3.0.4:
+minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -4621,6 +4702,13 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
+nth-check@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+ integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+ dependencies:
+ boolbase "~1.0.0"
+
nullthrows@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
@@ -5154,6 +5242,14 @@ react-native-screens@^2.9.0:
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.9.0.tgz#ead2843107ba00fee259aa377582e457c74f1f3b"
integrity sha512-5MaiUD6HA3nzY3JbVI8l3V7pKedtxQF3d8qktTVI0WmWXTI4QzqOU8r8fPVvfKo3MhOXwhWBjr+kQ7DZaIQQeg==
+react-native-svg@^12.1.0:
+ version "12.1.0"
+ resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-12.1.0.tgz#acfe48c35cd5fca3d5fd767abae0560c36cfc03d"
+ integrity sha512-1g9qBRci7man8QsHoXn6tP3DhCDiypGgc6+AOWq+Sy+PmP6yiyf8VmvKuoqrPam/tf5x+ZaBT2KI0gl7bptZ7w==
+ dependencies:
+ css-select "^2.1.0"
+ css-tree "^1.0.0-alpha.39"
+
react-native@0.62.2:
version "0.62.2"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.62.2.tgz#d831e11a3178705449142df19a70ac2ca16bad10"
@@ -5473,6 +5569,14 @@ rimraf@~2.2.6:
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=
+rn-fetch-blob@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz#ec610d2f9b3f1065556b58ab9c106eeb256f3cba"
+ integrity sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA==
+ dependencies:
+ base-64 "0.1.0"
+ glob "7.0.6"
+
rsvp@^4.8.4:
version "4.8.5"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"