diff options
Diffstat (limited to 'src/services')
| -rw-r--r-- | src/services/SocialLinkingService.ts | 184 | ||||
| -rw-r--r-- | src/services/UserProfileService.ts | 40 | ||||
| -rw-r--r-- | src/services/index.ts | 1 |
3 files changed, 209 insertions, 16 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 []; + } +}; diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts index f5523be4..31383f67 100644 --- a/src/services/UserProfileService.ts +++ b/src/services/UserProfileService.ts @@ -1,16 +1,18 @@ //Abstracted common profile api calls out here +import AsyncStorage from '@react-native-community/async-storage'; import {Alert} from 'react-native'; +import RNFetchBlob from 'rn-fetch-blob'; +import {SocialAccountType} from 'src/types'; import { - PROFILE_INFO_ENDPOINT, AVATAR_PHOTO_ENDPOINT, COVER_PHOTO_ENDPOINT, + GET_FB_POSTS_ENDPOINT, + GET_IG_POSTS_ENDPOINT, + GET_TWITTER_POSTS_ENDPOINT, + PROFILE_INFO_ENDPOINT, } from '../constants'; -import AsyncStorage from '@react-native-community/async-storage'; -import RNFetchBlob from 'rn-fetch-blob'; -import {SocialAccountType} from 'src/types'; - export const loadProfileInfo = async ( token: string, userId: string, @@ -83,13 +85,19 @@ export const loadCover = async ( } }; -export const loadSocialPosts = async ( - token: string, +const integratedSocialPostsEndpoints: {[social: string]: string} = { + Facebook: GET_FB_POSTS_ENDPOINT, + Instagram: GET_IG_POSTS_ENDPOINT, + Twitter: GET_TWITTER_POSTS_ENDPOINT, +}; + +export const loadSocialPosts: ( userId: string, socialType: string, - endpoint: string, - socialAccounts: Record<string, SocialAccountType>, -) => { +) => Promise<SocialAccountType> = async (userId, socialType) => { + const token = await AsyncStorage.getItem('token'); + const endpoint = integratedSocialPostsEndpoints[socialType]; + const accountData: SocialAccountType = {}; try { const response = await fetch(endpoint + `${userId}/`, { method: 'GET', @@ -99,16 +107,16 @@ export const loadSocialPosts = async ( }); if (response.status === 200) { const body = await response.json(); - socialAccounts[socialType].handle = body.handle; - socialAccounts[socialType].posts = body.posts; - socialAccounts[socialType].profile_pic = body.profile_pic; + accountData.handle = body.handle; + accountData.posts = body.posts; + accountData.profile_pic = body.profile_pic; } else { - throw new Error(await response.json()); + throw 'Unable to fetch posts data from ' + socialType; } } catch (error) { - console.log(error); + console.warn(error); } - return socialAccounts; + return accountData; }; export const loadRecentlySearchedUsers = async (callback: Function) => { diff --git a/src/services/index.ts b/src/services/index.ts index aa13dbe3..6d0f4314 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,3 +1,4 @@ export * from './UserProfileService'; +export * from './SocialLinkingService'; export * from './MomentServices'; export * from './UserFollowServices'; |
