aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/friends/InviteFriendTile.tsx142
-rw-r--r--src/constants/api.ts1
-rw-r--r--src/screens/main/NotificationsScreen.tsx4
-rw-r--r--src/screens/profile/InviteFriendsScreen.tsx176
-rw-r--r--src/screens/profile/legacy/UsersFromContacts.tsx106
-rw-r--r--src/services/UserFriendsService.ts19
6 files changed, 279 insertions, 169 deletions
diff --git a/src/components/friends/InviteFriendTile.tsx b/src/components/friends/InviteFriendTile.tsx
index 355b88e8..50085f98 100644
--- a/src/components/friends/InviteFriendTile.tsx
+++ b/src/components/friends/InviteFriendTile.tsx
@@ -19,62 +19,115 @@ 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) {
- Alert.alert(
- SUCCESS_CONFIRM_INVITE_CONTACT_TITLE(invites_left),
- SUCCESS_CONFIRM_INVITE_CONTACT_MESSAGE,
- [
- {text: 'No!', style: 'cancel'},
- {
- text: 'Yes!',
- onPress: async () => {
- const success = await inviteFriendService(
- item.phoneNumber,
- item.firstName,
- item.lastName,
- );
- if (success) {
- const inviteCode = await handleCreateInviteCode();
- setInvited(true);
- Linking.openURL(
- `sms:${item.phoneNumber}&body=${INVITE_USER_SMS_BODY(
- item.firstName,
- name,
- inviteCode,
- )}`,
+ // 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 > 0) {
+ Alert.alert(
+ SUCCESS_CONFIRM_INVITE_CONTACT_TITLE(invites_left),
+ SUCCESS_CONFIRM_INVITE_CONTACT_MESSAGE,
+ [
+ {text: 'No!', style: 'cancel'},
+ {
+ text: 'Yes!',
+ onPress: async () => {
+ const response = await inviteFriendService(
+ item.phoneNumber,
+ item.firstName,
+ item.lastName,
+ false,
);
- if (invites_left === 1) {
- Alert.alert(SUCCESS_LAST_CONTACT_INVITE);
+ const inviteCode = response?.invite_code;
+ if (inviteCode) {
+ // 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 {
+ Alert.alert(ERROR_SOMETHING_WENT_WRONG);
}
- } else {
- Alert.alert(ERROR_SOMETHING_WENT_WRONG);
- }
+ },
},
- },
- ],
- );
- } else if (invites_left === -1 || invites_left === 0) {
- Alert.alert(ERROR_NO_CONTACT_INVITE_LEFT);
- } else {
- Alert.alert(ERROR_SOMETHING_WENT_WRONG);
+ ],
+ );
+ } else if (invites_left === -1 || invites_left === 0) {
+ Alert.alert(ERROR_NO_CONTACT_INVITE_LEFT);
+ } else {
+ Alert.alert(ERROR_SOMETHING_WENT_WRONG);
+ }
}
};
@@ -103,7 +156,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 +166,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 {