diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/images/tiktok-icon.png | bin | 0 -> 5552 bytes | |||
-rw-r--r-- | src/components/common/SocialIcon.tsx | 3 | ||||
-rw-r--r-- | src/components/onboarding/LinkSocialMedia.tsx | 204 | ||||
-rw-r--r-- | src/components/onboarding/RegistrationWizard.tsx | 12 | ||||
-rw-r--r-- | src/components/onboarding/index.ts | 1 | ||||
-rw-r--r-- | src/constants/constants.ts | 6 | ||||
-rw-r--r-- | src/routes/onboarding/Onboarding.tsx | 2 | ||||
-rw-r--r-- | src/routes/onboarding/OnboardingStack.tsx | 1 | ||||
-rw-r--r-- | src/screens/onboarding/Checkpoint.tsx | 11 | ||||
-rw-r--r-- | src/screens/onboarding/ProfileOnboarding.tsx | 26 | ||||
-rw-r--r-- | src/screens/onboarding/SocialMedia.tsx | 180 | ||||
-rw-r--r-- | src/screens/onboarding/index.ts | 1 |
12 files changed, 417 insertions, 30 deletions
diff --git a/src/assets/images/tiktok-icon.png b/src/assets/images/tiktok-icon.png Binary files differnew file mode 100644 index 00000000..c7fa788d --- /dev/null +++ b/src/assets/images/tiktok-icon.png diff --git a/src/components/common/SocialIcon.tsx b/src/components/common/SocialIcon.tsx index 5c1098af..a46b1445 100644 --- a/src/components/common/SocialIcon.tsx +++ b/src/components/common/SocialIcon.tsx @@ -40,6 +40,9 @@ const SocialIcon: React.FC<SocialIconProps> = ({ case 'Youtube': var icon = require('../../assets/images/youtube-icon.png'); break; + case 'TikTok': + var icon = require('../../assets/images/tiktok-icon.png'); + break; default: var icon = require('../../assets/images/logo.png'); break; diff --git a/src/components/onboarding/LinkSocialMedia.tsx b/src/components/onboarding/LinkSocialMedia.tsx new file mode 100644 index 00000000..5f95fac8 --- /dev/null +++ b/src/components/onboarding/LinkSocialMedia.tsx @@ -0,0 +1,204 @@ +/** + * This is a duplicate file, adding this now to avoid conflicts with incoming changes on the original 'SocialMediaLinker' + */ + +import AsyncStorage from '@react-native-community/async-storage'; +import React from 'react'; +import { + Alert, + Image, + StyleSheet, + Text, + TouchableOpacity, + TouchableOpacityProps, +} from 'react-native'; +import InAppBrowser from 'react-native-inappbrowser-reborn'; +import {LinkerType} from 'src/types'; +import { + LINK_FB_ENDPOINT, + LINK_FB_OAUTH, + LINK_IG_ENDPOINT, + LINK_IG_OAUTH, + LINK_TWITTER_ENDPOINT, + LINK_TWITTER_OAUTH, +} from '../../constants'; +import {SOCIAL_FONT_COLORS} from '../../constants/constants'; +import SocialIcon from '../common/SocialIcon'; + +interface SocialMediaLinkerProps extends TouchableOpacityProps { + social: LinkerType; +} + +const SocialMediaLinker: React.FC<SocialMediaLinkerProps> = ({ + social: {label}, +}) => { + const [state, setState] = React.useState({ + authenticated: false, + }); + + const integrated_endpoints: {[label: string]: [string, string]} = { + Instagram: [LINK_IG_OAUTH, LINK_IG_ENDPOINT], + Facebook: [LINK_FB_OAUTH, LINK_FB_ENDPOINT], + Twitter: [LINK_TWITTER_OAUTH, LINK_TWITTER_ENDPOINT], + }; + + const registerSocialLink: (token: string) => Promise<boolean> = async ( + callback_url, + ) => { + if (!(label in integrated_endpoints)) { + // This error is already handled earlier, more of a safety check here + return false; + } + const user_token = await AsyncStorage.getItem('token'); + const response = await fetch(integrated_endpoints[label][1], { + method: 'POST', + headers: { + Authorization: `Token ${user_token}`, + }, + body: JSON.stringify({ + callback_url: callback_url, + }), + }); + if (!(response.status === 201)) { + console.log(await response.json()); + } + return response.status === 201; + }; + + const handlePress = async () => { + try { + const isAvailable = await InAppBrowser.isAvailable(); + if (!(label in integrated_endpoints)) { + // TODO handle non-integrated social links with a modal + // TODO remove the alert below + Alert.alert('Coming soon!'); + return; + } + let url = integrated_endpoints[label][0]; + + // We will need to do an extra step for twitter sign-in + if (label === 'Twitter') { + const user_token = await AsyncStorage.getItem('token'); + const response = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Token ${user_token}`, + }, + }); + url = response.url; + } + + if (isAvailable) { + InAppBrowser.openAuth(url, 'taggid://callback', { + ephemeralWebSession: true, + }) + .then(async (response) => { + console.log(response); + if (response.type === 'success' && response.url) { + const success = await registerSocialLink(response.url); + if (!success) { + throw new Error('Unable to register with backend'); + } + setState({ + ...state, + authenticated: true, + }); + Alert.alert(`Successfully linked ${label} 🎉`); + } else { + throw new Error(`Unable to link with ${label} API`); + } + }) + .catch((error) => { + console.log(error); + Alert.alert(`Something went wrong, we can't link with ${label} 😔`); + }); + } else { + // Okay... to open an external browser and have it link back to + // the app is a bit tricky, we will need to have navigation routes + // setup for this screen and have it hooked up. + // See https://github.com/proyecto26/react-native-inappbrowser#authentication-flow-using-deep-linking + // Though this isn't the end of the world, from the documentation, + // the in-app browser should be supported from iOS 11, which + // is about 98.5% of all iOS devices in the world. + // See https://support.apple.com/en-gb/HT209574 + Alert.alert( + 'Sorry! Your device was unable to open a browser to let you sign-in! 😔', + ); + } + } catch (error) { + console.log(error); + Alert.alert(`Something went wrong, we can't link with ${label} 😔`); + } + }; + + switch (label) { + case 'Instagram': + var font_color = SOCIAL_FONT_COLORS.INSTAGRAM; + break; + case 'Facebook': + var font_color = SOCIAL_FONT_COLORS.FACEBOOK; + break; + case 'Twitter': + var font_color = SOCIAL_FONT_COLORS.TWITTER; + break; + case 'Twitch': + var font_color = SOCIAL_FONT_COLORS.TWITCH; + break; + case 'Pinterest': + var font_color = SOCIAL_FONT_COLORS.PINTEREST; + break; + case 'Whatsapp': + var font_color = SOCIAL_FONT_COLORS.WHATSAPP; + break; + case 'Linkedin': + var font_color = SOCIAL_FONT_COLORS.LINKEDIN; + break; + case 'Snapchat': + var font_color = SOCIAL_FONT_COLORS.SNAPCHAT; + break; + case 'Youtube': + var font_color = SOCIAL_FONT_COLORS.YOUTUBE; + break; + default: + var font_color = '#fff'; + } + + return ( + <TouchableOpacity + activeOpacity={0.7} + onPress={handlePress} + style={styles.container}> + <SocialIcon social={label} style={styles.icon} /> + <Text style={[styles.label, {color: font_color}]}>{label}</Text> + {state.authenticated && ( + <Image + source={require('../../assets/images/link-tick.png')} + style={styles.tick} + /> + )} + </TouchableOpacity> + ); +}; + +const styles = StyleSheet.create({ + container: { + width: '28%', + height: '100%', + backgroundColor: '#4c409a', + borderRadius: 8, + marginHorizontal: '2%', + marginVertical: '2%', + alignItems: 'center', + }, + icon: { + top: '15%', + }, + label: { + fontWeight: '500', + top: '25%', + }, + tick: { + top: '30%', + }, +}); +export default SocialMediaLinker; diff --git a/src/components/onboarding/RegistrationWizard.tsx b/src/components/onboarding/RegistrationWizard.tsx index 8d747b01..437e7cfb 100644 --- a/src/components/onboarding/RegistrationWizard.tsx +++ b/src/components/onboarding/RegistrationWizard.tsx @@ -3,7 +3,7 @@ import {View, StyleSheet, ViewProps, Keyboard} from 'react-native'; import * as Animatable from 'react-native-animatable'; interface RegistrationWizardProps extends ViewProps { - step: 'one' | 'two' | 'three' | 'four' | 'five' | 'six'; + step: 'one' | 'two' | 'three' | 'four' | 'five' | 'six' | 'seven'; } const RegistrationWizard = (props: RegistrationWizardProps) => { @@ -43,6 +43,10 @@ const RegistrationWizard = (props: RegistrationWizardProps) => { <View style={props.step === 'five' ? stepActiveStyle : stepStyle} /> <View style={styles.progress} /> <View style={props.step === 'six' ? stepActiveStyle : stepStyle} /> + <View style={styles.progress} /> + <View + style={props.step === 'seven' ? stepActiveStyle : stepStyle} + /> </View> </Animatable.View> )} @@ -62,6 +66,10 @@ const RegistrationWizard = (props: RegistrationWizardProps) => { <View style={props.step === 'five' ? stepActiveStyle : stepStyle} /> <View style={styles.progress} /> <View style={props.step === 'six' ? stepActiveStyle : stepStyle} /> + <View style={styles.progress} /> + <View + style={props.step === 'seven' ? stepActiveStyle : stepStyle} + /> </View> </Animatable.View> )} @@ -86,7 +94,7 @@ const styles = StyleSheet.create({ backgroundColor: '#e1f0ff', }, progress: { - width: '13%', + width: '10%', height: 2, backgroundColor: '#e1f0ff', }, diff --git a/src/components/onboarding/index.ts b/src/components/onboarding/index.ts index 627412df..aaad7a62 100644 --- a/src/components/onboarding/index.ts +++ b/src/components/onboarding/index.ts @@ -8,3 +8,4 @@ export {default as TaggBigInput} from './TaggBigInput'; export {default as TaggDatePicker} from './TaggDatePicker'; export {default as TaggDropDown} from './TaggDropDown'; export {default as SocialMediaLinker} from './SocialMediaLinker'; +export {default as LinkSocialMedia} from './LinkSocialMedia'; diff --git a/src/constants/constants.ts b/src/constants/constants.ts index 76a0df03..5f341f41 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -16,12 +16,8 @@ export const SOCIAL_LIST: Array<string> = [ 'Instagram', 'Facebook', 'Twitter', - 'Twitch', - 'Pinterest', - 'Whatsapp', - 'Linkedin', + 'TikTok', 'Snapchat', - 'Youtube', ]; export const INSTAGRAM_FONT_COLOR: string = '#FF97DE'; diff --git a/src/routes/onboarding/Onboarding.tsx b/src/routes/onboarding/Onboarding.tsx index 148dbefb..542625e2 100644 --- a/src/routes/onboarding/Onboarding.tsx +++ b/src/routes/onboarding/Onboarding.tsx @@ -10,6 +10,7 @@ import { ProfileOnboarding, Checkpoint, Splash, + SocialMedia, } from '../../screens'; import {StackCardInterpolationProps} from '@react-navigation/stack'; @@ -55,6 +56,7 @@ const Onboarding: React.FC = () => { name="ProfileOnboarding" component={ProfileOnboarding} /> + <OnboardingStack.Screen name="SocialMedia" component={SocialMedia} /> </OnboardingStack.Navigator> ); }; diff --git a/src/routes/onboarding/OnboardingStack.tsx b/src/routes/onboarding/OnboardingStack.tsx index f1cfda39..ab9816b4 100644 --- a/src/routes/onboarding/OnboardingStack.tsx +++ b/src/routes/onboarding/OnboardingStack.tsx @@ -10,6 +10,7 @@ export type OnboardingStackParams = { Checkpoint: {username: string; userId: string}; Verification: {email: string}; ProfileOnboarding: {username: string; userId: string}; + SocialMedia: {username: string; userId: string}; }; export const OnboardingStack = createStackNavigator<OnboardingStackParams>(); diff --git a/src/screens/onboarding/Checkpoint.tsx b/src/screens/onboarding/Checkpoint.tsx index 8ef7f307..aae2293e 100644 --- a/src/screens/onboarding/Checkpoint.tsx +++ b/src/screens/onboarding/Checkpoint.tsx @@ -30,14 +30,11 @@ interface CheckpointProps { const Checkpoint: React.FC<CheckpointProps> = ({route, navigation}) => { const {userId, username} = route.params; - /** - * login: determines if user successully created an account to - * navigate to home and display main tab navigation bar - */ - const {login} = React.useContext(AuthContext); - const handleSkip = () => { - login(userId, username); + navigation.navigate('SocialMedia', { + userId: userId, + username: username, + }); }; const handleProceed = () => { diff --git a/src/screens/onboarding/ProfileOnboarding.tsx b/src/screens/onboarding/ProfileOnboarding.tsx index e13ccc5a..7e489d6d 100644 --- a/src/screens/onboarding/ProfileOnboarding.tsx +++ b/src/screens/onboarding/ProfileOnboarding.tsx @@ -47,7 +47,10 @@ interface ProfileOnboardingProps { * @param navigation react-navigation navigation object */ -const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({route}) => { +const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({ + route, + navigation, +}) => { const {userId, username} = route.params; const [form, setForm] = React.useState({ largePic: '', @@ -97,12 +100,6 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({route}) => { }; /** - * login: determines if user successully created an account to - * navigate to home and display main tab navigation bar - */ - const {login} = React.useContext(AuthContext); - - /** * Profile screen "Add Large Profile Pic Here" button */ const LargeProfilePic = () => ( @@ -112,10 +109,7 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({route}) => { onPress={goToGalleryLargePic} style={styles.largeProfileUploader}> {form.largePic ? ( - <Image - source={{uri: form.largePic}} - style={styles.largeProfilePic} - /> + <Image source={{uri: form.largePic}} style={styles.largeProfilePic} /> ) : ( <Text style={styles.largeProfileText}>ADD LARGE PROFILE PIC HERE</Text> )} @@ -132,10 +126,7 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({route}) => { onPress={goToGallerySmallPic} style={styles.smallProfileUploader}> {form.smallPic ? ( - <Image - source={{uri: form.smallPic}} - style={styles.smallProfilePic} - /> + <Image source={{uri: form.smallPic}} style={styles.smallProfilePic} /> ) : ( <Text style={styles.smallProfileText}>ADD SMALLER PIC</Text> )} @@ -328,7 +319,10 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({route}) => { let statusCode = response.status; let data = await response.json(); if (statusCode === 200) { - login(userId, username); + navigation.navigate('SocialMedia', { + userId: userId, + username: username, + }); } else if (statusCode === 400) { Alert.alert( 'Profile update failed. 😔', diff --git a/src/screens/onboarding/SocialMedia.tsx b/src/screens/onboarding/SocialMedia.tsx new file mode 100644 index 00000000..868368e3 --- /dev/null +++ b/src/screens/onboarding/SocialMedia.tsx @@ -0,0 +1,180 @@ +import React from 'react'; +import { + StyleSheet, + View, + TouchableOpacity, + Text, + StatusBar, + KeyboardAvoidingView, + Platform, + Alert, +} from 'react-native'; +import { + Background, + RegistrationWizard, + LinkSocialMedia, +} from '../../components'; +import {LinkerType} from 'src/types'; +import {SOCIAL_LIST} from '../../constants/'; +import {OnboardingStackParams, AuthContext} from '../../routes'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; + +/** + * Social Media Screen for displaying social media linkers + */ + +type SocialMediaRouteProps = RouteProp<OnboardingStackParams, 'SocialMedia'>; + +type SocialMediaNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'SocialMedia' +>; + +interface SocialMediaProps { + route: SocialMediaRouteProps; + navigation: SocialMediaNavigationProp; +} + +const SocialMedia: React.FC<SocialMediaProps> = ({route, navigation}) => { + const {userId, username} = route.params; + const linkers: Array<LinkerType> = []; + const [state, setState] = React.useState({ + showMore: false, + }); + + /** + * login: determines if user successully created an account to + * navigate to home and display main tab navigation bar + */ + const {login} = React.useContext(AuthContext); + + // let numSocials: Number = state.showMore ? 9 : 3; + + for (let i = 0; i < SOCIAL_LIST.length; i++) { + let linker: LinkerType = { + label: SOCIAL_LIST[i], + }; + linkers.push(linker); + } + + /** + * Just commenting this out, in case we need it in the future + */ + // const handleShowPress = () => { + // setState({ + // ...state, + // showMore: !state.showMore, + // }); + // }; + + const handleLogin = () => { + try { + login(userId, username); + } catch (error) { + console.log(error); + Alert.alert('There was a problem logging you in'); + } + }; + + return ( + <Background style={styles.container}> + <StatusBar barStyle="light-content" /> + <RegistrationWizard style={styles.wizard} step="seven" /> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + style={styles.container}> + <View style={{marginBottom: '30%'}}> + <Text style={styles.header}>SOCIAL MEDIA</Text> + <Text style={styles.subtext}> + Select the social media you want to add + </Text> + </View> + <View style={styles.linkerContainer}> + {linkers.map((linker, index) => ( + <LinkSocialMedia key={index} social={linker} /> + ))} + </View> + </KeyboardAvoidingView> + <TouchableOpacity onPress={handleLogin} style={styles.loginButton}> + <Text style={styles.loginButtonLabel}>Login</Text> + </TouchableOpacity> + </Background> + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + linkerContainer: { + position: 'relative', + bottom: '15%', + flexDirection: 'row', + height: '25%', + flexWrap: 'wrap', + justifyContent: 'center', + alignContent: 'center', + marginBottom: '10%', + }, + header: { + color: '#fff', + fontSize: 22, + fontWeight: '600', + textAlign: 'center', + marginBottom: '4%', + marginHorizontal: '10%', + }, + subtext: { + color: '#fff', + fontSize: 14, + fontWeight: '600', + textAlign: 'center', + marginBottom: '35%', + marginHorizontal: '10%', + }, + // show: { + // borderColor: '#fff', + // borderWidth: 1, + // borderRadius: 3, + // paddingHorizontal: '2%', + // paddingVertical: '1%', + // marginVertical: '3%', + // marginLeft: '65%', + // }, + wizard: { + ...Platform.select({ + ios: { + top: 50, + }, + android: { + bottom: 40, + }, + }), + }, + loginButton: { + backgroundColor: '#8F01FF', + justifyContent: 'center', + alignItems: 'center', + width: 150, + height: 40, + borderRadius: 5, + borderWidth: 1, + borderColor: '#8F01FF', + marginBottom: '15%', + }, + loginButtonLabel: { + fontSize: 16, + fontWeight: '500', + color: '#ddd', + }, + form: { + alignItems: 'center', + justifyContent: 'flex-start', + flex: 3, + }, +}); + +export default SocialMedia; diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts index 27a33d32..d8ae7644 100644 --- a/src/screens/onboarding/index.ts +++ b/src/screens/onboarding/index.ts @@ -7,3 +7,4 @@ export {default as Checkpoint} from './Checkpoint'; export {default as ProfileOnboarding} from './ProfileOnboarding'; export {default as Splash} from './Splash'; export {default as InvitationCodeVerification} from './InvitationCodeVerification'; +export {default as SocialMedia} from './SocialMedia'; |