aboutsummaryrefslogtreecommitdiff
path: root/src/services/SocialLinkingService.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/services/SocialLinkingService.ts')
-rw-r--r--src/services/SocialLinkingService.ts184
1 files changed, 184 insertions, 0 deletions
diff --git a/src/services/SocialLinkingService.ts b/src/services/SocialLinkingService.ts
new file mode 100644
index 00000000..8d67d90e
--- /dev/null
+++ b/src/services/SocialLinkingService.ts
@@ -0,0 +1,184 @@
+import AsyncStorage from '@react-native-community/async-storage';
+import {Alert} from 'react-native';
+import InAppBrowser from 'react-native-inappbrowser-reborn';
+import {
+ LINKED_SOCIALS_ENDPOINT,
+ LINK_FB_ENDPOINT,
+ LINK_FB_OAUTH,
+ LINK_IG_ENDPOINT,
+ LINK_IG_OAUTH,
+ LINK_SNAPCHAT_ENDPOINT,
+ LINK_TIKTOK_ENDPOINT,
+ LINK_TWITTER_ENDPOINT,
+ LINK_TWITTER_OAUTH,
+} from '../constants';
+
+// A list of endpoint strings for all the integrated socials
+export const integratedEndpoints: {[social: string]: [string, string]} = {
+ Instagram: [LINK_IG_OAUTH, LINK_IG_ENDPOINT],
+ Facebook: [LINK_FB_OAUTH, LINK_FB_ENDPOINT],
+ Twitter: [LINK_TWITTER_OAUTH, LINK_TWITTER_ENDPOINT],
+};
+
+export const nonIntegratedEndponits: {[social: string]: string} = {
+ Snapchat: LINK_SNAPCHAT_ENDPOINT,
+ TikTok: LINK_TIKTOK_ENDPOINT,
+};
+
+export const registerNonIntegratedSocialLink: (
+ socialType: string,
+ username: string,
+) => Promise<boolean> = async (socialType, username) => {
+ if (!(socialType in nonIntegratedEndponits)) {
+ return false;
+ }
+ try {
+ const user_token = await AsyncStorage.getItem('token');
+ const response = await fetch(nonIntegratedEndponits[socialType], {
+ method: 'POST',
+ headers: {
+ Authorization: `Token ${user_token}`,
+ },
+ body: JSON.stringify({
+ username: username,
+ }),
+ });
+ return response.status === 200;
+ } catch (error) {
+ console.log(error);
+ return false;
+ }
+};
+
+// We have already received the short-lived token (callback_data), sending it
+// to backend to exchange for and store the long-lived token.
+export const registerIntegratedSocialLink: (
+ callback_data: string,
+ user_token: string,
+ socialType: string,
+) => Promise<boolean> = async (callback_data, user_token, socialType) => {
+ if (!(socialType in integratedEndpoints)) {
+ return false;
+ }
+ const response = await fetch(integratedEndpoints[socialType][1], {
+ method: 'POST',
+ headers: {
+ Authorization: `Token ${user_token}`,
+ },
+ body: JSON.stringify({
+ callback_url: callback_data,
+ }),
+ });
+ if (!(response.status === 201)) {
+ console.log(await response.json());
+ }
+ return response.status === 201;
+};
+
+// Twitter is a special case since they use OAuth1, we will need to request
+// for a request_token before we can begin browser signin.
+export const getTwitterRequestToken: (
+ user_token: string,
+) => Promise<string> = async (user_token) => {
+ const response = await fetch(integratedEndpoints.Twitter[0], {
+ method: 'GET',
+ headers: {
+ Authorization: `Token ${user_token}`,
+ },
+ });
+ return response.url;
+};
+
+// one stop shop for handling all browser sign-in social linkings
+export const handlePressForAuthBrowser: (
+ socialType: string,
+) => Promise<boolean> = async (socialType: string) => {
+ try {
+ if (!(socialType in integratedEndpoints)) {
+ Alert.alert('Coming soon!');
+ return false;
+ }
+
+ if (!(await InAppBrowser.isAvailable())) {
+ // 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! 😔',
+ );
+ return false;
+ }
+
+ let url = integratedEndpoints[socialType][0];
+ const user_token = await AsyncStorage.getItem('token');
+
+ if (!user_token) {
+ throw 'Unable to get user token';
+ }
+
+ // We will need to do an extra step for twitter sign-in
+ if (socialType === 'Twitter') {
+ url = await getTwitterRequestToken(user_token);
+ }
+
+ return await InAppBrowser.openAuth(url, 'taggid://callback', {
+ ephemeralWebSession: true,
+ })
+ .then(async (response) => {
+ if (response.type === 'success' && response.url) {
+ const success = await registerIntegratedSocialLink(
+ response.url,
+ user_token,
+ socialType,
+ );
+ if (!success) {
+ throw 'Unable to register with backend';
+ }
+ Alert.alert(`Successfully linked ${socialType} 🎉`);
+ return true;
+ } else {
+ throw 'Error from Oauth API';
+ }
+ })
+ .catch((error) => {
+ console.log(error);
+ Alert.alert(
+ `Something went wrong, we can't link with ${socialType} 😔`,
+ );
+ return false;
+ });
+ } catch (error) {
+ console.log(error);
+ Alert.alert(`Something went wrong, we can't link with ${socialType} 😔`);
+ }
+ return false;
+};
+
+// get all the linked socials from backend as an array
+export const getLinkedSocials: (user_id: string) => Promise<string[]> = async (
+ user_id: string,
+) => {
+ try {
+ const user_token = await AsyncStorage.getItem('token');
+ const response = await fetch(`${LINKED_SOCIALS_ENDPOINT}${user_id}/`, {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + user_token,
+ },
+ });
+ const body = await response.json();
+ if (response.status !== 200) {
+ console.log(body);
+ throw 'Unable to fetch from server';
+ }
+ return body.linked_socials || [];
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+};