aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshm Walia <40498934+ashmgarv@users.noreply.github.com>2020-10-24 17:53:37 -0700
committerGitHub <noreply@github.com>2020-10-24 20:53:37 -0400
commit44a25bfabd356f5eee5ec4f580452407a7e40246 (patch)
tree12fd8aa166049361fc6306b2c427e3201e13856e
parent80a5b47d9fef940604d729ff5c428e16aa4be37a (diff)
[TMA 302] Social media linking to login (#71)
* Social media linking to login * Social media linking to login
-rw-r--r--src/assets/images/tiktok-icon.pngbin0 -> 5552 bytes
-rw-r--r--src/components/common/SocialIcon.tsx3
-rw-r--r--src/components/onboarding/LinkSocialMedia.tsx204
-rw-r--r--src/components/onboarding/RegistrationWizard.tsx12
-rw-r--r--src/components/onboarding/index.ts1
-rw-r--r--src/constants/constants.ts6
-rw-r--r--src/routes/onboarding/Onboarding.tsx2
-rw-r--r--src/routes/onboarding/OnboardingStack.tsx1
-rw-r--r--src/screens/onboarding/Checkpoint.tsx11
-rw-r--r--src/screens/onboarding/ProfileOnboarding.tsx26
-rw-r--r--src/screens/onboarding/SocialMedia.tsx180
-rw-r--r--src/screens/onboarding/index.ts1
12 files changed, 417 insertions, 30 deletions
diff --git a/src/assets/images/tiktok-icon.png b/src/assets/images/tiktok-icon.png
new file mode 100644
index 00000000..c7fa788d
--- /dev/null
+++ b/src/assets/images/tiktok-icon.png
Binary files differ
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';