diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/common/TaggPrompt.tsx | 4 | ||||
| -rw-r--r-- | src/components/friends/InviteFriendTile.tsx | 135 | ||||
| -rw-r--r-- | src/components/friends/index.ts | 1 | ||||
| -rw-r--r-- | src/components/moments/CaptionScreenHeader.tsx | 7 | ||||
| -rw-r--r-- | src/components/moments/IndividualMomentTitleBar.tsx | 8 | ||||
| -rw-r--r-- | src/components/moments/Moment.tsx | 22 | ||||
| -rw-r--r-- | src/components/notifications/NotificationPrompts.tsx | 58 | ||||
| -rw-r--r-- | src/components/notifications/index.ts | 1 | ||||
| -rw-r--r-- | src/components/profile/Friends.tsx | 173 | ||||
| -rw-r--r-- | src/components/search/SearchBar.tsx | 2 |
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); }; |
