aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/common/TaggPrompt.tsx4
-rw-r--r--src/components/friends/InviteFriendTile.tsx135
-rw-r--r--src/components/friends/index.ts1
-rw-r--r--src/components/moments/CaptionScreenHeader.tsx7
-rw-r--r--src/components/moments/IndividualMomentTitleBar.tsx8
-rw-r--r--src/components/moments/Moment.tsx22
-rw-r--r--src/components/notifications/NotificationPrompts.tsx58
-rw-r--r--src/components/notifications/index.ts1
-rw-r--r--src/components/profile/Friends.tsx173
-rw-r--r--src/components/search/SearchBar.tsx2
10 files changed, 374 insertions, 37 deletions
diff --git a/src/components/common/TaggPrompt.tsx b/src/components/common/TaggPrompt.tsx
index 75f3009b..d65e30c6 100644
--- a/src/components/common/TaggPrompt.tsx
+++ b/src/components/common/TaggPrompt.tsx
@@ -7,7 +7,7 @@ import {normalize, SCREEN_HEIGHT} from '../../utils';
type TaggPromptProps = {
messageHeader: string;
messageBody: string | Element;
- logoType: 'plus' | 'tagg';
+ logoType: 'plus' | 'tagg' | 'invite_friends';
hideCloseButton?: boolean;
noPadding?: boolean;
onClose: () => void;
@@ -29,6 +29,8 @@ const TaggPrompt: React.FC<TaggPromptProps> = ({
switch (logoType) {
case 'plus':
return require('../../assets/icons/plus-logo.png');
+ case 'invite_friends':
+ return require('../../assets/icons/invite-friends-prompt-icon.png');
case 'tagg':
default:
return require('../../assets/images/logo-purple.png');
diff --git a/src/components/friends/InviteFriendTile.tsx b/src/components/friends/InviteFriendTile.tsx
new file mode 100644
index 00000000..95ebf16a
--- /dev/null
+++ b/src/components/friends/InviteFriendTile.tsx
@@ -0,0 +1,135 @@
+import React, {useEffect, useState} from 'react';
+import {
+ Alert,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ TouchableWithoutFeedback,
+ View,
+} from 'react-native';
+import {TAGG_LIGHT_BLUE} from '../../constants';
+import {ERROR_SOMETHING_WENT_WRONG} from '../../constants/strings';
+import {inviteFriendService} from '../../services';
+import {normalize} from '../../utils';
+
+interface InviteFriendTileProps {
+ item: Object;
+}
+
+const InviteFriendTile: React.FC<InviteFriendTileProps> = ({item}) => {
+ const [invited, setInvited] = useState<boolean>(false);
+ const [formatedPhoneNumber, setFormattedPhoneNumber] = useState<string>('');
+ const handleInviteFriend = async () => {
+ const response = await inviteFriendService(
+ item.phoneNumber,
+ item.firstName,
+ item.lastName,
+ );
+ if (response) {
+ setInvited(response);
+ } else {
+ Alert.alert(ERROR_SOMETHING_WENT_WRONG);
+ }
+ };
+
+ useEffect(() => {
+ const formatPhoneNumer = () => {
+ const unformatted_number: string = item.phoneNumber;
+ const part_one: string = unformatted_number.substring(2, 5);
+ const part_two: string = unformatted_number.substring(5, 8);
+ const part_three: string = unformatted_number.substring(
+ 8,
+ unformatted_number.length,
+ );
+ const temp = '(' + part_one + ')' + part_two + '-' + part_three;
+ setFormattedPhoneNumber(temp);
+ };
+ formatPhoneNumer();
+ });
+
+ return (
+ <TouchableWithoutFeedback>
+ <View style={styles.container}>
+ <View style={styles.bodyContainer}>
+ <Text style={styles.label}>
+ {item.firstName + ' ' + item.lastName}
+ </Text>
+ <Text style={styles.phoneNumber}>{formatedPhoneNumber}</Text>
+ </View>
+ <TouchableOpacity
+ disabled={invited}
+ style={[
+ styles.button,
+ invited ? styles.pendingButton : styles.inviteButton,
+ ]}
+ onPress={handleInviteFriend}>
+ <Text
+ style={[
+ styles.buttonTitle,
+ invited ? styles.pendingButtonTitle : styles.inviteButtonTitle,
+ ]}>
+ {invited ? 'Pending' : 'Invite'}
+ </Text>
+ </TouchableOpacity>
+ </View>
+ </TouchableWithoutFeedback>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ height: normalize(42),
+ marginBottom: '5%',
+ },
+ bodyContainer: {
+ flexDirection: 'column',
+ height: normalize(42),
+ justifyContent: 'space-around',
+ },
+ label: {
+ fontWeight: '500',
+ fontSize: normalize(14),
+ },
+ phoneNumber: {
+ fontSize: normalize(12),
+ fontWeight: '400',
+ color: '#6C6C6C',
+ letterSpacing: normalize(0.1),
+ },
+ button: {
+ alignSelf: 'center',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: 82,
+ height: 25,
+ borderWidth: 2,
+ borderRadius: 2,
+ padding: 0,
+ borderColor: TAGG_LIGHT_BLUE,
+ },
+ pendingButton: {
+ backgroundColor: TAGG_LIGHT_BLUE,
+ },
+ inviteButton: {
+ backgroundColor: 'transparent',
+ },
+ buttonTitle: {
+ padding: 0,
+ fontSize: normalize(11),
+ fontWeight: '700',
+ lineHeight: normalize(13.13),
+ letterSpacing: normalize(0.6),
+ paddingHorizontal: '3.8%',
+ },
+ pendingButtonTitle: {
+ color: 'white',
+ },
+ inviteButtonTitle: {
+ color: TAGG_LIGHT_BLUE,
+ },
+});
+
+export default InviteFriendTile;
diff --git a/src/components/friends/index.ts b/src/components/friends/index.ts
new file mode 100644
index 00000000..42727784
--- /dev/null
+++ b/src/components/friends/index.ts
@@ -0,0 +1 @@
+export {default as InviteFriendTile} from './InviteFriendTile';
diff --git a/src/components/moments/CaptionScreenHeader.tsx b/src/components/moments/CaptionScreenHeader.tsx
index 46dfddfe..0638c128 100644
--- a/src/components/moments/CaptionScreenHeader.tsx
+++ b/src/components/moments/CaptionScreenHeader.tsx
@@ -21,18 +21,15 @@ const styles = StyleSheet.create({
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
- height: '5%',
},
headerContainer: {
- position: 'absolute',
- left: '50%',
+ width: '90%',
},
header: {
- position: 'relative',
- right: '50%',
fontSize: 20,
fontWeight: 'bold',
color: 'white',
+ textAlign: 'center',
},
});
export default CaptionScreenHeader;
diff --git a/src/components/moments/IndividualMomentTitleBar.tsx b/src/components/moments/IndividualMomentTitleBar.tsx
index 6cdfe0e8..88e0c308 100644
--- a/src/components/moments/IndividualMomentTitleBar.tsx
+++ b/src/components/moments/IndividualMomentTitleBar.tsx
@@ -18,7 +18,9 @@ const IndividualMomentTitleBar: React.FC<IndividualMomentTitleBarProps> = ({
<TouchableOpacity style={styles.closeButton} onPress={close}>
<CloseIcon height={'100%'} width={'100%'} color={'white'} />
</TouchableOpacity>
- <Text style={styles.header}>{title}</Text>
+ <View style={styles.headerContainer}>
+ <Text style={styles.header}>{title}</Text>
+ </View>
</View>
);
};
@@ -30,6 +32,10 @@ const styles = StyleSheet.create({
justifyContent: 'center',
height: '5%',
},
+ headerContainer: {
+ flexShrink: 1,
+ marginLeft: '11%',
+ },
header: {
color: 'white',
fontSize: normalize(18),
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index 10cf6070..2ac6aebb 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -12,7 +12,7 @@ import PlusIcon from '../../assets/icons/plus_icon-01.svg';
import BigPlusIcon from '../../assets/icons/plus_icon-02.svg';
import UpIcon from '../../assets/icons/up_icon.svg';
import {TAGG_LIGHT_BLUE} from '../../constants';
-import {ERROR_UPLOAD_MOMENT_SHORT} from '../../constants/strings';
+import {ERROR_UPLOAD} from '../../constants/strings';
import {normalize, SCREEN_WIDTH} from '../../utils';
import MomentTile from './MomentTile';
@@ -69,7 +69,7 @@ const Moment: React.FC<MomentProps> = ({
})
.catch((err) => {
if (err.code && err.code !== 'E_PICKER_CANCELLED') {
- Alert.alert(ERROR_UPLOAD_MOMENT_SHORT);
+ Alert.alert(ERROR_UPLOAD);
}
});
};
@@ -81,14 +81,14 @@ const Moment: React.FC<MomentProps> = ({
{title}
</Text>
{!userXId ? (
- <>
+ <View style={{flexDirection: 'row'}}>
{showUpButton && move && (
<UpIcon
width={19}
height={19}
onPress={() => move('up', title)}
color={TAGG_LIGHT_BLUE}
- style={{marginLeft: 5}}
+ style={{marginHorizontal: 4}}
/>
)}
{showDownButton && move && (
@@ -97,31 +97,32 @@ const Moment: React.FC<MomentProps> = ({
height={19}
onPress={() => move('down', title)}
color={TAGG_LIGHT_BLUE}
- style={{marginLeft: 5}}
+ style={{marginHorizontal: 4}}
/>
)}
- </>
+ </View>
) : (
<Fragment />
)}
- <View style={styles.flexer} />
+ {/* <View style={styles.flexer} /> */}
{!userXId ? (
- <>
+ <View style={{marginRight: 8, flexDirection: 'row'}}>
<PlusIcon
width={21}
height={21}
onPress={() => navigateToImagePicker()}
color={TAGG_LIGHT_BLUE}
- style={{marginRight: 10}}
+ style={{marginHorizontal: 4}}
/>
{shouldAllowDeletion && (
<DeleteIcon
onPress={() => handleMomentCategoryDelete(title)}
width={19}
height={19}
+ style={{marginHorizontal: 4}}
/>
)}
- </>
+ </View>
) : (
<React.Fragment />
)}
@@ -171,6 +172,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
titleText: {
+ width: '70%',
fontSize: normalize(16),
fontWeight: 'bold',
color: TAGG_LIGHT_BLUE,
diff --git a/src/components/notifications/NotificationPrompts.tsx b/src/components/notifications/NotificationPrompts.tsx
new file mode 100644
index 00000000..dc27925b
--- /dev/null
+++ b/src/components/notifications/NotificationPrompts.tsx
@@ -0,0 +1,58 @@
+import React, {Fragment} from 'react';
+import {Image, StyleSheet, Text} from 'react-native';
+import {TaggPrompt} from '../common';
+
+export const InviteFriendsPrompt: React.FC = () => {
+ return (
+ <TaggPrompt
+ messageHeader={'Invite Friends To Tagg!'}
+ messageBody={
+ 'A new feature that lets you invite your friends to Tagg. \nClick on your friends list to do so!'
+ }
+ logoType={'invite_friends'}
+ hideCloseButton={true}
+ noPadding={true}
+ onClose={() => {}}
+ />
+ );
+};
+
+interface SPPromptNotificationProps {
+ showSPNotifyPopUp: boolean;
+}
+
+export const SPPromptNotification: React.FC<SPPromptNotificationProps> = ({
+ showSPNotifyPopUp,
+}) => {
+ return showSPNotifyPopUp ? (
+ <TaggPrompt
+ messageHeader={'New Suggested People Page!'}
+ messageBody={
+ <>
+ <Text>
+ A new page where you can discover new profiles. Just press the new{' '}
+ </Text>
+ <Image
+ style={styles.icon}
+ source={require('../../assets/navigationIcons/home.png')}
+ />
+ <Text> button on the tab bar to check it out!</Text>
+ </>
+ }
+ logoType={'tagg'}
+ hideCloseButton={true}
+ noPadding={true}
+ onClose={() => {}}
+ />
+ ) : (
+ <Fragment />
+ );
+};
+
+const styles = StyleSheet.create({
+ icon: {
+ width: 20,
+ height: 20,
+ tintColor: 'grey',
+ },
+});
diff --git a/src/components/notifications/index.ts b/src/components/notifications/index.ts
index 0260ce24..733b56f1 100644
--- a/src/components/notifications/index.ts
+++ b/src/components/notifications/index.ts
@@ -1 +1,2 @@
export {default as Notification} from './Notification';
+export {InviteFriendsPrompt} from './NotificationPrompts';
diff --git a/src/components/profile/Friends.tsx b/src/components/profile/Friends.tsx
index 7c7265c5..ac724ae0 100644
--- a/src/components/profile/Friends.tsx
+++ b/src/components/profile/Friends.tsx
@@ -1,14 +1,23 @@
-import React from 'react';
-import {View, StyleSheet, ScrollView, Text} from 'react-native';
-import {ProfilePreviewType, ScreenType} from '../../types';
-import {ProfilePreview} from '../profile';
-import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {useNavigation} from '@react-navigation/native';
+import React, {useEffect, useState} from 'react';
+import {Alert, Linking, ScrollView, StyleSheet, Text, View} from 'react-native';
+import {checkPermission} from 'react-native-contacts';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {useDispatch, useSelector, useStore} from 'react-redux';
import {TAGG_LIGHT_BLUE} from '../../constants';
-import {RootState} from '../../store/rootReducer';
-import {useDispatch, useStore} from 'react-redux';
-import {handleUnfriend} from '../../utils/friends';
+import {usersFromContactsService} from '../../services';
import {NO_USER} from '../../store/initialStates';
-import {TouchableOpacity} from 'react-native-gesture-handler';
+import {RootState} from '../../store/rootReducer';
+import {ProfilePreviewType, ScreenType} from '../../types';
+import {
+ extractContacts,
+ normalize,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+} from '../../utils';
+import {handleAddFriend, handleUnfriend} from '../../utils/friends';
+import {ProfilePreview} from '../profile';
+import FindFriendsBlueIcon from '../../assets/icons/findFriends/find-friends-blue-icon.svg';
interface FriendsProps {
result: Array<ProfilePreviewType>;
@@ -19,14 +28,101 @@ interface FriendsProps {
const Friends: React.FC<FriendsProps> = ({result, screenType, userId}) => {
const state: RootState = useStore().getState();
const dispatch = useDispatch();
-
const {user: loggedInUser = NO_USER} = state;
+ const navigation = useNavigation();
+ const [usersFromContacts, setUsersFromContacts] = useState<
+ ProfilePreviewType[]
+ >([]);
+
+ useEffect(() => {
+ const handleFindFriends = () => {
+ extractContacts().then(async (contacts) => {
+ const permission = await checkPermission();
+ if (permission === 'authorized') {
+ let response = await usersFromContactsService(contacts);
+ await setUsersFromContacts(response.existing_tagg_users);
+ } else {
+ console.log('Authorize access to contacts');
+ }
+ });
+ };
+ handleFindFriends();
+ }, []);
+
+ const UsersFromContacts = () => (
+ <>
+ {usersFromContacts?.splice(0, 2).map((profilePreview) => (
+ <View key={profilePreview.id} style={styles.container}>
+ <View style={styles.friend}>
+ <ProfilePreview
+ {...{profilePreview}}
+ previewType={'Friend'}
+ screenType={screenType}
+ />
+ </View>
+ <TouchableOpacity
+ style={styles.addFriendButton}
+ onPress={() => {
+ handleAddFriend(screenType, profilePreview, dispatch, state).then(
+ (success) => {
+ if (success) {
+ let users = usersFromContacts;
+ setUsersFromContacts(
+ users.filter(
+ (user) => user.username !== profilePreview.username,
+ ),
+ );
+ }
+ },
+ );
+ }}>
+ <Text style={styles.addFriendButtonTitle}>Add Friend</Text>
+ </TouchableOpacity>
+ </View>
+ ))}
+ </>
+ );
return (
<>
- <View style={styles.subheader}>
- {/* <Text style={styles.subheaderText}>Friends</Text> */}
- </View>
+ {loggedInUser.userId === userId && (
+ <View style={styles.subheader}>
+ <View style={styles.addFriendHeaderContainer}>
+ <Text style={[styles.subheaderText]}>Add Friends</Text>
+ <TouchableOpacity
+ style={styles.findFriendsButton}
+ onPress={async () => {
+ const permission = await checkPermission();
+ if (permission === 'authorized') {
+ navigation.navigate('InviteFriendsScreen', {
+ screenType: ScreenType.Profile,
+ });
+ } else {
+ Alert.alert(
+ '"Tagg" Would Like to Access Your Contacts',
+ 'This helps you quickly get in touch with friends on the app and more',
+ [
+ {
+ text: "Don't Allow",
+ style: 'cancel',
+ },
+ {text: 'Allow', onPress: () => Linking.openSettings()},
+ ],
+ );
+ }
+ }}>
+ <FindFriendsBlueIcon width={20} height={20} />
+ <Text style={styles.findFriendsSubheaderText}>
+ Invite Friends
+ </Text>
+ </TouchableOpacity>
+ </View>
+ <UsersFromContacts />
+ </View>
+ )}
+ <Text style={[styles.subheaderText, styles.friendsSubheaderText]}>
+ Friends
+ </Text>
<ScrollView
keyboardShouldPersistTaps={'always'}
style={styles.scrollView}
@@ -43,11 +139,11 @@ const Friends: React.FC<FriendsProps> = ({result, screenType, userId}) => {
</View>
{loggedInUser.userId === userId && (
<TouchableOpacity
- style={styles.button}
+ style={styles.unfriendButton}
onPress={() =>
handleUnfriend(screenType, profilePreview, dispatch, state)
}>
- <Text style={styles.buttonTitle}>Unfriend</Text>
+ <Text style={styles.unfriendButtonTitle}>Unfriend</Text>
</TouchableOpacity>
)}
</View>
@@ -63,12 +159,19 @@ const styles = StyleSheet.create({
alignSelf: 'center',
width: SCREEN_WIDTH * 0.85,
},
+ firstScrollView: {},
scrollViewContent: {
alignSelf: 'center',
paddingBottom: SCREEN_HEIGHT / 7,
width: SCREEN_WIDTH * 0.85,
marginTop: '1%',
},
+ addFriendHeaderContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: '3%',
+ marginTop: '2%',
+ },
header: {flexDirection: 'row'},
subheader: {
alignSelf: 'center',
@@ -81,6 +184,20 @@ const styles = StyleSheet.create({
fontWeight: '600',
lineHeight: normalize(14.32),
},
+ findFriendsButton: {flexDirection: 'row'},
+ friendsSubheaderText: {
+ alignSelf: 'center',
+ width: SCREEN_WIDTH * 0.85,
+ marginVertical: '1%',
+ marginBottom: '2%',
+ },
+ findFriendsSubheaderText: {
+ marginLeft: '5%',
+ color: '#08E2E2',
+ fontSize: normalize(12),
+ fontWeight: '600',
+ lineHeight: normalize(14.32),
+ },
container: {
alignSelf: 'center',
flexDirection: 'row',
@@ -94,7 +211,7 @@ const styles = StyleSheet.create({
alignSelf: 'center',
height: '100%',
},
- button: {
+ addFriendButton: {
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
@@ -104,9 +221,29 @@ const styles = StyleSheet.create({
borderWidth: 2,
borderRadius: 2,
padding: 0,
- backgroundColor: 'transparent',
+ 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%',
+ },
+ unfriendButton: {
+ alignSelf: 'center',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: 82,
+ height: '55%',
+ borderColor: TAGG_LIGHT_BLUE,
+ borderWidth: 2,
+ borderRadius: 2,
+ padding: 0,
},
- buttonTitle: {
+ unfriendButtonTitle: {
color: TAGG_LIGHT_BLUE,
padding: 0,
fontSize: normalize(11),
diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx
index 1d0021ad..62bda77e 100644
--- a/src/components/search/SearchBar.tsx
+++ b/src/components/search/SearchBar.tsx
@@ -70,8 +70,6 @@ const SearchBar: React.FC<SearchBarProps> = ({
// TODO: FIGURE OUT WHY CHANGES IN placeholderId ARE NOT REFLECTED HERE
// my thought: the value is set when the function is defined, so it keeps
// its inital value of -1 forever.
- // console.log(`Previous ID: ${placeholderId}`);
- // console.log(`Next ID: ${nextId}`);
setPlaceholderId(nextId);
};