aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/taggs/Tagg.tsx5
-rw-r--r--src/components/taggs/TaggsBar.tsx11
-rw-r--r--src/screens/profile/EditProfile.tsx348
-rw-r--r--src/services/UserProfileService.ts9
-rw-r--r--src/store/actions/user.ts24
-rw-r--r--src/store/initialStates.ts2
-rw-r--r--src/store/reducers/userReducer.ts18
-rw-r--r--src/types/types.ts2
8 files changed, 253 insertions, 166 deletions
diff --git a/src/components/taggs/Tagg.tsx b/src/components/taggs/Tagg.tsx
index 086b3c87..8158cbac 100644
--- a/src/components/taggs/Tagg.tsx
+++ b/src/components/taggs/Tagg.tsx
@@ -26,7 +26,7 @@ interface TaggProps {
isLinked: boolean;
isIntegrated: boolean;
setTaggsNeedUpdate: (_: boolean) => void;
- setSocialDataNeedUpdate: (_: string) => void;
+ setSocialDataNeedUpdate: (social: string, username: string) => void;
userXId: string;
screenType: ScreenType;
}
@@ -84,7 +84,7 @@ const Tagg: React.FC<TaggProps> = ({
if (isIntegrated) {
handlePressForAuthBrowser(social).then((success) => {
setTaggsNeedUpdate(success);
- if (success) setSocialDataNeedUpdate(social);
+ if (success) setSocialDataNeedUpdate(social, '');
});
} else {
setModalVisible(true);
@@ -112,6 +112,7 @@ const Tagg: React.FC<TaggProps> = ({
if (await registerNonIntegratedSocialLink(social, username)) {
Alert.alert(`Successfully linked ${social} 🎉`);
setTaggsNeedUpdate(true);
+ setSocialDataNeedUpdate(social, username);
} else {
// If we display too fast the alert will get dismissed with the modal
setTimeout(() => {
diff --git a/src/components/taggs/TaggsBar.tsx b/src/components/taggs/TaggsBar.tsx
index 12e4b93a..082743d0 100644
--- a/src/components/taggs/TaggsBar.tsx
+++ b/src/components/taggs/TaggsBar.tsx
@@ -12,7 +12,7 @@ import {StatusBarHeight} from '../../utils';
import Tagg from './Tagg';
import {RootState} from '../../store/rootReducer';
import {ScreenType} from '../../types';
-import {loadIndividualSocial} from '../../store/actions';
+import {loadIndividualSocial, updateSocial} from '../../store/actions';
const {View, ScrollView, interpolate, Extrapolate} = Animated;
interface TaggsBarProps {
@@ -38,10 +38,15 @@ const TaggsBar: React.FC<TaggsBarProps> = ({
/**
* Updates the individual social that needs update
+ * If username is empty, update nonintegrated socials like Snapchat and TikTok
* @param socialType Type of the social that needs update
*/
- const handleSocialUpdate = (socialType: string) => {
- dispatch(loadIndividualSocial(user.userId, socialType));
+ const handleSocialUpdate = (socialType: string, username: string) => {
+ if (username !== '') {
+ dispatch(updateSocial(socialType, username));
+ } else {
+ dispatch(loadIndividualSocial(user.userId, socialType));
+ }
};
/**
diff --git a/src/screens/profile/EditProfile.tsx b/src/screens/profile/EditProfile.tsx
index ab58db41..97c58177 100644
--- a/src/screens/profile/EditProfile.tsx
+++ b/src/screens/profile/EditProfile.tsx
@@ -11,6 +11,9 @@ import {
Alert,
View,
SafeAreaView,
+ KeyboardAvoidingView,
+ Platform,
+ Keyboard,
} from 'react-native';
import {Button} from 'react-native-elements';
import {
@@ -21,7 +24,6 @@ import {
BirthDatePicker,
TabsGradient,
} from '../../components';
-import {OnboardingStackParams} from '../../routes/onboarding';
import ImagePicker from 'react-native-image-crop-picker';
import {
EDIT_PROFILE_ENDPOINT,
@@ -30,23 +32,21 @@ import {
genderRegex,
} from '../../constants';
import AsyncStorage from '@react-native-community/async-storage';
+import {ProfileStackParams} from '../../routes';
import Animated from 'react-native-reanimated';
import {HeaderHeight, SCREEN_HEIGHT} from '../../utils';
import {RootState} from '../../store/rootReducer';
import {useDispatch, useSelector} from 'react-redux';
import {loadUserData} from '../../store/actions';
-type ProfileOnboardingScreenRouteProp = RouteProp<
- OnboardingStackParams,
- 'ProfileOnboarding'
+type EditProfileNavigationProp = StackNavigationProp<
+ ProfileStackParams,
+ 'EditProfile'
>;
-type ProfileOnboardingScreenNavigationProp = StackNavigationProp<
- OnboardingStackParams,
- 'ProfileOnboarding'
->;
-interface ProfileOnboardingProps {
- route: ProfileOnboardingScreenRouteProp;
- navigation: ProfileOnboardingScreenNavigationProp;
+
+interface EditProfileProps {
+ route: RouteProp<ProfileStackParams, 'EditProfile'>;
+ navigation: EditProfileNavigationProp;
}
/**
@@ -54,14 +54,11 @@ interface ProfileOnboardingProps {
* @param navigation react-navigation navigation object
*/
-const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
- route,
- navigation,
-}) => {
+const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => {
const y: Animated.Value<number> = Animated.useValue(0);
const {userId, username} = route.params;
const {
- profile: {website, biography, birthday, gender},
+ profile: {website, biography, birthday, gender, snapchat, tiktok},
avatar,
cover,
} = useSelector((state: RootState) => state.user);
@@ -73,7 +70,8 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
if (needsUpdate) {
dispatch(loadUserData({userId, username}));
}
- }, [loadUserData, needsUpdate]);
+
+ }, [dispatch, needsUpdate, userId, username]);
const [isCustomGender, setIsCustomGender] = React.useState<boolean>(
gender !== '' && gender !== 'female' && gender !== 'male',
@@ -87,42 +85,15 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
birthdate: birthday && moment(birthday).format('YYYY-MM-DD'),
gender: isCustomGender ? 'custom' : gender,
customGenderText: isCustomGender ? gender : '',
+ snapchat: snapchat,
+ tiktok: tiktok,
isValidWebsite: true,
isValidBio: true,
isValidGender: true,
+ isValidSnapchat: true,
+ isValidTiktok: true,
attemptedSubmit: false,
});
- // refs for changing focus
- const bioRef = React.useRef();
- const birthdateRef = React.useRef();
- const genderRef = React.useRef();
- const customGenderRef = React.useRef();
- /**
- * Handles focus change to the next input field.
- * @param field key for field to move focus onto
- */
- const handleFocusChange = (field: string): void => {
- switch (field) {
- case 'bio':
- const bioField: any = bioRef.current;
- bioField.focus();
- break;
- case 'birthdate':
- const birthdateField: any = birthdateRef.current;
- birthdateField.focus();
- break;
- case 'gender':
- const genderField: any = genderRef.current;
- genderField.focus();
- break;
- case 'customGender':
- const customGenderField: any = customGenderRef.current;
- customGenderField.focus();
- break;
- default:
- return;
- }
- };
/**
* Profile screen "Add header image" button
@@ -256,6 +227,24 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
});
};
+ const handleSnapchatUpdate = (newUsername: string) => {
+ // Allow any username, empty means to "un-link" it
+ // TODO: refresh taggs bar after
+ setForm({
+ ...form,
+ snapchat: newUsername,
+ });
+ };
+
+ const handleTikTokUpdate = (newUsername: string) => {
+ // Allow any username, empty means to "un-link" it
+ // TODO: refresh taggs bar after
+ setForm({
+ ...form,
+ tiktok: newUsername,
+ });
+ };
+
const handleSubmit = useCallback(async () => {
if (!form.largePic) {
Alert.alert('Please select a Header image!');
@@ -325,6 +314,22 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
}
}
+ if (form.isValidSnapchat) {
+ request.append('snapchat', form.snapchat);
+ } else {
+ setForm({...form, attemptedSubmit: false});
+ setTimeout(() => setForm({...form, attemptedSubmit: true}));
+ invalidFields = true;
+ }
+
+ if (form.isValidTiktok) {
+ request.append('tiktok', form.tiktok);
+ } else {
+ setForm({...form, attemptedSubmit: false});
+ setTimeout(() => setForm({...form, attemptedSubmit: true}));
+ invalidFields = true;
+ }
+
if (invalidFields) {
return;
}
@@ -357,10 +362,7 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
);
}
} catch (error) {
- Alert.alert(
- 'Profile creation failed 😓',
- 'Please double-check your network connection and retry.',
- );
+ Alert.alert('Please double-check your network connection and retry.');
return {
name: 'Profile creation error',
description: error,
@@ -384,118 +386,158 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
return (
<Background centered>
<SafeAreaView>
- <Animated.ScrollView
- style={styles.container}
- onScroll={(e) => y.setValue(e.nativeEvent.contentOffset.y)}
- showsHorizontalScrollIndicator={false}
- showsVerticalScrollIndicator={true}
- scrollEventThrottle={1}
- alwaysBounceVertical
- contentContainerStyle={{paddingBottom: SCREEN_HEIGHT / 15}}>
- <StatusBar barStyle="light-content" translucent={false} />
- <View
- style={{
- position: 'relative',
- alignSelf: 'center',
- }}>
- <View>
- <View style={styles.profile}>
- <LargeProfilePic />
- <SmallProfilePic />
- </View>
- <View
- style={{
- position: 'relative',
- width: 280,
- alignSelf: 'center',
- }}>
- <TaggInput
- accessibilityHint="Enter a website."
- accessibilityLabel="Website input field."
- placeholder="Website"
- autoCompleteType="off"
- textContentType="URL"
- autoCapitalize="none"
- returnKeyType="next"
- onChangeText={handleWebsiteUpdate}
- onSubmitEditing={() => handleFocusChange('bio')}
- blurOnSubmit={false}
- valid={form.isValidWebsite}
- attemptedSubmit={form.attemptedSubmit}
- invalidWarning={
- 'Website must be a valid link to your website'
- }
- width={280}
- value={form.website}
- />
- <TaggBigInput
- accessibilityHint="Enter a bio."
- accessibilityLabel="Bio input field."
- placeholder="Bio"
- autoCompleteType="off"
- textContentType="none"
- autoCapitalize="none"
- returnKeyType="next"
- onChangeText={handleBioUpdate}
- onSubmitEditing={() => handleFocusChange('bio')}
- blurOnSubmit={false}
- ref={bioRef}
- valid={form.isValidBio}
- attemptedSubmit={form.attemptedSubmit}
- invalidWarning={
- 'Bio must be less than 150 characters and must contain valid characters'
- }
- width={280}
- value={form.bio}
- />
- <BirthDatePicker
- ref={birthdateRef}
- handleBDUpdate={handleBirthdateUpdate}
- width={280}
- date={form.birthdate}
- showPresetdate={true}
- />
-
- <TaggDropDown
- value={form.gender}
- onValueChange={(value: string) => handleGenderUpdate(value)}
- items={[
- {label: 'Male', value: 'male'},
- {label: 'Female', value: 'female'},
- {label: 'Custom', value: 'custom'},
- ]}
- placeholder={{
- label: 'Gender',
- value: null,
- color: '#fff',
- }}
- />
- {isCustomGender && (
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
+ <Animated.ScrollView
+ style={styles.container}
+ onScroll={(e) => y.setValue(e.nativeEvent.contentOffset.y)}
+ showsHorizontalScrollIndicator={false}
+ showsVerticalScrollIndicator={false}
+ scrollEventThrottle={1}
+ alwaysBounceVertical
+ contentContainerStyle={{paddingBottom: SCREEN_HEIGHT / 15}}>
+ <StatusBar barStyle="light-content" translucent={false} />
+ <View
+ style={{
+ position: 'relative',
+ alignSelf: 'center',
+ }}>
+ <View>
+ <View style={styles.profile}>
+ <LargeProfilePic />
+ <SmallProfilePic />
+ </View>
+ <View
+ style={{
+ position: 'relative',
+ width: 280,
+ alignSelf: 'center',
+ }}>
<TaggInput
- style={styles.customGenderInput}
- value={form.customGenderText}
- accessibilityHint="Custom"
- accessibilityLabel="Gender input field."
- placeholder="Enter your gender"
+ accessibilityHint="Enter a website."
+ accessibilityLabel="Website input field."
+ placeholder="Website"
+ autoCompleteType="off"
+ textContentType="URL"
+ autoCapitalize="none"
+ returnKeyType="done"
+ onChangeText={handleWebsiteUpdate}
+ blurOnSubmit={false}
+ valid={form.isValidWebsite}
+ attemptedSubmit={form.attemptedSubmit}
+ invalidWarning={
+ 'Website must be a valid link to your website'
+ }
+ width={280}
+ value={form.website}
+ />
+ <TaggBigInput
+ accessibilityHint="Enter a bio."
+ accessibilityLabel="Bio input field."
+ placeholder="Bio"
autoCompleteType="off"
textContentType="none"
autoCapitalize="none"
returnKeyType="next"
+ onChangeText={handleBioUpdate}
blurOnSubmit={false}
- ref={customGenderRef}
- onChangeText={handleCustomGenderUpdate}
- onSubmitEditing={() => handleSubmit()}
- valid={form.isValidGender}
+ valid={form.isValidBio}
attemptedSubmit={form.attemptedSubmit}
invalidWarning={
- 'Custom field can only contain letters and hyphens'
+ 'Bio must be less than 150 characters and must contain valid characters'
}
+ width={280}
+ value={form.bio}
+ />
+ <BirthDatePicker
+ handleBDUpdate={handleBirthdateUpdate}
+ width={280}
+ date={moment(form.birthdate).toDate()}
+ showPresetdate={true}
+ />
+
+ <TaggDropDown
+ value={form.gender}
+ onValueChange={(value: string) => handleGenderUpdate(value)}
+ items={[
+ {label: 'Male', value: 'male'},
+ {label: 'Female', value: 'female'},
+ {label: 'Custom', value: 'custom'},
+ ]}
+ placeholder={{
+ label: 'Gender',
+ value: null,
+ color: '#fff',
+ }}
/>
- )}
+ {isCustomGender && (
+ <TaggInput
+ accessibilityHint="Custom"
+ accessibilityLabel="Gender input field."
+ attemptedSubmit={form.attemptedSubmit}
+ autoCapitalize="none"
+ autoCompleteType="off"
+ blurOnSubmit={false}
+ invalidWarning={
+ 'Custom field can only contain letters and hyphens'
+ }
+ onChangeText={handleCustomGenderUpdate}
+ onSubmitEditing={() => handleSubmit()}
+ placeholder="Enter your gender"
+ returnKeyType="done"
+ style={styles.customGenderInput}
+ textContentType="none"
+ valid={form.isValidGender}
+ value={form.customGenderText}
+ />
+ )}
+ {snapchat !== '' && (
+ <TaggInput
+ accessibilityHint="Snapchat Username"
+ accessibilityLabel="Snapchat Username Input Field."
+ attemptedSubmit={form.attemptedSubmit}
+ autoCapitalize="none"
+ autoCompleteType="off"
+ autoCorrect={false}
+ blurOnSubmit={false}
+ enablesReturnKeyAutomatically={true}
+ invalidWarning={'Please enter something!'}
+ onChangeText={handleSnapchatUpdate}
+ onSubmitEditing={Keyboard.dismiss}
+ placeholder="Snapchat Username"
+ returnKeyType="done"
+ textContentType="none"
+ valid={form.isValidSnapchat}
+ value={form.snapchat && form.snapchat}
+ width={280}
+ />
+ )}
+ {tiktok !== '' && (
+ <TaggInput
+ accessibilityHint="TikTok Username"
+ accessibilityLabel="TikTok Username Input Field."
+ attemptedSubmit={form.attemptedSubmit}
+ autoCapitalize="none"
+ autoCompleteType="off"
+ autoCorrect={false}
+ blurOnSubmit={false}
+ enablesReturnKeyAutomatically={true}
+ invalidWarning={'Please enter something!'}
+ onChangeText={handleTikTokUpdate}
+ onSubmitEditing={Keyboard.dismiss}
+ placeholder="TikTok Username"
+ returnKeyType="done"
+ textContentType="none"
+ valid={form.isValidTiktok}
+ value={form.tiktok}
+ width={280}
+ />
+ )}
+ </View>
</View>
</View>
- </View>
- </Animated.ScrollView>
+ </Animated.ScrollView>
+ </KeyboardAvoidingView>
</SafeAreaView>
<TabsGradient />
</Background>
@@ -585,4 +627,4 @@ const styles = StyleSheet.create({
},
});
-export default ProfileOnboarding;
+export default EditProfile;
diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts
index e69e4103..c8dbcdd1 100644
--- a/src/services/UserProfileService.ts
+++ b/src/services/UserProfileService.ts
@@ -1,10 +1,8 @@
-//Abstracted common profile api calls out here
-
import AsyncStorage from '@react-native-community/async-storage';
import moment from 'moment';
import {Alert} from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
-import {SocialAccountType, MomentType} from 'src/types';
+import {SocialAccountType} from 'src/types';
import {
AVATAR_PHOTO_ENDPOINT,
COVER_PHOTO_ENDPOINT,
@@ -25,10 +23,9 @@ export const loadProfileInfo = async (token: string, userId: string) => {
const status = response.status;
if (status === 200) {
const info = await response.json();
- let {name, biography, website, birthday, gender} = info;
- // user should always have a birthday, but a safety check here
+ let {name, biography, website, birthday, gender, snapchat, tiktok} = info;
birthday = birthday && moment(birthday).format('YYYY-MM-DD');
- return {name, biography, website, birthday, gender};
+ return {name, biography, website, birthday, gender, snapchat, tiktok};
}
} catch (error) {
Alert.alert(
diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts
index 09ec8abf..e77b8513 100644
--- a/src/store/actions/user.ts
+++ b/src/store/actions/user.ts
@@ -2,7 +2,7 @@ import {RootState} from '../rootReducer';
import {UserType} from '../../types/types';
import {loadProfileInfo, loadAvatar, loadCover} from '../../services';
import {Action, ThunkAction} from '@reduxjs/toolkit';
-import {userLoggedIn, userDetailsFetched} from '../reducers';
+import {userLoggedIn, userDetailsFetched, socialEdited} from '../reducers';
import {getTokenOrLogout} from '../../utils';
/**
@@ -38,6 +38,28 @@ export const loadUserData = (
}
};
+/**
+ * To update editable socials
+ * @param social social to be updated
+ * @param value username of social to be updated
+ */
+export const updateSocial = (
+ social: string,
+ value: string,
+): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
+ dispatch,
+) => {
+ try {
+ console.log(social);
+ dispatch({
+ type: socialEdited.type,
+ payload: {social, value},
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
export const logout = (): ThunkAction<
Promise<void>,
RootState,
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 4087b97c..817af86b 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -14,6 +14,8 @@ export const NO_PROFILE: ProfileType = {
name: '',
gender: '',
birthday: undefined,
+ snapchat: '',
+ tiktok: '',
};
export const EMPTY_MOMENTS_LIST = <MomentType[]>[];
diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts
index f43bd0bc..2fd5c462 100644
--- a/src/store/reducers/userReducer.ts
+++ b/src/store/reducers/userReducer.ts
@@ -29,8 +29,24 @@ const userDataSlice = createSlice({
state.avatar = action.payload.avatar;
state.cover = action.payload.cover;
},
+
+ socialEdited: (state, action) => {
+ const {social, value} = action.payload;
+ switch (social) {
+ case 'Snapchat':
+ state.profile.snapchat = value;
+ break;
+ case 'TikTok':
+ state.profile.tiktok = value;
+ break;
+ }
+ },
},
});
-export const {userLoggedIn, userDetailsFetched} = userDataSlice.actions;
+export const {
+ userLoggedIn,
+ userDetailsFetched,
+ socialEdited,
+} = userDataSlice.actions;
export const userDataReducer = userDataSlice.reducer;
diff --git a/src/types/types.ts b/src/types/types.ts
index 51837a91..79f15ae9 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -19,6 +19,8 @@ export interface ProfileType {
website: string;
gender: string;
birthday: Date | undefined;
+ snapchat: string;
+ tiktok: string;
}
export interface SocialAccountType {