1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
/**
* 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, TAGG_ICON_DIM} 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%',
width: TAGG_ICON_DIM,
height: TAGG_ICON_DIM,
},
label: {
fontWeight: '500',
top: '25%',
},
tick: {
top: '30%',
},
});
export default SocialMediaLinker;
|