diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/friends/InviteFriendTile.tsx | 108 | ||||
-rw-r--r-- | src/constants/api.ts | 1 | ||||
-rw-r--r-- | src/screens/main/NotificationsScreen.tsx | 4 | ||||
-rw-r--r-- | src/screens/profile/InviteFriendsScreen.tsx | 176 | ||||
-rw-r--r-- | src/screens/profile/legacy/UsersFromContacts.tsx | 106 | ||||
-rw-r--r-- | src/services/UserFriendsService.ts | 19 |
6 files changed, 260 insertions, 154 deletions
diff --git a/src/components/friends/InviteFriendTile.tsx b/src/components/friends/InviteFriendTile.tsx index 355b88e8..48f65a94 100644 --- a/src/components/friends/InviteFriendTile.tsx +++ b/src/components/friends/InviteFriendTile.tsx @@ -19,25 +19,54 @@ import { SUCCESS_CONFIRM_INVITE_CONTACT_TITLE, SUCCESS_LAST_CONTACT_INVITE, } from '../../constants/strings'; -import {InviteContactType} from '../../screens/profile/InviteFriendsScreen'; import { - getRemainingInviteCount, - handleCreateInviteCode, - inviteFriendService, -} from '../../services'; + InviteContactType, + SearchResultType, +} from '../../screens/profile/InviteFriendsScreen'; +import {getRemainingInviteCount, inviteFriendService} from '../../services'; import {normalize} from '../../utils'; interface InviteFriendTileProps { item: InviteContactType; + remind: boolean; + results: SearchResultType; + setResults: Function; } -const InviteFriendTile: React.FC<InviteFriendTileProps> = ({item}) => { - const [invited, setInvited] = useState<boolean>(false); +const InviteFriendTile: React.FC<InviteFriendTileProps> = ({ + item, + remind, + results, + setResults, +}) => { + const [invited, setInvited] = useState<boolean>(remind); const {name} = useSelector((state: RootState) => state.user.profile); const [formatedPhoneNumber, setFormattedPhoneNumber] = useState<string>(''); + const handleInviteFriend = async () => { - const invites_left = await getRemainingInviteCount(); - if (invites_left > 0) { + // If user has been invited already, don't show alerts and change invite count + if (invited) { + const response = await inviteFriendService( + item.phoneNumber, + item.firstName, + item.lastName, + true, + ); + const inviteCode = response?.invite_code; + if (inviteCode) { + Linking.openURL( + `sms:${item.phoneNumber}&body=${INVITE_USER_SMS_BODY( + item.firstName, + name, + inviteCode, + )}`, + ); + } + } else { + const invites_left = await getRemainingInviteCount(); + if (invites_left < 1) { + Alert.alert(ERROR_NO_CONTACT_INVITE_LEFT); + } Alert.alert( SUCCESS_CONFIRM_INVITE_CONTACT_TITLE(invites_left), SUCCESS_CONFIRM_INVITE_CONTACT_MESSAGE, @@ -46,35 +75,55 @@ const InviteFriendTile: React.FC<InviteFriendTileProps> = ({item}) => { { text: 'Yes!', onPress: async () => { - const success = await inviteFriendService( + const response = await inviteFriendService( item.phoneNumber, item.firstName, item.lastName, + false, ); - if (success) { - const inviteCode = await handleCreateInviteCode(); - setInvited(true); - Linking.openURL( - `sms:${item.phoneNumber}&body=${INVITE_USER_SMS_BODY( - item.firstName, - name, - inviteCode, - )}`, - ); - if (invites_left === 1) { - Alert.alert(SUCCESS_LAST_CONTACT_INVITE); - } - } else { + const inviteCode = response?.invite_code; + if (!inviteCode) { Alert.alert(ERROR_SOMETHING_WENT_WRONG); } + // Add user to Pending Users list + const newPendingUser: InviteContactType = { + phoneNumber: item.phoneNumber, + firstName: item.firstName, + lastName: item.lastName, + }; + + // Filtering user from nonUsersFromContacts list + const filteredNonUsers = results.nonUsersFromContacts.filter( + (user: InviteContactType) => + user.phoneNumber !== item.phoneNumber, + ); + + // Open iMessages + Linking.openURL( + `sms:${item.phoneNumber}&body=${INVITE_USER_SMS_BODY( + item.firstName, + name, + inviteCode, + )}`, + ); + + // Update results after navigating out of the app + setTimeout(() => { + setInvited(true); + setResults({ + ...results, + pendingUsers: [...results.pendingUsers, newPendingUser], + nonUsersFromContacts: filteredNonUsers, + }); + }, 500); + + if (invites_left === 1) { + Alert.alert(SUCCESS_LAST_CONTACT_INVITE); + } }, }, ], ); - } else if (invites_left === -1 || invites_left === 0) { - Alert.alert(ERROR_NO_CONTACT_INVITE_LEFT); - } else { - Alert.alert(ERROR_SOMETHING_WENT_WRONG); } }; @@ -103,7 +152,6 @@ const InviteFriendTile: React.FC<InviteFriendTileProps> = ({item}) => { <Text style={styles.phoneNumber}>{formatedPhoneNumber}</Text> </View> <TouchableOpacity - disabled={invited} style={[ styles.button, invited ? styles.pendingButton : styles.inviteButton, @@ -114,7 +162,7 @@ const InviteFriendTile: React.FC<InviteFriendTileProps> = ({item}) => { styles.buttonTitle, invited ? styles.pendingButtonTitle : styles.inviteButtonTitle, ]}> - {invited ? 'Pending' : 'Invite'} + {invited ? 'Remind' : 'Invite'} </Text> </TouchableOpacity> </View> diff --git a/src/constants/api.ts b/src/constants/api.ts index ec67b6f9..6cc357f5 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -52,7 +52,6 @@ export const USERS_FROM_CONTACTS_ENDPOINT: string = API_URL + 'user_contacts/find_friends/'; export const INVITE_FRIEND_ENDPOINT: string = API_URL + 'user_contacts/invite_friend/'; -export const CREATE_INVITE_CODE = API_URL + 'create-code/'; export const GET_REMAINING_INVITES = API_URL + 'user_contacts/check_invite_count/' // Suggested People export const SP_USERS_ENDPOINT: string = API_URL + 'suggested_people/'; diff --git a/src/screens/main/NotificationsScreen.tsx b/src/screens/main/NotificationsScreen.tsx index 7e2f082c..bce48ef2 100644 --- a/src/screens/main/NotificationsScreen.tsx +++ b/src/screens/main/NotificationsScreen.tsx @@ -259,9 +259,7 @@ const NotificationsScreen: React.FC = () => { onPress={async () => { const permission = await checkPermission(); if (permission === 'authorized') { - navigation.navigate('InviteFriendsScreen', { - screenType: ScreenType.Profile, - }); + navigation.navigate('InviteFriendsScreen'); } else { Alert.alert( '"Tagg" Would Like to Access Your Contacts', diff --git a/src/screens/profile/InviteFriendsScreen.tsx b/src/screens/profile/InviteFriendsScreen.tsx index e1f739c5..bf91e8f3 100644 --- a/src/screens/profile/InviteFriendsScreen.tsx +++ b/src/screens/profile/InviteFriendsScreen.tsx @@ -1,23 +1,28 @@ -import React, {useEffect, useState} from 'react'; +import {RouteProp} from '@react-navigation/native'; +import React, {useEffect, useMemo, useState} from 'react'; import { - View, - Text, - TouchableOpacity, - SafeAreaView, - StyleSheet, - TextInput, FlatList, Keyboard, Linking, + SafeAreaView, + ScrollView, StatusBar, + StyleSheet, + Text, + TextInput, TouchableWithoutFeedback, - ScrollView, + View, } from 'react-native'; -import {useDispatch, useStore} from 'react-redux'; +import {checkPermission} from 'react-native-contacts'; +import Animated from 'react-native-reanimated'; +import Icon from 'react-native-vector-icons/Feather'; +import {TabsGradient} from '../../components'; +import {InviteFriendTile} from '../../components/friends'; +import {MainStackParams} from '../../routes'; +import {usersFromContactsService} from '../../services/UserFriendsService'; import {ProfilePreviewType} from '../../types'; import { extractContacts, - handleAddFriend, HeaderHeight, isIPhoneX, normalize, @@ -25,15 +30,6 @@ import { SCREEN_WIDTH, StatusBarHeight, } from '../../utils'; -import {checkPermission} from 'react-native-contacts'; -import {usersFromContactsService} from '../../services/UserFriendsService'; -import {ProfilePreview, TabsGradient} from '../../components'; -import Animated from 'react-native-reanimated'; -import Icon from 'react-native-vector-icons/Feather'; -import {InviteFriendTile} from '../../components/friends'; -import {TAGG_LIGHT_BLUE} from '../../constants'; -import {MainStackParams} from '../../routes'; -import {RouteProp} from '@react-navigation/native'; const AnimatedIcon = Animated.createAnimatedComponent(Icon); export type InviteContactType = { @@ -42,9 +38,9 @@ export type InviteContactType = { phoneNumber: string; }; -type SearchResultType = { - usersFromContacts: ProfilePreviewType[]; +export type SearchResultType = { nonUsersFromContacts: InviteContactType[]; + pendingUsers: InviteContactType[]; }; type InviteFriendsScreenRouteProp = RouteProp< @@ -56,17 +52,15 @@ interface InviteFriendsScreenProps { route: InviteFriendsScreenRouteProp; } -const InviteFriendsScreen: React.FC<InviteFriendsScreenProps> = ({route}) => { - const {screenType} = route.params; - const dispatch = useDispatch(); - const state = useStore().getState(); +const InviteFriendsScreen: React.FC<InviteFriendsScreenProps> = ({}) => { const [usersFromContacts, setUsersFromContacts] = useState< ProfilePreviewType[] >([]); const [nonUsersFromContacts, setNonUsersFromContacts] = useState<[]>([]); + const [pendingUsers] = useState<[]>([]); const [results, setResults] = useState<SearchResultType>({ - usersFromContacts: usersFromContacts, nonUsersFromContacts: nonUsersFromContacts, + pendingUsers: pendingUsers, }); const [query, setQuery] = useState(''); @@ -79,8 +73,8 @@ const InviteFriendsScreen: React.FC<InviteFriendsScreenProps> = ({route}) => { await setUsersFromContacts(response.existing_tagg_users); await setNonUsersFromContacts(response.invite_from_contacts); setResults({ - usersFromContacts: response.existing_tagg_users, nonUsersFromContacts: response.invite_from_contacts, + pendingUsers: response.pending_users, }); } else { Linking.openSettings(); @@ -118,54 +112,54 @@ const InviteFriendsScreen: React.FC<InviteFriendsScreenProps> = ({route}) => { setResults(sanitizedResult); } else { setResults({ - usersFromContacts: usersFromContacts, nonUsersFromContacts: nonUsersFromContacts, + pendingUsers: pendingUsers, }); } }; search(); }, [query]); - const UsersFromContacts = () => ( - <> + const PendingList = useMemo( + () => ( <FlatList + contentContainerStyle={styles.nonUsersFlatListContainer} showsVerticalScrollIndicator={false} scrollEnabled={false} - data={results.usersFromContacts} - keyExtractor={(item) => item.username} + data={results.pendingUsers} + keyExtractor={(item) => item.phoneNumber} renderItem={({item}) => ( - <View key={item.id} style={styles.ppContainer}> - <View style={styles.friend}> - <ProfilePreview - {...{profilePreview: item}} - previewType={'Friend'} - screenType={screenType} - /> - </View> - <TouchableOpacity - style={styles.addFriendButton} - onPress={() => { - handleAddFriend(screenType, item, dispatch, state).then( - (success) => { - if (success) { - let users = usersFromContacts; - const filteredUsers = users.filter( - (user) => user.username !== item.username, - ); - setResults({ - ...results, - usersFromContacts: filteredUsers, - }); - } - }, - ); - }}> - <Text style={styles.addFriendButtonTitle}>Add Friend</Text> - </TouchableOpacity> - </View> + <InviteFriendTile + item={item} + remind={true} + results={results} + setResults={setResults} + /> )} /> - </> + ), + [results.pendingUsers], + ); + + const InviteList = useMemo( + () => ( + <FlatList + contentContainerStyle={styles.nonUsersFlatListContainer} + showsVerticalScrollIndicator={false} + scrollEnabled={false} + data={results.nonUsersFromContacts} + keyExtractor={(item) => item.phoneNumber} + renderItem={({item}) => ( + <InviteFriendTile + item={item} + remind={false} + results={results} + setResults={setResults} + /> + )} + /> + ), + [results.nonUsersFromContacts], ); return ( @@ -209,20 +203,21 @@ const InviteFriendsScreen: React.FC<InviteFriendsScreenProps> = ({route}) => { /> </Animated.View> </View> - <View style={styles.subheader}> - <Text style={styles.subheaderText}>Contacts on Tagg</Text> - <UsersFromContacts /> + <View + style={[ + styles.subheader, + { + height: + 72 * + (results.pendingUsers ? results.pendingUsers.length : 1), + }, + ]}> + <Text style={styles.subheaderText}>Pending Users</Text> + {PendingList} </View> <View style={styles.subheader}> <Text style={styles.subheaderText}>Invite your friends!</Text> - <FlatList - contentContainerStyle={styles.nonUsersFlatListContainer} - showsVerticalScrollIndicator={false} - scrollEnabled={false} - data={results.nonUsersFromContacts} - keyExtractor={(item) => item.phoneNumber} - renderItem={({item}) => <InviteFriendTile item={item} />} - /> + {InviteList} </View> </ScrollView> </SafeAreaView> @@ -275,16 +270,6 @@ const styles = StyleSheet.create({ marginBottom: '3%', marginHorizontal: 10, }, - ppContainer: { - alignSelf: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - width: '100%', - height: normalize(42), - alignItems: 'center', - marginBottom: '5%', - marginHorizontal: 10, - }, inputContainer: { flexGrow: 1, flexDirection: 'row', @@ -314,31 +299,6 @@ const styles = StyleSheet.create({ color: '#818181', fontWeight: '500', }, - friend: { - alignSelf: 'center', - height: '100%', - }, - addFriendButton: { - alignSelf: 'center', - justifyContent: 'center', - alignItems: 'center', - width: 82, - height: 25, - borderColor: TAGG_LIGHT_BLUE, - borderWidth: 2, - borderRadius: 2, - padding: 0, - backgroundColor: TAGG_LIGHT_BLUE, - }, - addFriendButtonTitle: { - color: 'white', - padding: 0, - fontSize: normalize(11), - fontWeight: '700', - lineHeight: normalize(13.13), - letterSpacing: normalize(0.6), - paddingHorizontal: '3.8%', - }, }); export default InviteFriendsScreen; diff --git a/src/screens/profile/legacy/UsersFromContacts.tsx b/src/screens/profile/legacy/UsersFromContacts.tsx new file mode 100644 index 00000000..5499799e --- /dev/null +++ b/src/screens/profile/legacy/UsersFromContacts.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import {FlatList, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; +import {useDispatch, useStore} from 'react-redux'; +import {ProfilePreview} from '../../../components/profile'; +import {TAGG_LIGHT_BLUE} from '../../../constants/constants'; +import {RootState} from '../../../store/rootReducer'; +import {ScreenType} from '../../../types/types'; +import {normalize} from '../../../utils'; +import {handleAddFriend} from '../../../utils/friends'; +import {SearchResultType} from '../InviteFriendsScreen'; + +interface UsersFromContactsProps { + screenType: ScreenType; + results: SearchResultType; + setResults: Function; +} + +const UsersFromContacts: React.FC<UsersFromContactsProps> = ({ + screenType, + results, + setResults, +}) => { + const dispatch = useDispatch(); + const state: RootState = useStore().getState(); + return ( + <> + <FlatList + showsVerticalScrollIndicator={false} + scrollEnabled={false} + data={results.usersFromContacts} + keyExtractor={(item) => item.username} + renderItem={({item}) => ( + <View key={item.id} style={styles.ppContainer}> + <View style={styles.friend}> + <ProfilePreview + {...{profilePreview: item}} + previewType={'Friend'} + screenType={screenType} + /> + </View> + <TouchableOpacity + style={styles.addFriendButton} + onPress={() => { + handleAddFriend(screenType, item, dispatch, state).then( + (success) => { + if (success) { + let users = results.usersFromContacts; + const filteredUsers = users.filter( + (user) => user.username !== item.username, + ); + setResults({ + ...results, + usersFromContacts: filteredUsers, + }); + } + }, + ); + }}> + <Text style={styles.addFriendButtonTitle}>Add Friend</Text> + </TouchableOpacity> + </View> + )} + /> + </> + ); +}; + +export default UsersFromContacts; + +const styles = StyleSheet.create({ + ppContainer: { + alignSelf: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + height: normalize(42), + alignItems: 'center', + marginBottom: '5%', + marginHorizontal: 10, + }, + friend: { + alignSelf: 'center', + height: '100%', + }, + addFriendButton: { + alignSelf: 'center', + justifyContent: 'center', + alignItems: 'center', + width: 82, + height: 25, + borderColor: TAGG_LIGHT_BLUE, + borderWidth: 2, + borderRadius: 2, + padding: 0, + backgroundColor: TAGG_LIGHT_BLUE, + }, + addFriendButtonTitle: { + color: 'white', + padding: 0, + fontSize: normalize(11), + fontWeight: '700', + lineHeight: normalize(13.13), + letterSpacing: normalize(0.6), + paddingHorizontal: '3.8%', + }, +}); diff --git a/src/services/UserFriendsService.ts b/src/services/UserFriendsService.ts index 453f35a5..a53ceef6 100644 --- a/src/services/UserFriendsService.ts +++ b/src/services/UserFriendsService.ts @@ -1,7 +1,6 @@ import AsyncStorage from '@react-native-community/async-storage'; import {Alert} from 'react-native'; import { - CREATE_INVITE_CODE, FRIENDS_ENDPOINT, GET_REMAINING_INVITES, INVITE_FRIEND_ENDPOINT, @@ -221,6 +220,7 @@ export const inviteFriendService = async ( phoneNumber: string, inviteeFirstName: string, inviteeLastName: string, + reminding: boolean, ) => { try { const token = await AsyncStorage.getItem('token'); @@ -233,9 +233,14 @@ export const inviteFriendService = async ( invitee_phone_number: phoneNumber, invitee_first_name: inviteeFirstName, invitee_last_name: inviteeLastName, + reminding: reminding, }), }); - return response.status === 201 || response.status === 200; + if (response.status === 201 || response.status === 200) { + return await response.json(); + } else { + return undefined; + } } catch (error) { console.log(error); Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); @@ -243,16 +248,6 @@ export const inviteFriendService = async ( } }; -export const handleCreateInviteCode = async () => { - const response = await fetch(CREATE_INVITE_CODE, {method: 'POST'}); - if (response.status === 200) { - const data = await response.json(); - return data.code; - } else if (response.status === 500) { - return -1; - } -}; - export const getRemainingInviteCount = async () => { const token = await AsyncStorage.getItem('token'); try { |