diff options
Diffstat (limited to 'src')
39 files changed, 443 insertions, 383 deletions
diff --git a/src/assets/icons/back-arrow.svg b/src/assets/icons/back-arrow.svg new file mode 100644 index 00000000..aa203dea --- /dev/null +++ b/src/assets/icons/back-arrow.svg @@ -0,0 +1 @@ +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 385.86 696.76"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:77.17px;}</style></defs><polyline class="cls-1" points="347.28 38.58 38.58 351.69 347.28 658.17"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/up_arrow.svg b/src/assets/icons/up_arrow.svg new file mode 100644 index 00000000..fc92b551 --- /dev/null +++ b/src/assets/icons/up_arrow.svg @@ -0,0 +1 @@ +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 792 792"><defs></defs><path class="cls-1" d="M522.58,375.86l-18.19,16.86a13.06,13.06,0,0,1-18.46-.68l-62.6-67.45v219a22,22,0,0,1-22,22H393.1a22,22,0,0,1-22-22V322l-65,70.05a13.11,13.11,0,0,1-18.5.68l-18.19-16.86a13.12,13.12,0,0,1-.68-18.51l96.52-104,20.39-22,1.52-1.4a13.6,13.6,0,0,1,2.52-1.85,14.44,14.44,0,0,1,2.85-1.16,2.18,2.18,0,0,1,.88-.2,6.27,6.27,0,0,1,1.2-.2,11.3,11.3,0,0,1,1.16-.08h.44a11.3,11.3,0,0,1,1.16.08,6.58,6.58,0,0,1,1.28.2,4.48,4.48,0,0,1,.92.24c.48.12.93.32,1.41.48l1,.48a13.24,13.24,0,0,1,2.84,2l1.52,1.4,20.43,22,96.48,104A13.12,13.12,0,0,1,522.58,375.86Z" fill="currentColor"/></svg>
\ No newline at end of file diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index f8c0b6bc..24b3473c 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -1,17 +1,19 @@ -import * as React from 'react'; +import React, {useEffect} from 'react'; import { Image, + Keyboard, KeyboardAvoidingView, Platform, StyleSheet, View, } from 'react-native'; -import AsyncStorage from '@react-native-community/async-storage'; -import {TaggBigInput} from '../onboarding'; +import {TextInput, TouchableOpacity} from 'react-native-gesture-handler'; +import {useSelector} from 'react-redux'; +import UpArrowIcon from '../../assets/icons/up_arrow.svg'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {postMomentComment} from '../../services'; -import {logout} from '../../store/actions'; -import {useSelector, useDispatch} from 'react-redux'; import {RootState} from '../../store/rootreducer'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; /** * This file provides the add comment view for a user. @@ -29,87 +31,114 @@ const AddComment: React.FC<AddCommentProps> = ({ moment_id, }) => { const [comment, setComment] = React.useState(''); + const [keyboardVisible, setKeyboardVisible] = React.useState(false); - const dispatch = useDispatch(); const { avatar, user: {userId}, } = useSelector((state: RootState) => state.user); - const handleCommentUpdate = (comment: string) => { - setComment(comment); - }; - const postComment = async () => { - try { - const token = await AsyncStorage.getItem('token'); - if (!token) { - dispatch(logout()); - return; - } - const postedComment = await postMomentComment( - userId, - comment, - moment_id, - token, - ); + const postedComment = await postMomentComment( + userId, + comment.trim(), + moment_id, + ); - if (postedComment) { - //Set the current comment to en empty string if the comment was posted successfully. - handleCommentUpdate(''); - - //Indicate the MomentCommentsScreen that it needs to download the new comments again - setNewCommentsAvailable(true); - } - } catch (err) { - console.log('Error while posting comment!'); + if (postedComment) { + setComment(''); + setNewCommentsAvailable(true); } }; + useEffect(() => { + const showKeyboard = () => setKeyboardVisible(true); + Keyboard.addListener('keyboardWillShow', showKeyboard); + return () => Keyboard.removeListener('keyboardWillShow', showKeyboard); + }, []); + + useEffect(() => { + const hideKeyboard = () => setKeyboardVisible(false); + Keyboard.addListener('keyboardWillHide', hideKeyboard); + return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard); + }, []); + return ( <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} - keyboardVerticalOffset={130}> - <View style={styles.container}> - <Image - style={styles.avatar} - source={ - avatar - ? {uri: avatar} - : require('../../assets/images/avatar-placeholder.png') - } - /> - <TaggBigInput - style={styles.text} - multiline - placeholder="Add a comment....." - placeholderTextColor="gray" - onChangeText={handleCommentUpdate} - onSubmitEditing={postComment} - value={comment} - /> + keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}> + <View + style={[ + styles.container, + keyboardVisible ? {backgroundColor: '#fff'} : {}, + ]}> + <View style={styles.textContainer}> + <Image + style={styles.avatar} + source={ + avatar + ? {uri: avatar} + : require('../../assets/images/avatar-placeholder.png') + } + /> + <TextInput + style={styles.text} + placeholder="Add a comment..." + placeholderTextColor="grey" + onChangeText={setComment} + value={comment} + autoCorrect={false} + multiline={true} + /> + <View style={styles.submitButton}> + <TouchableOpacity style={styles.submitButton} onPress={postComment}> + <UpArrowIcon width={35} height={35} color={'white'} /> + </TouchableOpacity> + </View> + </View> </View> </KeyboardAvoidingView> ); }; const styles = StyleSheet.create({ - container: {flexDirection: 'row'}, + container: { + backgroundColor: '#f7f7f7', + alignItems: 'center', + width: SCREEN_WIDTH, + }, + textContainer: { + width: '95%', + flexDirection: 'row', + backgroundColor: '#e8e8e8', + alignItems: 'center', + justifyContent: 'space-between', + margin: '3%', + borderRadius: 25, + }, text: { - position: 'relative', - right: '18%', - backgroundColor: 'white', - width: '70%', - paddingLeft: '2%', - paddingRight: '2%', - paddingBottom: '1%', - paddingTop: '1%', - height: 60, + flex: 1, + padding: '1%', + marginHorizontal: '1%', }, avatar: { - height: 40, - width: 40, + height: 35, + width: 35, borderRadius: 30, - marginRight: 15, + marginRight: 10, + marginLeft: '3%', + marginVertical: '2%', + alignSelf: 'flex-end', + }, + submitButton: { + height: 35, + width: 35, + backgroundColor: TAGG_LIGHT_BLUE, + borderRadius: 999, + justifyContent: 'center', + alignItems: 'center', + marginRight: '3%', + marginVertical: '2%', + alignSelf: 'flex-end', }, }); diff --git a/src/components/common/AcceptDeclineButtons.tsx b/src/components/common/AcceptDeclineButtons.tsx index 221056c0..9caaffca 100644 --- a/src/components/common/AcceptDeclineButtons.tsx +++ b/src/components/common/AcceptDeclineButtons.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {StyleProp, StyleSheet, Text, View, ViewStyle} from 'react-native'; -import {TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {ProfilePreviewType} from '../../types'; import {SCREEN_WIDTH} from '../../utils'; import {TouchableOpacity} from 'react-native-gesture-handler'; @@ -55,18 +55,18 @@ const styles = StyleSheet.create({ }, acceptButton: { padding: 0, - backgroundColor: TAGG_TEXT_LIGHT_BLUE, + backgroundColor: TAGG_LIGHT_BLUE, }, rejectButton: { borderWidth: 1, backgroundColor: 'white', - borderColor: TAGG_TEXT_LIGHT_BLUE, + borderColor: TAGG_LIGHT_BLUE, }, acceptButtonTitleColor: { color: 'white', }, rejectButtonTitleColor: { - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, buttonTitle: { padding: 0, diff --git a/src/components/common/GenericMoreInfoDrawer.tsx b/src/components/common/GenericMoreInfoDrawer.tsx index 098482ae..a23d7736 100644 --- a/src/components/common/GenericMoreInfoDrawer.tsx +++ b/src/components/common/GenericMoreInfoDrawer.tsx @@ -10,7 +10,7 @@ import { } from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import {BottomDrawer} from '.'; -import {TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; // conforms the JSX onPress attribute type @@ -87,7 +87,7 @@ const styles = StyleSheet.create({ panelButtonTitleCancel: { fontSize: 18, fontWeight: 'bold', - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, divider: {height: 1, borderWidth: 1, borderColor: '#e7e7e7'}, }); diff --git a/src/components/common/SocialLinkModal.tsx b/src/components/common/SocialLinkModal.tsx index b307a62c..41b044fe 100644 --- a/src/components/common/SocialLinkModal.tsx +++ b/src/components/common/SocialLinkModal.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {Modal, StyleSheet, Text, TouchableHighlight, View} from 'react-native'; import {TextInput} from 'react-native-gesture-handler'; -import { TAGG_TEXT_LIGHT_BLUE } from '../../constants'; +import { TAGG_LIGHT_BLUE } from '../../constants'; import {SCREEN_WIDTH} from '../../utils'; interface SocialLinkModalProps { @@ -105,7 +105,7 @@ const styles = StyleSheet.create({ fontSize: 14, /* identical to box height */ textAlign: 'center', - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, textInput: { height: 20, diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx index 7905e8a9..a6b553b1 100644 --- a/src/components/moments/Moment.tsx +++ b/src/components/moments/Moment.tsx @@ -11,9 +11,9 @@ import DownIcon from '../../assets/icons/down_icon.svg'; import PlusIcon from '../../assets/icons/plus_icon-01.svg'; import BigPlusIcon from '../../assets/icons/plus_icon-02.svg'; import UpIcon from '../../assets/icons/up_icon.svg'; -import {TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {ERROR_UPLOAD_MOMENT_SHORT} from '../../constants/strings'; -import {SCREEN_WIDTH} from '../../utils'; +import {normalize, SCREEN_WIDTH} from '../../utils'; import MomentTile from './MomentTile'; interface MomentProps { @@ -87,7 +87,7 @@ const Moment: React.FC<MomentProps> = ({ width={19} height={19} onPress={() => move('up', title)} - color={TAGG_TEXT_LIGHT_BLUE} + color={TAGG_LIGHT_BLUE} style={{marginLeft: 5}} /> )} @@ -96,7 +96,7 @@ const Moment: React.FC<MomentProps> = ({ width={19} height={19} onPress={() => move('down', title)} - color={TAGG_TEXT_LIGHT_BLUE} + color={TAGG_LIGHT_BLUE} style={{marginLeft: 5}} /> )} @@ -111,7 +111,7 @@ const Moment: React.FC<MomentProps> = ({ width={21} height={21} onPress={() => navigateToImagePicker()} - color={TAGG_TEXT_LIGHT_BLUE} + color={TAGG_LIGHT_BLUE} style={{marginRight: 10}} /> {shouldAllowDeletion && ( @@ -171,15 +171,10 @@ const styles = StyleSheet.create({ alignItems: 'center', }, titleText: { - fontSize: 16, + fontSize: normalize(16), fontWeight: 'bold', - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, - // titleContainer: { - // flex: 1, - // flexDirection: 'row', - // justifyContent: 'flex-end', - // }, flexer: { flex: 1, }, diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx index 93271fa1..508b6d9f 100644 --- a/src/components/moments/MomentPostContent.tsx +++ b/src/components/moments/MomentPostContent.tsx @@ -27,6 +27,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({ setElapsedTime(getTimePosted(dateTime)); getMomentCommentsCount(momentId, setCommentsCount); }, [dateTime, momentId]); + return ( <View style={[styles.container, style]}> <Image diff --git a/src/components/onboarding/TaggBigInput.tsx b/src/components/onboarding/TaggBigInput.tsx index 4e8e1ef7..0e42bd13 100644 --- a/src/components/onboarding/TaggBigInput.tsx +++ b/src/components/onboarding/TaggBigInput.tsx @@ -1,5 +1,11 @@ import React from 'react'; -import {View, TextInput, StyleSheet, TextInputProps} from 'react-native'; +import { + View, + TextInput, + StyleSheet, + TextInputProps, + ViewStyle, +} from 'react-native'; import * as Animatable from 'react-native-animatable'; import {TAGG_LIGHT_PURPLE} from '../../constants'; @@ -8,13 +14,15 @@ interface TaggBigInputProps extends TextInputProps { invalidWarning?: string; attemptedSubmit?: boolean; width?: number | string; + containerStyle?: ViewStyle; } /** * An input component that receives all props a normal TextInput component does. TaggInput components grow to 60% of their parent's width by default, but this can be set using the `width` prop. */ const TaggBigInput = React.forwardRef((props: TaggBigInputProps, ref: any) => { return ( - <View style={styles.container}> + <View + style={props.containerStyle ? props.containerStyle : styles.container}> <TextInput style={[{width: props.width}, styles.input]} placeholderTextColor="#ddd" diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index e7fb566b..a35a5820 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,3 +1,4 @@ +import {useFocusEffect, useNavigation} from '@react-navigation/native'; import React, {useCallback, useEffect, useState} from 'react'; import { Alert, @@ -9,52 +10,49 @@ import { Text, View, } from 'react-native'; +import {TouchableOpacity} from 'react-native-gesture-handler'; import Animated from 'react-native-reanimated'; +import {useDispatch, useSelector, useStore} from 'react-redux'; +import {Cover} from '.'; +import GreyPlusLogo from '../../assets/icons/grey-plus-logo.svg'; +import {COVER_HEIGHT, TAGG_LIGHT_BLUE} from '../../constants'; import { - CategorySelectionScreenType, - FriendshipStatusType, - MomentCategoryType, - MomentType, - ProfilePreviewType, - ProfileType, - ScreenType, - UserType, -} from '../../types'; -import {COVER_HEIGHT, TAGG_TEXT_LIGHT_BLUE} from '../../constants'; + UPLOAD_MOMENT_PROMPT_THREE_HEADER, + UPLOAD_MOMENT_PROMPT_THREE_MESSAGE, + UPLOAD_MOMENT_PROMPT_TWO_HEADER, + UPLOAD_MOMENT_PROMPT_TWO_MESSAGE, +} from '../../constants/strings'; +import { + blockUnblockUser, + deleteUserMomentsForCategory, + friendUnfriendUser, + loadFriendsData, + updateMomentCategories, + updateUserXFriends, + updateUserXProfileAllScreens, +} from '../../store/actions'; +import { + EMPTY_MOMENTS_LIST, + EMPTY_PROFILE_PREVIEW_LIST, + NO_PROFILE, + NO_USER, +} from '../../store/initialStates'; +import {RootState} from '../../store/rootreducer'; +import {CategorySelectionScreenType, MomentType, ScreenType} from '../../types'; import { fetchUserX, getUserAsProfilePreviewType, moveCategory, + normalize, SCREEN_HEIGHT, userLogin, } from '../../utils'; -import TaggsBar from '../taggs/TaggsBar'; +import {TaggPrompt} from '../common'; import {Moment} from '../moments'; +import TaggsBar from '../taggs/TaggsBar'; import ProfileBody from './ProfileBody'; import ProfileCutout from './ProfileCutout'; import ProfileHeader from './ProfileHeader'; -import {useDispatch, useSelector, useStore} from 'react-redux'; -import {RootState} from '../../store/rootreducer'; -import { - friendUnfriendUser, - blockUnblockUser, - loadFriendsData, - updateUserXFriends, - updateMomentCategories, - deleteUserMomentsForCategory, - updateUserXProfileAllScreens, -} from '../../store/actions'; -import { - NO_USER, - NO_PROFILE, - EMPTY_PROFILE_PREVIEW_LIST, - EMPTY_MOMENTS_LIST, -} from '../../store/initialStates'; -import {Cover} from '.'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {useFocusEffect, useNavigation} from '@react-navigation/native'; -import GreyPlusLogo from '../../assets/icons/grey-plus-logo.svg'; -import {TaggPrompt} from '../common'; interface ContentProps { y: Animated.Value<number>; @@ -113,9 +111,10 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { const [isStageOnePromptClosed, setIsStageOnePromptClosed] = useState<boolean>( false, ); - const [isStageThreePromptClosed, setIsStageThreePromptClosed] = useState< - boolean - >(false); + const [ + isStageThreePromptClosed, + setIsStageThreePromptClosed, + ] = useState<boolean>(false); const onRefresh = useCallback(() => { const refrestState = async () => { @@ -284,7 +283,7 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { momentCategories.filter((mc) => mc !== category), false, ), - ) + ); dispatch(deleteUserMomentsForCategory(category)); }, }, @@ -352,10 +351,8 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { profile.profile_completion_stage === 2 && !isStageTwoPromptClosed && ( <TaggPrompt - messageHeader="Create a new category" - messageBody={ - 'Post your first moment to continue building your digital identity!' - } + messageHeader={UPLOAD_MOMENT_PROMPT_TWO_HEADER} + messageBody={UPLOAD_MOMENT_PROMPT_TWO_MESSAGE} logoType="" onClose={() => { setIsStageTwoPromptClosed(true); @@ -366,10 +363,8 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { profile.profile_completion_stage === 3 && !isStageThreePromptClosed && ( <TaggPrompt - messageHeader="Continue to build your profile" - messageBody={ - 'Continue to personalize your own digital space in\nthis community by filling your profile with\ncategories and moments!' - } + messageHeader={UPLOAD_MOMENT_PROMPT_THREE_HEADER} + messageBody={UPLOAD_MOMENT_PROMPT_THREE_MESSAGE} logoType="" onClose={() => { setIsStageThreePromptClosed(true); @@ -423,7 +418,7 @@ const styles = StyleSheet.create({ flexDirection: 'column', }, createCategoryButton: { - backgroundColor: TAGG_TEXT_LIGHT_BLUE, + backgroundColor: TAGG_LIGHT_BLUE, justifyContent: 'center', alignItems: 'center', width: '70%', @@ -432,7 +427,7 @@ const styles = StyleSheet.create({ alignSelf: 'center', }, createCategoryButtonLabel: { - fontSize: 16, + fontSize: normalize(16), fontWeight: '500', color: 'white', }, @@ -443,7 +438,7 @@ const styles = StyleSheet.create({ marginVertical: '10%', }, noMomentsText: { - fontSize: 14, + fontSize: normalize(14), fontWeight: 'bold', color: 'gray', marginVertical: '8%', diff --git a/src/components/profile/FriendsCount.tsx b/src/components/profile/FriendsCount.tsx index 23a24787..9647710e 100644 --- a/src/components/profile/FriendsCount.tsx +++ b/src/components/profile/FriendsCount.tsx @@ -5,6 +5,7 @@ import {useNavigation} from '@react-navigation/native'; import {RootState} from '../../store/rootReducer'; import {useSelector} from 'react-redux'; import {ScreenType} from '../../types'; +import {normalize} from '../../utils'; interface FriendsCountProps extends ViewProps { userXId: string | undefined; @@ -55,11 +56,11 @@ const styles = StyleSheet.create({ }, count: { fontWeight: '700', - fontSize: 13, + fontSize: normalize(14), }, label: { fontWeight: '500', - fontSize: 13, + fontSize: normalize(14), }, }); diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx index 6284ff59..d10e2e15 100644 --- a/src/components/profile/ProfileBody.tsx +++ b/src/components/profile/ProfileBody.tsx @@ -1,9 +1,9 @@ import React from 'react'; import {StyleSheet, View, Text, LayoutChangeEvent, Linking} from 'react-native'; -import {Button} from 'react-native-elements'; +import {Button, normalize} from 'react-native-elements'; import { TAGG_DARK_BLUE, - TAGG_TEXT_LIGHT_BLUE, + TAGG_LIGHT_BLUE, TOGGLE_BUTTON_TYPE, } from '../../constants'; import ToggleButton from './ToggleButton'; @@ -160,16 +160,15 @@ const styles = StyleSheet.create({ }, username: { fontWeight: '600', - fontSize: 16.5, + fontSize: normalize(12), marginBottom: '1%', - marginTop: '-3%', }, biography: { - fontSize: 16, + fontSize: normalize(12), marginBottom: '1.5%', }, website: { - fontSize: 16, + fontSize: normalize(12), color: TAGG_DARK_BLUE, marginBottom: '1%', }, @@ -178,7 +177,7 @@ const styles = StyleSheet.create({ alignItems: 'center', width: SCREEN_WIDTH * 0.4, height: SCREEN_WIDTH * 0.09, - borderColor: TAGG_TEXT_LIGHT_BLUE, + borderColor: TAGG_LIGHT_BLUE, borderWidth: 3, borderRadius: 5, marginRight: '2%', @@ -186,7 +185,7 @@ const styles = StyleSheet.create({ backgroundColor: 'transparent', }, requestedButtonTitle: { - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, padding: 0, fontSize: 14, fontWeight: '700', @@ -205,7 +204,7 @@ const styles = StyleSheet.create({ padding: 0, borderRadius: 5, marginRight: '2%', - backgroundColor: TAGG_TEXT_LIGHT_BLUE, + backgroundColor: TAGG_LIGHT_BLUE, }, }); diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 8d502d97..7dad2a68 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -2,10 +2,10 @@ import React, {useState} from 'react'; import {StyleSheet, Text, View} from 'react-native'; import {useSelector} from 'react-redux'; import {UniversityIcon} from '.'; -import {NO_MOMENTS} from '../../store/initialStates'; +import {PROFILE_CUTOUT_TOP_Y} from '../../constants'; import {RootState} from '../../store/rootreducer'; import {ScreenType} from '../../types'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import {normalize} from '../../utils'; import Avatar from './Avatar'; import FriendsCount from './FriendsCount'; import ProfileMoreInfoDrawer from './ProfileMoreInfoDrawer'; @@ -31,7 +31,6 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = ({ : useSelector((state: RootState) => state.user); const [drawerVisible, setDrawerVisible] = useState(false); const [firstName, lastName] = [...name.split(' ')]; - return ( <View style={styles.container}> <ProfileMoreInfoDrawer @@ -59,13 +58,8 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = ({ </View> )} <View style={styles.friendsAndUniversity}> - <FriendsCount - style={styles.friends} - screenType={screenType} - userXId={userXId} - /> + <FriendsCount screenType={screenType} userXId={userXId} /> <UniversityIcon - style={styles.university} university="brown" university_class={university_class} /> @@ -78,7 +72,7 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = ({ const styles = StyleSheet.create({ container: { - top: SCREEN_HEIGHT / 2.4, + top: PROFILE_CUTOUT_TOP_Y * 1.02, width: '100%', position: 'absolute', }, @@ -87,35 +81,27 @@ const styles = StyleSheet.create({ }, header: { flexDirection: 'column', - justifyContent: 'center', + justifyContent: 'space-evenly', alignItems: 'center', - marginTop: SCREEN_WIDTH / 18.2, - marginLeft: SCREEN_WIDTH / 8, - marginBottom: SCREEN_WIDTH / 50, + marginRight: '15%', + marginLeft: '5%', + flex: 1, }, avatar: { - bottom: SCREEN_WIDTH / 80, - left: '10%', + marginLeft: '3%', + top: '-8%', }, name: { - marginLeft: SCREEN_WIDTH / 8, - fontSize: 17, + fontSize: normalize(17), fontWeight: '500', alignSelf: 'center', }, - friends: { - alignSelf: 'flex-start', - marginRight: SCREEN_WIDTH / 20, - }, - university: { - alignSelf: 'flex-end', - bottom: 3, - }, friendsAndUniversity: { flexDirection: 'row', - flex: 1, - marginLeft: SCREEN_WIDTH / 10, - marginTop: SCREEN_WIDTH / 40, + alignItems: 'center', + justifyContent: 'space-evenly', + width: '100%', + height: 50, }, }); diff --git a/src/components/profile/ProfileMoreInfoDrawer.tsx b/src/components/profile/ProfileMoreInfoDrawer.tsx index 76f0f27f..daa83eb3 100644 --- a/src/components/profile/ProfileMoreInfoDrawer.tsx +++ b/src/components/profile/ProfileMoreInfoDrawer.tsx @@ -4,7 +4,7 @@ import {StyleSheet, TouchableOpacity} from 'react-native'; import {useSelector} from 'react-redux'; import MoreIcon from '../../assets/icons/more_horiz-24px.svg'; import PersonOutline from '../../assets/ionicons/person-outline.svg'; -import {TAGG_DARK_BLUE, TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {TAGG_DARK_BLUE, TAGG_LIGHT_BLUE} from '../../constants'; import {RootState} from '../../store/rootreducer'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {GenericMoreInfoDrawer} from '../common'; @@ -101,13 +101,12 @@ const styles = StyleSheet.create({ panelButtonTitleCancel: { fontSize: 18, fontWeight: 'bold', - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, divider: {height: 1, borderWidth: 1, borderColor: '#e7e7e7'}, more: { position: 'absolute', right: '5%', - marginTop: '4%', zIndex: 1, }, }); diff --git a/src/components/profile/ProfilePreview.tsx b/src/components/profile/ProfilePreview.tsx index b2c0a24d..389ca367 100644 --- a/src/components/profile/ProfilePreview.tsx +++ b/src/components/profile/ProfilePreview.tsx @@ -1,27 +1,26 @@ -import React, {useEffect, useState, useContext} from 'react'; -import {ProfilePreviewType, ScreenType} from '../../types'; +import AsyncStorage from '@react-native-community/async-storage'; +import {useNavigation} from '@react-navigation/native'; +import React, {useEffect, useState} from 'react'; import { - View, - Text, + Alert, Image, StyleSheet, - ViewProps, + Text, TouchableOpacity, - Alert, + View, + ViewProps, } from 'react-native'; -import {useNavigation} from '@react-navigation/native'; -import RNFetchBlob from 'rn-fetch-blob'; -import AsyncStorage from '@react-native-community/async-storage'; -import {PROFILE_PHOTO_THUMBNAIL_ENDPOINT} from '../../constants'; -import {UserType, PreviewType} from '../../types'; -import {isUserBlocked, loadAvatar} from '../../services'; -import {useSelector, useDispatch, useStore} from 'react-redux'; +import {useDispatch, useSelector, useStore} from 'react-redux'; +import {ERROR_UNABLE_TO_VIEW_PROFILE} from '../../constants/strings'; +import {loadAvatar} from '../../services'; import {RootState} from '../../store/rootreducer'; -import {logout} from '../../store/actions'; +import { + PreviewType, + ProfilePreviewType, + ScreenType, + UserType, +} from '../../types'; import {checkIfUserIsBlocked, fetchUserX, userXInStore} from '../../utils'; -import {SearchResultsBackground} from '../search'; -import NavigationBar from 'src/routes/tabs'; -import {ERROR_UNABLE_TO_VIEW_PROFILE} from '../../constants/strings'; const NO_USER: UserType = { userId: '', diff --git a/src/components/profile/ToggleButton.tsx b/src/components/profile/ToggleButton.tsx index 5d8f7874..236d811c 100644 --- a/src/components/profile/ToggleButton.tsx +++ b/src/components/profile/ToggleButton.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import {StyleSheet, Text} from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; -import {TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {getToggleButtonText, SCREEN_WIDTH} from '../../utils'; type ToggleButtonProps = { @@ -36,7 +36,7 @@ const styles = StyleSheet.create({ alignItems: 'center', width: SCREEN_WIDTH * 0.4, height: SCREEN_WIDTH * 0.08, - borderColor: TAGG_TEXT_LIGHT_BLUE, + borderColor: TAGG_LIGHT_BLUE, borderWidth: 3, borderRadius: 5, marginRight: '2%', @@ -45,10 +45,10 @@ const styles = StyleSheet.create({ fontWeight: 'bold', }, buttonColor: { - backgroundColor: TAGG_TEXT_LIGHT_BLUE, + backgroundColor: TAGG_LIGHT_BLUE, }, textColor: {color: 'white'}, buttonColorToggled: {backgroundColor: 'white'}, - textColorToggled: {color: TAGG_TEXT_LIGHT_BLUE}, + textColorToggled: {color: TAGG_LIGHT_BLUE}, }); export default ToggleButton; diff --git a/src/components/profile/UniversityIcon.tsx b/src/components/profile/UniversityIcon.tsx index 13586359..95aef8b9 100644 --- a/src/components/profile/UniversityIcon.tsx +++ b/src/components/profile/UniversityIcon.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {StyleSheet, ViewProps} from 'react-native'; import {Image, Text, View} from 'react-native-animatable'; -import {getUniversityClass} from '../../utils'; +import {getUniversityClass, normalize} from '../../utils'; export interface UniversityIconProps extends ViewProps { university: string; @@ -38,19 +38,19 @@ const UniversityIcon: React.FC<UniversityIconProps> = ({ const styles = StyleSheet.create({ container: { - flex: 1, flexDirection: 'column', flexWrap: 'wrap', justifyContent: 'center', + alignItems: 'center', + height: '100%', }, univClass: { - fontSize: 13, + fontSize: normalize(14), fontWeight: '500', }, icon: { - alignSelf: 'center', - width: 17, - height: 19, + width: normalize(17), + height: normalize(19), }, }); diff --git a/src/components/search/Explore.tsx b/src/components/search/Explore.tsx index c07c66b8..4a71249b 100644 --- a/src/components/search/Explore.tsx +++ b/src/components/search/Explore.tsx @@ -4,6 +4,7 @@ import {useSelector} from 'react-redux'; import {EXPLORE_SECTION_TITLES} from '../../constants'; import {RootState} from '../../store/rootReducer'; import {ExploreSectionType} from '../../types'; +import {normalize} from '../../utils'; import ExploreSection from './ExploreSection'; const Explore: React.FC = () => { @@ -21,11 +22,10 @@ const Explore: React.FC = () => { const styles = StyleSheet.create({ container: { zIndex: 0, - // margin: '5%', }, header: { fontWeight: '700', - fontSize: 22, + fontSize: normalize(22), color: '#fff', marginBottom: '2%', margin: '5%', diff --git a/src/components/search/ExploreSection.tsx b/src/components/search/ExploreSection.tsx index 8e8b4988..17079e77 100644 --- a/src/components/search/ExploreSection.tsx +++ b/src/components/search/ExploreSection.tsx @@ -1,6 +1,7 @@ import React, {Fragment} from 'react'; import {ScrollView, StyleSheet, Text, View} from 'react-native'; import {ProfilePreviewType} from '../../types'; +import {normalize} from '../../utils'; import ExploreSectionUser from './ExploreSectionUser'; /** @@ -34,7 +35,7 @@ const styles = StyleSheet.create({ }, header: { fontWeight: '600', - fontSize: 20, + fontSize: normalize(18), color: '#fff', marginLeft: '5%', marginBottom: '5%', diff --git a/src/components/search/ExploreSectionUser.tsx b/src/components/search/ExploreSectionUser.tsx index 0bf68a20..68e077e3 100644 --- a/src/components/search/ExploreSectionUser.tsx +++ b/src/components/search/ExploreSectionUser.tsx @@ -12,7 +12,7 @@ import {useDispatch, useSelector, useStore} from 'react-redux'; import {loadAvatar} from '../../services'; import {RootState} from '../../store/rootReducer'; import {ProfilePreviewType, ScreenType} from '../../types'; -import {fetchUserX, userXInStore} from '../../utils'; +import {fetchUserX, normalize, userXInStore} from '../../utils'; /** * Search Screen for user recommendations and a search @@ -110,13 +110,13 @@ const styles = StyleSheet.create({ name: { fontWeight: '600', flexWrap: 'wrap', - fontSize: 16, + fontSize: normalize(16), color: '#fff', textAlign: 'center', }, username: { fontWeight: '400', - fontSize: 14, + fontSize: normalize(14), color: '#fff', }, }); diff --git a/src/components/search/RecentSearches.tsx b/src/components/search/RecentSearches.tsx index 8a06017c..bdbd5773 100644 --- a/src/components/search/RecentSearches.tsx +++ b/src/components/search/RecentSearches.tsx @@ -7,7 +7,7 @@ import { TouchableOpacityProps, } from 'react-native'; import {PreviewType, ProfilePreviewType, ScreenType} from 'src/types'; -import {TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import SearchResults from './SearchResults'; interface RecentSearchesProps extends TouchableOpacityProps { @@ -55,7 +55,7 @@ const styles = StyleSheet.create({ clear: { fontSize: 18, fontWeight: 'bold', - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, }); diff --git a/src/components/taggs/Tagg.tsx b/src/components/taggs/Tagg.tsx index 82ac07df..5fa8b395 100644 --- a/src/components/taggs/Tagg.tsx +++ b/src/components/taggs/Tagg.tsx @@ -22,6 +22,7 @@ import { ERROR_UNABLE_TO_FIND_PROFILE, SUCCESS_LINK, } from '../../constants/strings'; +import {normalize} from '../../utils'; interface TaggProps { social: string; @@ -165,7 +166,7 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', alignItems: 'center', marginHorizontal: 15, - height: 90, + height: normalize(90), }, iconTap: { justifyContent: 'center', diff --git a/src/components/taggs/TwitterTaggPost.tsx b/src/components/taggs/TwitterTaggPost.tsx index c971a82c..0cfde857 100644 --- a/src/components/taggs/TwitterTaggPost.tsx +++ b/src/components/taggs/TwitterTaggPost.tsx @@ -6,7 +6,7 @@ import LinearGradient from 'react-native-linear-gradient'; import { AVATAR_DIM, TAGGS_GRADIENT, - TAGG_TEXT_LIGHT_BLUE, + TAGG_LIGHT_BLUE, } from '../../constants'; import {TwitterPostType} from '../../types'; import {handleOpenSocialUrlOnBrowser, SCREEN_WIDTH} from '../../utils'; @@ -237,7 +237,7 @@ const styles = StyleSheet.create({ }, replyShowThisThread: { fontSize: 15, - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, }); diff --git a/src/constants/constants.ts b/src/constants/constants.ts index b96d9438..ad43c337 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -1,13 +1,15 @@ import {ReactText} from 'react'; import {BackgroundGradientType, ExploreSectionType} from './../types/'; -import {SCREEN_WIDTH, SCREEN_HEIGHT} from '../utils'; +import {SCREEN_WIDTH, SCREEN_HEIGHT, isIPhoneX, normalize} from '../utils'; export const CHIN_HEIGHT = 34; -export const PROFILE_CUTOUT_TOP_Y = SCREEN_HEIGHT / 2.3; -export const PROFILE_CUTOUT_BOTTOM_Y = SCREEN_HEIGHT / 1.76; -export const PROFILE_CUTOUT_CORNER_X = SCREEN_WIDTH / 2.9; -export const PROFILE_CUTOUT_CORNER_Y = SCREEN_HEIGHT / 1.95; +export const PROFILE_CUTOUT_TOP_Y = SCREEN_HEIGHT * 0.435; +export const PROFILE_CUTOUT_BOTTOM_Y = isIPhoneX() + ? SCREEN_HEIGHT * 0.55 + : SCREEN_HEIGHT * 0.58; +export const PROFILE_CUTOUT_CORNER_X = SCREEN_WIDTH * 0.344; +export const PROFILE_CUTOUT_CORNER_Y = SCREEN_HEIGHT * 0.513; export const IMAGE_WIDTH = SCREEN_WIDTH; export const IMAGE_HEIGHT = SCREEN_WIDTH; @@ -17,7 +19,7 @@ export const AVATAR_DIM = 44; export const AVATAR_GRADIENT_DIM = 50; export const TAGG_ICON_DIM = 58; -export const TAGG_RING_DIM = 65; +export const TAGG_RING_DIM = normalize(60); export const INTEGRATED_SOCIAL_LIST: string[] = [ 'Instagram', @@ -59,7 +61,7 @@ export const SNAPCHAT_FONT_COLOR: string = '#FFFC00'; export const YOUTUBE_FONT_COLOR: string = '#FCA4A4'; export const TAGG_DARK_BLUE = '#4E699C'; -export const TAGG_TEXT_LIGHT_BLUE: string = '#698DD3'; +export const TAGG_LIGHT_BLUE: string = '#698DD3'; export const TAGG_LIGHT_PURPLE = '#F4DDFF'; export const TAGGS_GRADIENT = { diff --git a/src/constants/regex.ts b/src/constants/regex.ts index fe5ce3ab..7de36492 100644 --- a/src/constants/regex.ts +++ b/src/constants/regex.ts @@ -52,10 +52,10 @@ export const genderRegex: RegExp = /^$|^[A-Za-z\- ]{2,20}$/; /** * The phone regex has the following constraints * - must be 10 digits - * - accepts 012-345-6789 + * - accepts 0123456789 * */ -export const phoneRegex: RegExp = /([0-9]{10})/; +export const phoneRegex: RegExp = /^[0-9]{10}$/; /** * The code regex has the following constraints diff --git a/src/constants/strings.ts b/src/constants/strings.ts index b5344afd..a1793658 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -12,23 +12,23 @@ export const ERROR_DOUBLE_CHECK_CONNECTION = 'Please double-check your network c export const ERROR_DUP_OLD_PWD = 'You may not use a previously used password'; export const ERROR_EMAIL_IN_USE = 'Email already in use, please try another one'; export const ERROR_FAILED_LOGIN_INFO = 'Login failed, please try re-entering your login information'; -export const ERROR_FAILED_TO_COMMENT = 'Unable to post comment, refresh and try again!'; +export const ERROR_FAILED_TO_COMMENT = 'Unable to post comment, refresh and try again!'; export const ERROR_INVALID_INVITATION_CODE = 'Invitation code invalid, try again or talk to the friend that sent it π¬'; export const ERROR_INVALID_LOGIN = 'Invalid login, Please login again'; export const ERROR_INVALID_PWD_CODE = 'Looks like you have entered the wrong code, please try again'; export const ERROR_INVALID_VERIFICATION_CODE = 'Invalid verification code, try re-entering or tap the resend code button for a new code'; export const ERROR_INVALID_VERIFICATION_CODE_FORMAT = 'Please enter the 6 digit code sent to your phone'; export const ERROR_INVLAID_CODE = 'The code entered is not valid!'; -export const ERROR_LINK = (str: string) => `Unable to link with ${str}, Please check your login and try again`; -export const ERROR_LOGIN = 'There was a problem logging you in, please refresh and try again'; +export const ERROR_LINK = (str: string) => `Unable to link with ${str}, Please check your login and try again`; +export const ERROR_LOGIN = 'There was a problem logging you in, please refresh and try again'; export const ERROR_LOGIN_FAILED = 'Login failed. Check your username and passoword, and try again'; export const ERROR_NEXT_PAGE = 'There was a problem while loading the next page π, try again in a couple minutes'; export const ERROR_PROFILE_CREATION_SHORT = 'Profile creation failed π'; export const ERROR_PWD_ACCOUNT = (str: string) => `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${str}`; -export const ERROR_REGISTRATION = (str: string) => `Registration failed π, ${str}`; +export const ERROR_REGISTRATION = (str: string) => `Registration failed π, ${str}`; export const ERROR_SELECT_CLASS_YEAR = 'Please select your Class Year'; export const ERROR_SERVER_DOWN = 'mhm, looks like our servers are down, please refresh and try again in a few mins'; -export const ERROR_SOMETHING_WENT_WRONG = "Oh dear, donβt worry someone will be held responsible for this error, In the meantime refresh the app"; +export const ERROR_SOMETHING_WENT_WRONG = 'Oh dear, donβt worry someone will be held responsible for this error, In the meantime refresh the app'; export const ERROR_SOMETHING_WENT_WRONG_REFRESH = "Ha, looks like this one's on us, please refresh and try again"; export const ERROR_SOMETHING_WENT_WRONG_RELOAD = "You broke it, Just kidding! we don't know what happened... Please reload the app and try again"; export const ERROR_UNABLE_TO_FIND_PROFILE = 'We were unable to find this profile. Please check username and try again'; @@ -45,3 +45,8 @@ export const SUCCESS_LINK = (str: string) => `Successfully linked ${str} π`; export const SUCCESS_PIC_UPLOAD = 'Beautiful, the picture was uploaded successfully!'; export const SUCCESS_PWD_RESET = 'Your password was reset successfully!'; export const SUCCESS_VERIFICATION_CODE_SENT = 'New verification code sent! Check your phone messages for your code'; +export const UPLOAD_MOMENT_PROMPT_ONE_MESSAGE = 'Post your first moment to\n continue building your digital\nidentity!'; +export const UPLOAD_MOMENT_PROMPT_THREE_HEADER = 'Continue to build your profile'; +export const UPLOAD_MOMENT_PROMPT_THREE_MESSAGE = 'Continue to personalize your own digital space in\nthis community by filling your profile with\ncategories and moments!'; +export const UPLOAD_MOMENT_PROMPT_TWO_HEADER = 'Create a new category'; +export const UPLOAD_MOMENT_PROMPT_TWO_MESSAGE = 'You can now create new categories \nand continue to fill your profile with moments!'; diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx index b4eaa213..3e425101 100644 --- a/src/routes/main/MainStackScreen.tsx +++ b/src/routes/main/MainStackScreen.tsx @@ -54,7 +54,7 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { })(); const modalStyle: StackNavigationOptions = { - cardStyle: {backgroundColor: 'transparent'}, + cardStyle: {backgroundColor: 'rgba(80,80,80,0.9)'}, gestureDirection: 'vertical', cardOverlayEnabled: true, cardStyleInterpolator: ({current: {progress}}) => ({ @@ -64,14 +64,6 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { outputRange: [0, 0.25, 0.7, 1], }), }, - overlayStyle: { - backgroundColor: '#505050', - opacity: progress.interpolate({ - inputRange: [0, 1], - outputRange: [0, 0.9], - extrapolate: 'clamp', - }), - }, }), }; @@ -156,16 +148,25 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { name="IndividualMoment" component={IndividualMoment} options={{ - ...modalStyle, + gestureEnabled: false, + cardStyle: { + backgroundColor: 'rgba(0, 0, 0, 0.6)', + }, + cardOverlayEnabled: true, + cardStyleInterpolator: ({current: {progress}}) => ({ + cardStyle: { + opacity: progress.interpolate({ + inputRange: [0, 0.5, 0.9, 1], + outputRange: [0, 0.25, 0.7, 1], + }), + }, + }), }} initialParams={{screenType}} /> <MainStack.Screen name="MomentCommentsScreen" component={MomentCommentsScreen} - options={{ - ...modalStyle, - }} initialParams={{screenType}} /> <MainStack.Screen diff --git a/src/screens/onboarding/RegistrationOne.tsx b/src/screens/onboarding/RegistrationOne.tsx index 2a1d884d..c9822f76 100644 --- a/src/screens/onboarding/RegistrationOne.tsx +++ b/src/screens/onboarding/RegistrationOne.tsx @@ -146,7 +146,7 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { <Text style={styles.formHeader}>ENTER PHONE NUMBER</Text> </View> <TaggInput - maxLength={12} // currently only support US phone numbers + maxLength={10} // currently only support US phone numbers accessibilityHint="Enter your phone number." accessibilityLabel="Phone number input field." placeholder="Phone Number" @@ -154,7 +154,7 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { textContentType="telephoneNumber" autoCapitalize="none" returnKeyType="next" - keyboardType="phone-pad" + keyboardType="number-pad" onChangeText={handlePhoneUpdate} blurOnSubmit={false} ref={phoneRef} diff --git a/src/screens/profile/EditProfile.tsx b/src/screens/profile/EditProfile.tsx index 3b3fa36e..7d3ca581 100644 --- a/src/screens/profile/EditProfile.tsx +++ b/src/screens/profile/EditProfile.tsx @@ -29,9 +29,10 @@ import { websiteRegex, bioRegex, genderRegex, + CLASS_YEAR_LIST, } from '../../constants'; import AsyncStorage from '@react-native-community/async-storage'; -import {ProfileStackParams} from '../../routes'; +import {MainStackParams} from '../../routes'; import Animated from 'react-native-reanimated'; import {HeaderHeight, SCREEN_HEIGHT} from '../../utils'; import {RootState} from '../../store/rootReducer'; @@ -47,12 +48,12 @@ import { import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; type EditProfileNavigationProp = StackNavigationProp< - ProfileStackParams, + MainStackParams, 'EditProfile' >; interface EditProfileProps { - route: RouteProp<ProfileStackParams, 'EditProfile'>; + route: RouteProp<MainStackParams, 'EditProfile'>; navigation: EditProfileNavigationProp; } @@ -65,7 +66,7 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { const y: Animated.Value<number> = Animated.useValue(0); const {userId, username} = route.params; const { - profile: {website, biography, gender, snapchat, tiktok}, + profile: {website, biography, gender, snapchat, tiktok, university_class}, avatar, cover, } = useSelector((state: RootState) => state.user); @@ -99,6 +100,13 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { isValidSnapchat: true, isValidTiktok: true, attemptedSubmit: false, + classYear: university_class, + }); + + var classYearList: Array<any> = []; + + CLASS_YEAR_LIST.map((value) => { + classYearList.push({label: value, value: value}); }); /** @@ -254,6 +262,14 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { }); }; + const handleClassYearUpdate = (value: string) => { + const classYear = Number.parseInt(value); + setForm({ + ...form, + classYear, + }); + }; + const handleSubmit = useCallback(async () => { if (!form.largePic) { Alert.alert(ERROR_UPLOAD_LARGE_PROFILE_PIC); @@ -297,7 +313,7 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { if (form.bio) { if (form.isValidBio) { - request.append('biography', form.bio); + request.append('biography', form.bio.trim()); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); @@ -335,6 +351,15 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { invalidFields = true; } + if (form.classYear !== university_class) { + if (!form.classYear) { + invalidFields = true; + Alert.alert('Please select a valid class year'); + } else { + request.append('university_class', form.classYear); + } + } + if (invalidFields) { return; } @@ -487,6 +512,19 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { value={form.customGenderText} /> )} + + <TaggDropDown + value={form.classYear.toString()} + onValueChange={(value: string) => + handleClassYearUpdate(value) + } + items={classYearList} + placeholder={{ + label: 'Class Year', + value: null, + color: '#ddd', + }} + /> {snapchat !== '' && ( <View style={styles.row}> <SocialIcon social={'Snapchat'} style={styles.icon} /> diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx index f13e1295..6b82b31c 100644 --- a/src/screens/profile/IndividualMoment.tsx +++ b/src/screens/profile/IndividualMoment.tsx @@ -80,7 +80,7 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({ return ( <BlurView blurType="light" - blurAmount={10} + blurAmount={30} reducedTransparencyFallbackColor="white" style={styles.contentContainer}> <IndividualMomentTitleBar diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx index ebe4da28..2bceafc9 100644 --- a/src/screens/profile/MomentCommentsScreen.tsx +++ b/src/screens/profile/MomentCommentsScreen.tsx @@ -1,17 +1,21 @@ -import * as React from 'react'; import {RouteProp, useNavigation} from '@react-navigation/native'; +import React, {useEffect, useRef} from 'react'; +import { + ScrollView, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import {useDispatch} from 'react-redux'; +import {getMomentComments} from '../..//services'; +import BackIcon from '../../assets/icons/back-arrow.svg'; +import {CommentTile, TabsGradient} from '../../components'; +import {AddComment} from '../../components/'; import {ProfileStackParams} from '../../routes/main'; -import {CenteredView, CommentTile} from '../../components'; import {CommentType} from '../../types'; -import {ScrollView, StyleSheet, Text, View} from 'react-native'; -import {SCREEN_WIDTH} from '../../utils/screenDimensions'; -import {Button} from 'react-native-elements'; -import {AddComment} from '../../components/'; -import {useEffect} from 'react'; -import AsyncStorage from '@react-native-community/async-storage'; -import {getMomentComments} from '../..//services'; -import {useDispatch} from 'react-redux'; -import {logout} from '../../store/actions'; +import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; /** * Comments Screen for an image uploaded @@ -34,105 +38,98 @@ const MomentCommentsScreen: React.FC<MomentCommentsScreenProps> = ({route}) => { const [commentsList, setCommentsList] = React.useState([]); const [newCommentsAvailable, setNewCommentsAvailable] = React.useState(true); const dispatch = useDispatch(); + const ref = useRef<ScrollView>(null); useEffect(() => { const loadComments = async () => { - const token = await AsyncStorage.getItem('token'); - if (!token) { - dispatch(logout()); - return; - } - getMomentComments(moment_id, setCommentsList, token); + getMomentComments(moment_id, setCommentsList); setNewCommentsAvailable(false); }; if (newCommentsAvailable) { loadComments(); + setTimeout(() => { + ref.current?.scrollToEnd({ + animated: true, + }); + }, 500); } }, [dispatch, moment_id, newCommentsAvailable]); return ( - <CenteredView> - <View style={styles.modalView}> + <View style={styles.background}> + <SafeAreaView> <View style={styles.header}> - <Button - title="X" - buttonStyle={styles.button} - titleStyle={styles.buttonText} + <TouchableOpacity + style={styles.headerButton} onPress={() => { navigation.pop(); - }} - /> + }}> + <BackIcon height={'100%'} width={'100%'} color={'white'} /> + </TouchableOpacity> <Text style={styles.headerText}> {commentsList.length + ' Comments'} </Text> </View> - <ScrollView - style={styles.modalScrollView} - contentContainerStyle={styles.modalScrollViewContent}> - {commentsList && - commentsList.map((comment: CommentType) => ( - <CommentTile - key={comment.comment_id} - comment_object={comment} - screenType={screenType} - /> - ))} - </ScrollView> - <AddComment - setNewCommentsAvailable={setNewCommentsAvailable} - moment_id={moment_id} - /> - </View> - </CenteredView> + <View style={styles.body}> + <ScrollView + ref={ref} + style={styles.scrollView} + contentContainerStyle={styles.scrollViewContent}> + {commentsList && + commentsList.map((comment: CommentType) => ( + <CommentTile + key={comment.comment_id} + comment_object={comment} + screenType={screenType} + /> + ))} + </ScrollView> + <AddComment + setNewCommentsAvailable={setNewCommentsAvailable} + moment_id={moment_id} + /> + </View> + </SafeAreaView> + <TabsGradient /> + </View> ); }; const styles = StyleSheet.create({ - header: {flexDirection: 'row'}, + background: { + backgroundColor: 'white', + height: '100%', + }, + header: {justifyContent: 'center', padding: '3%'}, headerText: { - position: 'relative', - left: '180%', + position: 'absolute', alignSelf: 'center', - fontSize: 18, - fontWeight: '500', + fontSize: 20.5, + fontWeight: '600', }, - container: { - position: 'relative', - top: '5%', - left: '5%', - backgroundColor: 'white', - borderRadius: 5, - width: SCREEN_WIDTH / 1.1, - height: '55%', - }, - button: { - backgroundColor: 'transparent', + headerButton: { + width: '5%', + aspectRatio: 1, + padding: 0, + marginLeft: '5%', + alignSelf: 'flex-start', }, - buttonText: { + headerButtonText: { color: 'black', fontSize: 18, fontWeight: '400', }, - modalView: { - width: '85%', - height: '70%', - backgroundColor: '#fff', - shadowColor: '#000', - shadowOpacity: 30, - shadowOffset: {width: 0, height: 2}, - shadowRadius: 5, - borderRadius: 8, - paddingBottom: 15, + body: { + width: SCREEN_WIDTH, + height: SCREEN_HEIGHT * 0.8, + paddingTop: '3%', + }, + scrollView: { paddingHorizontal: 20, - paddingTop: 5, - justifyContent: 'space-between', }, - modalScrollViewContent: { + scrollViewContent: { justifyContent: 'center', }, - modalScrollView: { - marginBottom: 10, - }, }); export default MomentCommentsScreen; diff --git a/src/screens/profile/MomentUploadPromptScreen.tsx b/src/screens/profile/MomentUploadPromptScreen.tsx index 6111985d..9d46c1e9 100644 --- a/src/screens/profile/MomentUploadPromptScreen.tsx +++ b/src/screens/profile/MomentUploadPromptScreen.tsx @@ -6,6 +6,7 @@ import CloseIcon from '../../assets/ionicons/close-outline.svg'; import {StyleSheet, Text, View} from 'react-native'; import {Moment} from '../../components'; import {Image} from 'react-native-animatable'; +import {UPLOAD_MOMENT_PROMPT_ONE_MESSAGE} from '../../constants/strings'; type MomentUploadPromptScreenRouteProp = RouteProp< MainStackParams, @@ -38,10 +39,7 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({ }} /> - <Text style={styles.text}> - Post your first moment to {'\n'} continue building your digital {'\n'}{' '} - identity! - </Text> + <Text style={styles.text}>{UPLOAD_MOMENT_PROMPT_ONE_MESSAGE}</Text> <Image source={require('../../assets/gifs/dotted-arrow-white.gif')} style={styles.arrowGif} @@ -54,6 +52,8 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({ screenType={screenType} handleMomentCategoryDelete={() => {}} shouldAllowDeletion={false} + showDownButton={false} + showUpButton={false} externalStyles={{ container: styles.momentContainer, titleText: styles.momentHeaderText, diff --git a/src/screens/search/SearchScreen.tsx b/src/screens/search/SearchScreen.tsx index 9f98b4d7..059bd968 100644 --- a/src/screens/search/SearchScreen.tsx +++ b/src/screens/search/SearchScreen.tsx @@ -2,6 +2,7 @@ import AsyncStorage from '@react-native-community/async-storage'; import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useEffect, useState} from 'react'; import { + Dimensions, Keyboard, RefreshControl, ScrollView, @@ -20,7 +21,7 @@ import { SearchResultsBackground, TabsGradient, } from '../../components'; -import {SEARCH_ENDPOINT, TAGG_TEXT_LIGHT_BLUE} from '../../constants'; +import {SEARCH_ENDPOINT, TAGG_LIGHT_BLUE} from '../../constants'; import {loadRecentlySearched, resetScreenType} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {ProfilePreviewType, ScreenType, UserType} from '../../types'; @@ -213,7 +214,7 @@ const styles = StyleSheet.create({ clear: { fontSize: 17, fontWeight: 'bold', - color: TAGG_TEXT_LIGHT_BLUE, + color: TAGG_LIGHT_BLUE, }, image: { width: SCREEN_WIDTH, diff --git a/src/services/MomentServices.ts b/src/services/MomentServices.ts index 735f2ed2..7bad6d4c 100644 --- a/src/services/MomentServices.ts +++ b/src/services/MomentServices.ts @@ -14,9 +14,9 @@ import {checkImageUploadStatus} from '../utils'; export const getMomentComments = async ( momentId: string, callback: Function, - token: string, ) => { try { + const token = await AsyncStorage.getItem('token'); const response = await fetch(COMMENTS_ENDPOINT + '?moment_id=' + momentId, { method: 'GET', headers: { @@ -35,14 +35,13 @@ export const getMomentComments = async ( } }; -//Post a comment on a moment export const postMomentComment = async ( commenter: string, comment: string, momentId: string, - token: string, ) => { try { + const token = await AsyncStorage.getItem('token'); const request = new FormData(); request.append('moment_id', momentId); request.append('commenter', commenter); @@ -60,7 +59,7 @@ export const postMomentComment = async ( return await response.json(); } catch (error) { Alert.alert(ERROR_FAILED_TO_COMMENT); - return {}; + return undefined; } }; diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts index 75d7d367..b400843d 100644 --- a/src/services/UserProfileService.ts +++ b/src/services/UserProfileService.ts @@ -164,12 +164,8 @@ export const loadRecentlySearchedUsers = async () => { export const handlePasswordResetRequest = async (value: string) => { try { - const token = await AsyncStorage.getItem('token'); const response = await fetch(PASSWORD_RESET_ENDPOINT + 'request/', { method: 'POST', - headers: { - Authorization: 'Token ' + token, - }, body: JSON.stringify({ value, }), @@ -204,12 +200,8 @@ export const handlePasswordCodeVerification = async ( otp: string, ) => { try { - const token = await AsyncStorage.getItem('token'); const response = await fetch(PASSWORD_RESET_ENDPOINT + 'verify/', { method: 'POST', - headers: { - Authorization: 'Token ' + token, - }, body: JSON.stringify({ value, otp, @@ -239,12 +231,8 @@ export const handlePasswordCodeVerification = async ( export const handlePasswordReset = async (value: string, password: string) => { try { - const token = await AsyncStorage.getItem('token'); const response = await fetch(PASSWORD_RESET_ENDPOINT + 'reset/', { method: 'POST', - headers: { - Authorization: 'Token ' + token, - }, body: JSON.stringify({ value, password, diff --git a/src/utils/index.ts b/src/utils/index.ts index f5352af1..629a0091 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,5 +1,4 @@ -export * from './screenDimensions'; -export * from './statusBarHeight'; +export * from './layouts'; export * from './moments'; export * from './common'; export * from './users'; diff --git a/src/utils/layouts.ts b/src/utils/layouts.ts new file mode 100644 index 00000000..e2f1f0b1 --- /dev/null +++ b/src/utils/layouts.ts @@ -0,0 +1,48 @@ +import {PixelRatio, Platform, StatusBar} from 'react-native'; +import {Dimensions} from 'react-native'; + +export const {width: SCREEN_WIDTH, height: SCREEN_HEIGHT} = Dimensions.get( + 'window', +); + +export const SCREEN_RATIO = SCREEN_HEIGHT / SCREEN_WIDTH; + +/** + * Working as of Q1 2021, latest iPhone is 12 + * iPhone 8/SE has a logical screen ratio of about 1.77 + * Rest has a logical screen ratio of about 2.16 + */ +export const isIPhoneX = () => + Platform.OS === 'ios' && !Platform.isPad && !Platform.isTVOS + ? SCREEN_RATIO > 2 + : false; + +// Taken from: https://github.com/react-navigation/react-navigation/issues/283 +export const HeaderHeight = Platform.select({ + ios: 44, + android: 56, + default: 64, +}); + +export const StatusBarHeight = Platform.select({ + ios: isIPhoneX() ? 44 : 20, + android: StatusBar.currentHeight, + default: 0, +}); + +export const AvatarHeaderHeight = (HeaderHeight + StatusBarHeight) * 1.3; + +/** + * This is a function for normalizing the font size for different devices, based on iphone 8. + * + * E.g. font size 13 on an iphone 8 is 13, but on an iPhone 11 is + * 14.5 + */ +export const normalize = (fontSize: number) => { + // based on iphone 8 logical screen width + const scale = SCREEN_WIDTH / 375; + let newSize = fontSize * scale; + // round to the nearest 0.5 + newSize = Math.round(PixelRatio.roundToNearestPixel(newSize) * 2) / 2; + return newSize; +}; diff --git a/src/utils/screenDimensions.ts b/src/utils/screenDimensions.ts deleted file mode 100644 index 56277ddc..00000000 --- a/src/utils/screenDimensions.ts +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index b8eb7b33..00000000 --- a/src/utils/statusBarHeight.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Platform, StatusBar} from 'react-native'; -import {SCREEN_HEIGHT, SCREEN_WIDTH} from './screenDimensions'; - -const X_WIDTH = 375; -const X_HEIGHT = 812; -const XSMAX_WIDTH = 414; -const XSMAX_HEIGHT = 896; - -export 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; - -// Taken from: https://github.com/react-navigation/react-navigation/issues/283 -export const HeaderHeight = Platform.select({ - ios: 44, - android: 56, - default: 64, -}); - -export const StatusBarHeight = Platform.select({ - ios: isIPhoneX() ? 44 : 20, - android: StatusBar.currentHeight, - default: 0, -}); - -export const AvatarHeaderHeight = (HeaderHeight + StatusBarHeight) * 1.3; |