diff options
Diffstat (limited to 'src/screens')
-rw-r--r-- | src/screens/onboarding/OnboardingStepOne.tsx | 22 | ||||
-rw-r--r-- | src/screens/onboarding/OnboardingStepTwo.tsx | 42 | ||||
-rw-r--r-- | src/screens/profile/AccountType.tsx | 124 | ||||
-rw-r--r-- | src/screens/profile/MomentUploadPromptScreen.tsx | 55 | ||||
-rw-r--r-- | src/screens/profile/PrivacyScreen.tsx | 49 | ||||
-rw-r--r-- | src/screens/profile/SettingsCell.tsx | 146 | ||||
-rw-r--r-- | src/screens/profile/SettingsScreen.tsx | 87 | ||||
-rw-r--r-- | src/screens/profile/index.ts | 3 | ||||
-rw-r--r-- | src/screens/suggestedPeople/SuggestedPeopleScreen.tsx | 8 |
9 files changed, 492 insertions, 44 deletions
diff --git a/src/screens/onboarding/OnboardingStepOne.tsx b/src/screens/onboarding/OnboardingStepOne.tsx index 0fa7a6a5..618bc39b 100644 --- a/src/screens/onboarding/OnboardingStepOne.tsx +++ b/src/screens/onboarding/OnboardingStepOne.tsx @@ -108,15 +108,18 @@ const OnboardingStepOne: React.FC<OnboardingStepOneProps> = ({navigation}) => { }); } try { - if (form.isValidFname && form.isValidLname && form.isValidPhone) { - const code = await sendOtpStatusCode(form.phone); + const {isValidFname, isValidLname, isValidPhone} = form; + if (isValidFname && isValidLname && isValidPhone) { + const {phone} = form; + const code = await sendOtpStatusCode(phone); if (code) { switch (code) { case 200: + const {fname, lname} = form; navigation.navigate('PhoneVerification', { - firstName: form.fname, - lastName: form.lname, - phone: form.phone, + firstName: fname, + lastName: lname, + phone, }); break; case 409: @@ -157,7 +160,14 @@ const OnboardingStepOne: React.FC<OnboardingStepOneProps> = ({navigation}) => { </TouchableOpacity> </View> ), - [form.isValidFname, form.isValidLname, form.isValidPhone], + [ + form.fname, + form.lname, + form.phone, + form.isValidFname, + form.isValidLname, + form.isValidPhone, + ], ); return ( diff --git a/src/screens/onboarding/OnboardingStepTwo.tsx b/src/screens/onboarding/OnboardingStepTwo.tsx index e79e1886..a1100827 100644 --- a/src/screens/onboarding/OnboardingStepTwo.tsx +++ b/src/screens/onboarding/OnboardingStepTwo.tsx @@ -143,30 +143,40 @@ const OnboardingStepTwo: React.FC<OnboardingStepTwoProps> = ({ attemptedSubmit: true, }); } + const { + isValidEmail, + isValidUsername, + isValidPassword, + passwordsMatch, + tcAccepted, + } = form; try { if ( - form.isValidEmail && - form.isValidUsername && - form.isValidPassword && - form.passwordsMatch + isValidEmail && + isValidUsername && + isValidPassword && + passwordsMatch ) { - if (form.tcAccepted) { + if (tcAccepted) { + const {email, username, password} = form; + const {firstName, lastName, phone} = route.params; const response = await sendRegister( - route.params.firstName, - route.params.lastName, - route.params.phone, - form.email, - form.username, - form.password, + firstName, + lastName, + phone, + email, + username, + password, ); if (response) { const data = await response.json(); + const {token, UserID, username} = data; switch (response.status) { case 201: - await AsyncStorage.setItem('token', data.token); + await AsyncStorage.setItem('token', token); navigation.navigate('OnboardingStepThree', { - userId: data.UserID, - username: form.username, + userId: UserID, + username: username, }); break; case 400: @@ -224,6 +234,10 @@ const OnboardingStepTwo: React.FC<OnboardingStepTwoProps> = ({ </View> ), [ + form.email, + form.username, + form.password, + form.confirm, form.isValidEmail, form.isValidUsername, form.isValidPassword, diff --git a/src/screens/profile/AccountType.tsx b/src/screens/profile/AccountType.tsx new file mode 100644 index 00000000..60ed0668 --- /dev/null +++ b/src/screens/profile/AccountType.tsx @@ -0,0 +1,124 @@ +import React, {useState} from 'react'; +import { + ActivityIndicator, + StatusBar, + StyleSheet, + Switch, + Text, + View, +} from 'react-native'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import {useDispatch, useSelector} from 'react-redux'; +import {Background} from '../../components'; +import {updateProfileVisibility} from '../../services'; +import {NO_PROFILE} from '../../store/initialStates'; +import {RootState} from '../../store/rootReducer'; +import {BackgroundGradientType} from '../../types'; +import {getTokenOrLogout} from '../../utils'; +import {normalize} from '../../utils/layouts'; + +const AccountType: React.FC = () => { + const [isPrivateAccount, setIsPrivateAccount] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const { + user: {userId, username}, + profile: {is_private} = NO_PROFILE, + } = useSelector((state: RootState) => state.user); + + const dispatch = useDispatch(); + + const updateAccountVisibility = async () => { + setIsLoading(true); + const isPrivate = !isPrivateAccount; + setIsPrivateAccount(isPrivate); + const token = await getTokenOrLogout(dispatch); + await updateProfileVisibility( + token, + {userId, username}, + isPrivate, + dispatch, + ); + setIsLoading(false); + }; + + const getAccountText = () => { + return is_private ? 'Private Account' : 'Public Account'; + }; + + return ( + <> + <StatusBar barStyle="light-content" /> + <Background gradientType={BackgroundGradientType.Light}> + <SafeAreaView> + <View style={styles.container}> + <View style={styles.switchContainerStyle}> + <Text style={styles.title}>{getAccountText()}</Text> + <ActivityIndicator + animating={isLoading} + size="small" + color="white" + /> + {!isLoading && ( + <Switch + trackColor={{false: 'red', true: '#6EE7E7'}} + thumbColor={'white'} + ios_backgroundColor="transparent" + style={styles.switchStyles} + value={is_private} + onValueChange={updateAccountVisibility} + /> + )} + </View> + + <View style={styles.detailContainerStyle}> + <Text style={styles.detailTitleStyle}> + Enabling a public account will: + </Text> + <Text style={styles.detailContentStyle}> + {'\n'}Everyone can view my posts{'\n'} + {'\n'}Everyone can send me friend requests{'\n'} + {'\n'}Everyone can tagg me{'\n'} + {'\n'}Everyone can send me direct messages + </Text> + </View> + </View> + </SafeAreaView> + </Background> + </> + ); +}; + +const styles = StyleSheet.create({ + container: {marginHorizontal: '8%', marginTop: '20%'}, + title: { + alignSelf: 'center', + fontSize: normalize(18), + fontWeight: '600', + lineHeight: normalize(17.9), + color: 'white', + }, + switchContainerStyle: { + flexDirection: 'row', + alignContent: 'center', + justifyContent: 'space-between', + }, + detailContainerStyle: {marginTop: '40%'}, + detailTitleStyle: { + fontSize: normalize(19), + fontWeight: '700', + lineHeight: normalize(22.67), + color: 'white', + }, + detailContentStyle: { + fontSize: normalize(14), + fontWeight: '600', + lineHeight: normalize(16.71), + color: 'white', + }, + switchStyles: { + borderWidth: 2, + borderColor: 'white', + }, +}); + +export default AccountType; diff --git a/src/screens/profile/MomentUploadPromptScreen.tsx b/src/screens/profile/MomentUploadPromptScreen.tsx index 9d46c1e9..f79c81b4 100644 --- a/src/screens/profile/MomentUploadPromptScreen.tsx +++ b/src/screens/profile/MomentUploadPromptScreen.tsx @@ -7,6 +7,8 @@ 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'; +import {PROFILE_CUTOUT_BOTTOM_Y} from '../../constants'; +import {isIPhoneX, normalize} from '../../utils'; type MomentUploadPromptScreenRouteProp = RouteProp< MainStackParams, @@ -26,12 +28,10 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({ route, navigation, }) => { - const {screenType, momentCategory} = route.params; + const {screenType, momentCategory, profileBodyHeight} = route.params; return ( <View style={styles.container}> <CloseIcon - height={'10%'} - width={'10%'} color={'white'} style={styles.closeButton} onPress={() => { @@ -42,7 +42,11 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({ <Text style={styles.text}>{UPLOAD_MOMENT_PROMPT_ONE_MESSAGE}</Text> <Image source={require('../../assets/gifs/dotted-arrow-white.gif')} - style={styles.arrowGif} + style={[ + StyleSheet.absoluteFill, + styles.arrowGif, + {top: profileBodyHeight + PROFILE_CUTOUT_BOTTOM_Y}, + ]} /> <Moment key={1} @@ -55,7 +59,12 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({ showDownButton={false} showUpButton={false} externalStyles={{ - container: styles.momentContainer, + container: { + ...styles.momentContainer, + top: isIPhoneX() + ? profileBodyHeight + 615 + : profileBodyHeight + 500, + }, titleText: styles.momentHeaderText, header: styles.momentHeader, scrollContainer: styles.momentScrollContainer, @@ -67,32 +76,28 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({ const styles = StyleSheet.create({ container: { + flex: 1, flexDirection: 'column', - justifyContent: 'center', }, closeButton: { - position: 'relative', - height: '48%', - aspectRatio: 1, - top: 20, + ...StyleSheet.absoluteFillObject, + top: 45, + left: 20, + width: 40, + height: 40, }, text: { - justifyContent: 'center', + marginTop: 250, color: '#fff', fontWeight: 'bold', - fontSize: 20, + fontSize: normalize(20), textAlign: 'center', - position: 'relative', - top: '40%', }, arrowGif: { - position: 'relative', - width: '25%', - height: '40%', - left: '40%', - aspectRatio: 1.2, - top: '50%', - transform: [{scaleX: -1}, {rotate: '15deg'}], + width: 200, + height: 150, + left: 120, + transform: [{rotate: '350deg'}, {rotateY: '180deg'}], }, //Styles to adjust moment container @@ -100,14 +105,18 @@ const styles = StyleSheet.create({ backgroundColor: 'transparent', }, momentContainer: { - top: '62%', + ...StyleSheet.absoluteFillObject, backgroundColor: 'transparent', + height: 170, }, momentHeaderText: { - paddingBottom: '5%', + ...StyleSheet.absoluteFillObject, + marginLeft: 12, + marginTop: 10, }, momentHeader: { backgroundColor: 'transparent', + paddingVertical: 20, }, }); diff --git a/src/screens/profile/PrivacyScreen.tsx b/src/screens/profile/PrivacyScreen.tsx new file mode 100644 index 00000000..17872e24 --- /dev/null +++ b/src/screens/profile/PrivacyScreen.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { + SectionList, + StatusBar, + StyleSheet, + View, + SafeAreaView, +} from 'react-native'; +import {useSelector} from 'react-redux'; +import {RootState} from 'src/store/rootReducer'; +import {Background} from '../../components'; +import {NO_PROFILE} from '../../store/initialStates'; +import {BackgroundGradientType} from '../../types'; +import {SCREEN_HEIGHT} from '../../utils/layouts'; +import SettingsCell from './SettingsCell'; +import {SETTINGS_DATA} from '../../constants/constants'; + +const PrivacyScreen: React.FC = () => { + const {profile: {is_private} = NO_PROFILE} = useSelector( + (state: RootState) => state.user, + ); + + return ( + <> + <StatusBar barStyle="light-content" /> + <Background gradientType={BackgroundGradientType.Light}> + <SafeAreaView> + <View style={styles.container}> + <SectionList + sections={SETTINGS_DATA.PrivacyScreen} + keyExtractor={(item, index) => item.title + index} + renderItem={({item: {title, preimage, postimage}}) => ( + <SettingsCell + {...{title, preimage, postimage, isPrivate: is_private}} + /> + )} + /> + </View> + </SafeAreaView> + </Background> + </> + ); +}; + +const styles = StyleSheet.create({ + container: {height: SCREEN_HEIGHT, marginHorizontal: '8%', marginTop: '8%'}, +}); + +export default PrivacyScreen; diff --git a/src/screens/profile/SettingsCell.tsx b/src/screens/profile/SettingsCell.tsx new file mode 100644 index 00000000..f5360242 --- /dev/null +++ b/src/screens/profile/SettingsCell.tsx @@ -0,0 +1,146 @@ +import {useNavigation} from '@react-navigation/core'; +import React from 'react'; +import { + Alert, + Image, + Linking, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import InAppBrowser from 'react-native-inappbrowser-reborn'; +import {TAGG_PURPLE} from '../../constants'; +import {COMMUNITY_GUIDELINES, PRIVACY_POLICY} from '../../constants/api'; +import {ERROR_ATTEMPT_EDIT_SP} from '../../constants/strings'; +import {normalize, SCREEN_WIDTH} from '../../utils/layouts'; + +type SettingsCellProps = { + title: string; + preimage: number; + postimage: number; + isPrivate?: boolean; + suggested_people_linked?: number; +}; + +const SettingsCell: React.FC<SettingsCellProps> = ({ + title, + preimage, + postimage, + isPrivate, + suggested_people_linked, +}) => { + const navigation = useNavigation(); + const goToUpdateSPProfile = () => { + if (suggested_people_linked === 0) { + Alert.alert(ERROR_ATTEMPT_EDIT_SP); + } else { + // Sending undefined for updatedSelectedBadges to mark that there was no update yet + navigateTo('UpdateSPPicture', { + editing: true, + }); + } + }; + const getActions = (type: string) => { + switch (type) { + case 'Account Type': + navigateTo('AccountTypeScreen', {}); + break; + case 'Blocked Accounts': + //TODO: + break; + case 'Suggested People Profile': + goToUpdateSPProfile(); + break; + case 'Privacy': + navigateTo('PrivacyScreen', {}); + break; + case 'Community Guidelines': + openTaggLink(COMMUNITY_GUIDELINES); + break; + case 'Privacy Policy': + openTaggLink(PRIVACY_POLICY); + break; + default: + break; + } + }; + + const openTaggLink = async (url: string) => { + try { + if (await InAppBrowser.isAvailable()) { + await InAppBrowser.open(url, { + dismissButtonStyle: 'cancel', + preferredBarTintColor: TAGG_PURPLE, + preferredControlTintColor: 'white', + animated: true, + modalPresentationStyle: 'fullScreen', + modalTransitionStyle: 'coverVertical', + modalEnabled: true, + enableBarCollapsing: false, + animations: { + startEnter: 'slide_in_right', + startExit: 'slide_out_left', + endEnter: 'slide_in_left', + endExit: 'slide_out_right', + }, + }); + } else Linking.openURL(url); + } catch (error) { + Alert.alert(error.message); + } + }; + + const navigateTo = (screen: string, options: object) => { + navigation.navigate(screen, options); + }; + return ( + <TouchableOpacity + onPress={() => getActions(title)} + style={styles.itemStyles}> + <Image + resizeMode={'cover'} + style={styles.preImageStyles} + source={preimage} + /> + <View style={styles.titleContainerStyles}> + <Text style={styles.titleStyles}>{title}</Text> + </View> + <View style={[styles.itemStyles, styles.subItemStyles]}> + {title === 'Account Type' && ( + <Text style={[styles.titleStyles, styles.subtitleStyles]}> + {isPrivate ? 'Private' : 'Public'} + </Text> + )} + <Image style={styles.postImageStyles} source={postimage} /> + </View> + </TouchableOpacity> + ); +}; + +const styles = StyleSheet.create({ + container: {marginHorizontal: '8%'}, + itemStyles: { + marginTop: 36, + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + }, + subItemStyles: {position: 'absolute', right: 0}, + preImageStyles: {width: SCREEN_WIDTH * 0.05, height: SCREEN_WIDTH * 0.05}, + postImageStyles: {width: 15, height: 15}, + titleContainerStyles: {marginLeft: '12%'}, + titleStyles: { + fontSize: normalize(15), + fontWeight: '600', + lineHeight: normalize(17.9), + color: 'white', + }, + subtitleStyles: {color: '#C4C4C4', marginRight: 13}, + tc: { + marginVertical: '5%', + top: '8%', + }, +}); + +export default SettingsCell; diff --git a/src/screens/profile/SettingsScreen.tsx b/src/screens/profile/SettingsScreen.tsx new file mode 100644 index 00000000..05e051b5 --- /dev/null +++ b/src/screens/profile/SettingsScreen.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { + SafeAreaView, + SectionList, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import {useDispatch, useSelector} from 'react-redux'; +import {logout} from '../../store/actions'; +import {RootState} from 'src/store/rootReducer'; +import {Background} from '../../components'; +import {SETTINGS_DATA} from '../../constants/constants'; +import {BackgroundGradientType} from '../../types'; +import {normalize, SCREEN_HEIGHT} from '../../utils/layouts'; +import SettingsCell from './SettingsCell'; +import {useNavigation} from '@react-navigation/core'; + +const SettingsScreen: React.FC = () => { + const dispatch = useDispatch(); + const navigation = useNavigation(); + const {suggested_people_linked} = useSelector( + (state: RootState) => state.user.profile, + ); + + return ( + <> + <StatusBar barStyle="light-content" /> + <Background gradientType={BackgroundGradientType.Light}> + <SafeAreaView> + <View style={styles.container}> + <SectionList + stickySectionHeadersEnabled={false} + sections={SETTINGS_DATA.SettingsAndPrivacy} + keyExtractor={(item, index) => item.title + index} + renderItem={({item: {title, preimage, postimage}}) => ( + <SettingsCell + {...{title, preimage, postimage, suggested_people_linked}} + /> + )} + renderSectionHeader={({section: {title}}) => ( + <View style={styles.headerContainerStyles}> + <Text style={styles.headerTextStyles}>{title}</Text> + </View> + )} + ListFooterComponent={() => ( + <TouchableOpacity + style={styles.logoutContainerStyles} + onPress={() => { + dispatch(logout()); + navigation.reset({ + index: 0, + routes: [{name: 'SuggestedPeople'}], + }); + }}> + <Text style={styles.logoutTextStyles}>Logout</Text> + </TouchableOpacity> + )} + /> + </View> + </SafeAreaView> + </Background> + </> + ); +}; + +const styles = StyleSheet.create({ + container: {height: SCREEN_HEIGHT, marginHorizontal: '8%', marginTop: '8%'}, + headerContainerStyles: {marginTop: '14%'}, + headerTextStyles: { + fontSize: normalize(18), + fontWeight: '600', + lineHeight: normalize(21.48), + color: '#E9E9E9', + }, + logoutContainerStyles: {marginTop: '20%', marginLeft: '12%'}, + logoutTextStyles: { + fontSize: normalize(20), + fontWeight: '600', + lineHeight: normalize(23.87), + color: 'white', + }, +}); + +export default SettingsScreen; diff --git a/src/screens/profile/index.ts b/src/screens/profile/index.ts index f74946a6..b7efdd3b 100644 --- a/src/screens/profile/index.ts +++ b/src/screens/profile/index.ts @@ -7,3 +7,6 @@ export {default as FriendsListScreen} from './FriendsListScreen'; export {default as EditProfile} from './EditProfile'; export {default as MomentUploadPromptScreen} from './MomentUploadPromptScreen'; export {default as InviteFriendsScreen} from './InviteFriendsScreen'; +export {default as SettingsScreen} from './SettingsScreen'; +export {default as PrivacyScreen} from './PrivacyScreen'; +export {default as AccountType} from './AccountType'; diff --git a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx index 336e8b35..a296351f 100644 --- a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx +++ b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx @@ -74,12 +74,18 @@ const SuggestedPeopleScreen: React.FC = () => { useEffect(() => { const appendSelf = async () => { - const self = { + const self: SuggestedPeopleDataType = { user: getUserAsProfilePreviewType(state.user.user, state.user.profile), mutual_friends: [], badges: [], social_links: [], suggested_people_url: suggestedPeopleImage, + university: state.user.profile.university, + friendship: { + status: 'no_record', + requester_id: '', + }, + is_private: false, }; people.unshift(self); setPeople(people); |