diff options
Diffstat (limited to 'src/screens')
| -rw-r--r-- | src/screens/chat/ChatScreen.tsx | 3 | ||||
| -rw-r--r-- | src/screens/chat/ChatSearchBar.tsx | 14 | ||||
| -rw-r--r-- | src/screens/chat/NewChatModal.tsx | 1 | ||||
| -rw-r--r-- | src/screens/index.ts | 1 | ||||
| -rw-r--r-- | src/screens/moments/TagFriendsScreen.tsx | 152 | ||||
| -rw-r--r-- | src/screens/moments/TagSelectionScreen.tsx | 178 | ||||
| -rw-r--r-- | src/screens/moments/index.ts | 2 | ||||
| -rw-r--r-- | src/screens/onboarding/BasicInfoOnboarding.tsx | 8 | ||||
| -rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 118 | ||||
| -rw-r--r-- | src/screens/profile/InviteFriendsScreen.tsx | 93 |
10 files changed, 459 insertions, 111 deletions
diff --git a/src/screens/chat/ChatScreen.tsx b/src/screens/chat/ChatScreen.tsx index 17618867..8991d65b 100644 --- a/src/screens/chat/ChatScreen.tsx +++ b/src/screens/chat/ChatScreen.tsx @@ -21,6 +21,7 @@ import { MessageFooter, TypingIndicator, } from '../../components'; +import {TAGG_LIGHT_BLUE_2} from '../../constants/constants'; import {MainStackParams} from '../../routes'; import {ScreenType} from '../../types'; import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils'; @@ -38,7 +39,7 @@ const ChatScreen: React.FC<ChatScreenProps> = ({navigation}) => { const insets = useSafeAreaInsets(); const chatTheme: DeepPartial<Theme> = { colors: { - accent_blue: '#6EE7E7', + accent_blue: TAGG_LIGHT_BLUE_2, }, messageList: { container: { diff --git a/src/screens/chat/ChatSearchBar.tsx b/src/screens/chat/ChatSearchBar.tsx index 91018d4c..1c91f493 100644 --- a/src/screens/chat/ChatSearchBar.tsx +++ b/src/screens/chat/ChatSearchBar.tsx @@ -17,6 +17,7 @@ interface SearchBarProps extends TextInputProps { onCancel: () => void; searching: boolean; placeholder: string; + label?: string; } const ChatSearchBar: React.FC<SearchBarProps> = ({ onFocus, @@ -26,6 +27,7 @@ const ChatSearchBar: React.FC<SearchBarProps> = ({ onCancel, onLayout, placeholder, + label, }) => { const handleSubmit = ( e: NativeSyntheticEvent<TextInputSubmitEditingEventData>, @@ -34,14 +36,18 @@ const ChatSearchBar: React.FC<SearchBarProps> = ({ Keyboard.dismiss(); }; + const extraLabelStyle = {paddingLeft: label ? 0 : 10}; + return ( <View style={styles.container} onLayout={onLayout}> <Animated.View style={styles.inputContainer}> - <Animated.View style={styles.searchTextContainer}> - <Text style={styles.searchTextStyes}>To:</Text> - </Animated.View> + {label && ( + <Animated.View style={styles.searchTextContainer}> + <Text style={styles.searchTextStyes}>{label}</Text> + </Animated.View> + )} <TextInput - style={styles.input} + style={[extraLabelStyle, styles.input]} placeholderTextColor={'#828282'} onSubmitEditing={handleSubmit} clearButtonMode="always" diff --git a/src/screens/chat/NewChatModal.tsx b/src/screens/chat/NewChatModal.tsx index 9872dd6f..e57e7f7a 100644 --- a/src/screens/chat/NewChatModal.tsx +++ b/src/screens/chat/NewChatModal.tsx @@ -98,6 +98,7 @@ const NewChatModal: React.FC<NewChatModalProps> = ({ value={query} searching={searching} placeholder={''} + label={'To:'} /> {results.length > 0 && ( <View style={styles.headerContainerStyles}> diff --git a/src/screens/index.ts b/src/screens/index.ts index 44ae4b52..0c7d911f 100644 --- a/src/screens/index.ts +++ b/src/screens/index.ts @@ -6,3 +6,4 @@ export * from './suggestedPeople'; export * from './suggestedPeopleOnboarding'; export * from './badge'; export * from './chat'; +export * from './moments'; diff --git a/src/screens/moments/TagFriendsScreen.tsx b/src/screens/moments/TagFriendsScreen.tsx new file mode 100644 index 00000000..ba180921 --- /dev/null +++ b/src/screens/moments/TagFriendsScreen.tsx @@ -0,0 +1,152 @@ +import {RouteProp} from '@react-navigation/core'; +import {useNavigation} from '@react-navigation/native'; +import React, {Fragment, useEffect, useRef, useState} from 'react'; +import { + Image, + Keyboard, + KeyboardAvoidingView, + Platform, + StyleSheet, + TouchableWithoutFeedback, + View, +} from 'react-native'; +import {Button} from 'react-native-elements'; +import {MainStackParams} from 'src/routes'; +import { + CaptionScreenHeader, + MomentTags, + SearchBackground, + TaggLoadingIndicator, +} from '../../components'; +import {TagFriendsFooter} from '../../components/moments'; +import {TAGG_LIGHT_BLUE_2} from '../../constants'; +import {ProfilePreviewType} from '../../types'; +import {SCREEN_WIDTH, StatusBarHeight} from '../../utils'; + +type TagFriendsScreenRouteProps = RouteProp< + MainStackParams, + 'TagFriendsScreen' +>; +interface TagFriendsScreenProps { + route: TagFriendsScreenRouteProps; +} +const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { + const {image, selectedUsers} = route.params; + const navigation = useNavigation(); + const imageRef = useRef(null); + const [taggedUsers, setTaggedUsers] = useState<ProfilePreviewType[]>([]); + + /* + * Update list of tagged users from route params + */ + useEffect(() => { + setTaggedUsers(selectedUsers ? selectedUsers : []); + }, [route.params]); + + /* + * Navigate back to Tag Users Screen, send selected users + */ + const handleDone = () => { + navigation.navigate('CaptionScreen', { + ...route.params, + selectedUsers: taggedUsers, + }); + }; + + return ( + <SearchBackground> + <TouchableWithoutFeedback onPress={Keyboard.dismiss}> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + style={styles.flex}> + <View style={styles.contentContainer}> + <View style={styles.buttonsContainer}> + <Button + title="Cancel" + buttonStyle={styles.button} + onPress={() => navigation.goBack()} + /> + <Button + title="Done" + titleStyle={styles.shareButtonTitle} + buttonStyle={styles.button} + onPress={handleDone} + /> + </View> + <CaptionScreenHeader + style={styles.header} + title={'Tap on photo to Tag friends!'} + /> + <Image + ref={imageRef} + style={styles.image} + source={{uri: image.path}} + resizeMode={'cover'} + /> + <MomentTags + editing={true} + tags={taggedUsers.map((user) => ({ + id: '', + x: 0, + y: 0, + user, + }))} + imageRef={imageRef} + deleteFromList={(user) => + setTaggedUsers(taggedUsers.filter((u) => u.id !== user.id)) + } + /> + <View style={{marginHorizontal: '5%', marginTop: '3%'}}> + <TagFriendsFooter + taggedUsers={taggedUsers} + setTaggedUsers={setTaggedUsers} + /> + </View> + </View> + </KeyboardAvoidingView> + </TouchableWithoutFeedback> + </SearchBackground> + ); +}; + +const styles = StyleSheet.create({ + contentContainer: { + paddingTop: StatusBarHeight, + justifyContent: 'flex-end', + }, + buttonsContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + marginLeft: '5%', + marginRight: '5%', + }, + button: { + backgroundColor: 'transparent', + }, + shareButtonTitle: { + fontWeight: 'bold', + color: TAGG_LIGHT_BLUE_2, + }, + header: { + marginVertical: 20, + }, + image: { + position: 'relative', + width: SCREEN_WIDTH, + aspectRatio: 1, + marginBottom: '3%', + }, + text: { + position: 'relative', + backgroundColor: 'white', + width: '100%', + paddingHorizontal: '2%', + paddingVertical: '1%', + height: 60, + }, + flex: { + flex: 1, + }, +}); + +export default TagFriendsScreen; diff --git a/src/screens/moments/TagSelectionScreen.tsx b/src/screens/moments/TagSelectionScreen.tsx new file mode 100644 index 00000000..8d679b87 --- /dev/null +++ b/src/screens/moments/TagSelectionScreen.tsx @@ -0,0 +1,178 @@ +import {useNavigation} from '@react-navigation/core'; +import {RouteProp} from '@react-navigation/native'; +import React, {useEffect, useState} from 'react'; +import { + SafeAreaView, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import {FlatList} from 'react-native-gesture-handler'; +import BackIcon from '../../assets/icons/back-arrow.svg'; +import {SearchBar, TaggUserSelectionCell} from '../../components'; +import {SEARCH_ENDPOINT_MESSAGES} from '../../constants'; +import {MainStackParams} from '../../routes'; +import {loadSearchResults} from '../../services'; +import {ProfilePreviewType} from '../../types'; +import { + loadTaggUserSuggestions, + normalize, + SCREEN_HEIGHT, + SCREEN_WIDTH, +} from '../../utils'; + +type TagSelectionScreenRouteProps = RouteProp< + MainStackParams, + 'TagSelectionScreen' +>; +interface TagSelectionScreenProps { + route: TagSelectionScreenRouteProps; +} + +const TagSelectionScreen: React.FC<TagSelectionScreenProps> = ({route}) => { + const navigation = useNavigation(); + const [users, setUsers] = useState<ProfilePreviewType[]>([]); + const [selectedUsers, setSelectedUsers] = useState<ProfilePreviewType[]>( + route.params.selectedUsers, + ); + const [searching, setSearching] = useState(false); + const [query, setQuery] = useState<string>(''); + const [label, setLabel] = useState<string>('Recent'); + + /* + * Add back button, Send selected users to CaptionScreen + */ + useEffect(() => { + navigation.setOptions({ + headerLeft: () => ( + <TouchableOpacity + onPress={() => { + navigation.navigate('TagFriendsScreen', { + ...route.params, + selectedUsers: selectedUsers, + }); + }}> + <BackIcon + height={normalize(18)} + width={normalize(18)} + color={'black'} + style={styles.backButton} + /> + </TouchableOpacity> + ), + }); + }); + + /* + * Load the initial list users from search/suggested endpoint + * that the loggedInUser might want to select + */ + const loadUsers = async () => { + const data: ProfilePreviewType[] = await loadTaggUserSuggestions(); + const filteredData: ProfilePreviewType[] = data.filter((user) => { + const index = selectedUsers.findIndex((s) => s.id === user.id); + return index === -1; + }); + setUsers([...filteredData, ...selectedUsers]); + }; + + /* + * Load list of users based on search query + */ + const getQuerySuggested = async () => { + if (query.length > 0) { + const searchResults = await loadSearchResults( + `${SEARCH_ENDPOINT_MESSAGES}?query=${query}`, + ); + setUsers(searchResults?.users); + } else { + setUsers([]); + } + }; + + /* + * Make calls to appropriate functions to load user lists for selection + */ + useEffect(() => { + if (query.length === 0) { + setLabel('Recent'); + loadUsers(); + } + + if (!searching) { + return; + } + + if (query.length < 3) { + return; + } + setLabel(''); + getQuerySuggested(); + }, [query]); + + return ( + <SafeAreaView style={styles.safeAreaView}> + <View style={styles.searchBarContainer}> + <SearchBar + onChangeText={setQuery} + onFocus={() => { + setSearching(true); + }} + value={query} + /> + </View> + <Text style={styles.title}>{label}</Text> + {users && ( + <FlatList + data={users} + keyExtractor={(item) => item.id} + renderItem={(item) => ( + <TaggUserSelectionCell + key={item.item.id} + item={item.item} + selectedUsers={selectedUsers} + setSelectedUsers={setSelectedUsers} + /> + )} + /> + )} + </SafeAreaView> + ); +}; + +const styles = StyleSheet.create({ + safeAreaView: { + backgroundColor: 'white', + height: SCREEN_HEIGHT, + }, + backButton: { + marginLeft: 30, + marginTop: 20, + }, + searchBarContainer: { + width: SCREEN_WIDTH * 0.9, + alignSelf: 'flex-end', + marginTop: 10, + }, + searchContainer: { + alignSelf: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + height: normalize(42), + alignItems: 'center', + marginBottom: '3%', + marginHorizontal: 10, + }, + title: { + fontWeight: '700', + fontSize: normalize(17), + lineHeight: normalize(20.29), + marginHorizontal: '7%', + marginTop: '4%', + marginBottom: '2%', + }, +}); + +export default TagSelectionScreen; diff --git a/src/screens/moments/index.ts b/src/screens/moments/index.ts new file mode 100644 index 00000000..aac2ddeb --- /dev/null +++ b/src/screens/moments/index.ts @@ -0,0 +1,2 @@ +export {default as TagSelectionScreen} from './TagSelectionScreen'; +export {default as TagFriendsScreen} from './TagFriendsScreen'; diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx index 3058a04e..e5e6f59b 100644 --- a/src/screens/onboarding/BasicInfoOnboarding.tsx +++ b/src/screens/onboarding/BasicInfoOnboarding.tsx @@ -27,6 +27,7 @@ import { nameRegex, passwordRegex, phoneRegex, + TAGG_LIGHT_BLUE_2, usernameRegex, } from '../../constants'; import { @@ -70,9 +71,8 @@ const BasicInfoOnboarding: React.FC<BasicInfoOnboardingProps> = ({route}) => { const [invalidWithError, setInvalidWithError] = useState( 'Please enter a valid ', ); - const [autoCapitalize, setAutoCap] = useState< - 'none' | 'sentences' | 'words' | 'characters' | undefined - >('none'); + const [autoCapitalize, setAutoCap] = + useState<'none' | 'sentences' | 'words' | 'characters' | undefined>('none'); const [fadeValue, setFadeValue] = useState<Animated.Value<number>>( new Animated.Value(0), ); @@ -565,7 +565,7 @@ const styles = StyleSheet.create({ alignItems: 'center', }, arrow: { - color: '#6EE7E7', + color: TAGG_LIGHT_BLUE_2, }, showPassContainer: { marginVertical: '1%', diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 56fe672e..2093a1f9 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -1,6 +1,6 @@ import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {Fragment, useRef, useState} from 'react'; +import React, {Fragment, useEffect, useState} from 'react'; import { Alert, Image, @@ -8,13 +8,16 @@ import { KeyboardAvoidingView, Platform, StyleSheet, + Text, + TouchableOpacity, TouchableWithoutFeedback, View, } from 'react-native'; import {MentionInput} from 'react-native-controlled-mentions'; -import {Button} from 'react-native-elements'; +import {Button, normalize} from 'react-native-elements'; import {useDispatch, useSelector} from 'react-redux'; -import {MomentTags, SearchBackground} from '../../components'; +import FrontArrow from '../../assets/icons/front-arrow.svg'; +import {SearchBackground} from '../../components'; import {CaptionScreenHeader} from '../../components/'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import {TAGG_LIGHT_BLUE_2} from '../../constants'; @@ -26,6 +29,7 @@ import { updateProfileCompletionStage, } from '../../store/actions'; import {RootState} from '../../store/rootReducer'; +import {ProfilePreviewType} from '../../types'; import {SCREEN_WIDTH, StatusBarHeight} from '../../utils'; import {mentionPartTypes} from '../../utils/comments'; @@ -43,41 +47,34 @@ interface CaptionScreenProps { } const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { - const {title, image, screenType} = route.params; + const {title, image, screenType, selectedUsers} = route.params; const { user: {userId}, } = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const [caption, setCaption] = useState(''); const [loading, setLoading] = useState(false); - const imageRef = useRef(null); + const [taggedUsers, setTaggedUsers] = useState<ProfilePreviewType[]>([]); + const [taggedList, setTaggedList] = useState<string>(''); - const [taggList, setTaggList] = useState([ - { - first_name: 'Ivan', - id: 'cee45bf8-7f3d-43c8-99bb-ec04908efe58', - last_name: 'Chen', - thumbnail_url: - 'https://tagg-dev.s3.us-east-2.amazonaws.com/thumbnails/smallProfilePicture/spp-cee45bf8-7f3d-43c8-99bb-ec04908efe58-thumbnail.jpg', - username: 'ivan.tagg', - }, - { - first_name: 'Ankit', - id: '3bcf6947-bee6-46b0-ad02-6f4d25eaeac3', - last_name: 'Thanekar', - thumbnail_url: - 'https://tagg-dev.s3.us-east-2.amazonaws.com/thumbnails/smallProfilePicture/spp-3bcf6947-bee6-46b0-ad02-6f4d25eaeac3-thumbnail.jpg', - username: 'ankit.thanekar', - }, - { - first_name: 'Ankit', - id: '3bcf6947-bee6-46b0-ad02-6f4d25eaeac3', - last_name: 'Thanekar', - thumbnail_url: - 'https://tagg-dev.s3.us-east-2.amazonaws.com/thumbnails/smallProfilePicture/spp-3bcf6947-bee6-46b0-ad02-6f4d25eaeac3-thumbnail.jpg', - username: 'ankit.thanekar', - }, - ]); + useEffect(() => { + setTaggedUsers(selectedUsers ? selectedUsers : []); + }, [route.params]); + + useEffect(() => { + const getTaggedUsersListString = () => { + let listString = ''; + for (let i = 0; i < taggedUsers.length; i++) { + if (listString.length < 21) { + listString = listString.concat(`@${taggedUsers[i].username} `); + } else { + break; + } + } + setTaggedList(listString); + }; + getTaggedUsersListString(); + }, [taggedUsers]); const navigateToProfile = () => { //Since the logged In User is navigating to own profile, useXId is not required @@ -135,7 +132,6 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { <CaptionScreenHeader style={styles.header} {...{title: title}} /> {/* this is the image we want to center our tags' initial location within */} <Image - ref={imageRef} style={styles.image} source={{uri: image.path}} resizeMode={'cover'} @@ -148,19 +144,43 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { onChange={setCaption} partTypes={mentionPartTypes('blue')} /> - <MomentTags - editing={true} - tags={taggList.map((user) => ({ - id: '', - x: 0, - y: 0, - user, - }))} - imageRef={imageRef} - deleteFromList={(user) => - setTaggList(taggList.filter((u) => u.id !== user.id)) - } - /> + <View + style={{ + marginHorizontal: '5%', + marginTop: '3%', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }}> + <Image + source={require('../../assets/icons/tagging/tag-icon.png')} + style={{width: 20, height: 20}} + /> + <Text style={styles.tagFriendsTitle}>Tag Friends</Text> + <Text + numberOfLines={1} + style={{ + color: 'white', + width: 150, + fontSize: normalize(10), + lineHeight: normalize(11), + letterSpacing: normalize(0.3), + textAlign: 'right', + }}> + {taggedList} + {taggedList.length > 21 ? '. . .' : ''} + </Text> + <TouchableOpacity + onPress={() => + navigation.navigate('TagFriendsScreen', { + image: image, + screenType: screenType, + selectedUsers: taggedUsers, + }) + }> + <FrontArrow width={12} height={12} color={'white'} /> + </TouchableOpacity> + </View> </View> </KeyboardAvoidingView> </TouchableWithoutFeedback> @@ -205,6 +225,14 @@ const styles = StyleSheet.create({ flex: { flex: 1, }, + tagFriendsTitle: { + color: 'white', + fontSize: normalize(12), + lineHeight: normalize(16.71), + letterSpacing: normalize(0.3), + fontWeight: '600', + }, + tagFriendsContainer: {flexDirection: 'row', marginTop: '3%'}, }); export default CaptionScreen; diff --git a/src/screens/profile/InviteFriendsScreen.tsx b/src/screens/profile/InviteFriendsScreen.tsx index 4f6319f7..89f2e62f 100644 --- a/src/screens/profile/InviteFriendsScreen.tsx +++ b/src/screens/profile/InviteFriendsScreen.tsx @@ -9,14 +9,12 @@ import { StatusBar, StyleSheet, Text, - TextInput, TouchableWithoutFeedback, View, } from 'react-native'; 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 {TAGG_LIGHT_BLUE} from '../../constants'; +import {SearchBar, TabsGradient} from '../../components'; import {InviteFriendTile} from '../../components/friends'; import {headerBarOptions} from '../../routes'; import { @@ -33,7 +31,6 @@ import { SCREEN_WIDTH, StatusBarHeight, } from '../../utils'; -const AnimatedIcon = Animated.createAnimatedComponent(Icon); export type InviteContactType = { firstName: string; @@ -193,32 +190,13 @@ const InviteFriendsScreen: React.FC = () => { </Text> </View> <View style={styles.container}> - <Animated.View style={styles.inputContainer}> - <AnimatedIcon - name="search" - color={'#7E7E7E'} - size={16} - style={styles.searchIcon} - /> - <TextInput - style={[styles.input]} - placeholderTextColor={'#828282'} - clearButtonMode="while-editing" - autoCapitalize="none" - autoCorrect={false} - onChangeText={(text) => { - setQuery(text.toLowerCase()); - }} - onBlur={() => { - Keyboard.dismiss(); - }} - onEndEditing={() => { - Keyboard.dismiss(); - }} - value={query} - placeholder={'Search'} - /> - </Animated.View> + <SearchBar + onChangeText={setQuery} + onBlur={() => { + Keyboard.dismiss(); + }} + value={query} + /> </View> <View style={[ @@ -278,43 +256,44 @@ const styles = StyleSheet.create({ marginBottom: '5%', }, container: { + width: '100%', + height: normalize(42), + marginBottom: '3%', + }, + ppContainer: { alignSelf: 'center', flexDirection: 'row', justifyContent: 'space-between', width: '100%', height: normalize(42), alignItems: 'center', - marginBottom: '3%', + marginBottom: '5%', marginHorizontal: 10, }, - inputContainer: { - flexGrow: 1, - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 8, - marginHorizontal: '3%', - borderRadius: 20, - backgroundColor: '#F0F0F0', - height: 34, - }, - searchIcon: { - marginRight: '5%', - }, - input: { - flex: 1, - fontSize: normalize(16), - color: '#000', - letterSpacing: normalize(0.5), - }, - cancelButton: { + friend: { + alignSelf: 'center', height: '100%', - position: 'absolute', + }, + addFriendButton: { + alignSelf: 'center', justifyContent: 'center', - paddingHorizontal: 8, + alignItems: 'center', + width: 82, + height: 25, + borderColor: TAGG_LIGHT_BLUE, + borderWidth: 2, + borderRadius: 2, + padding: 0, + backgroundColor: TAGG_LIGHT_BLUE, }, - cancelText: { - color: '#818181', - fontWeight: '500', + addFriendButtonTitle: { + color: 'white', + padding: 0, + fontSize: normalize(11), + fontWeight: '700', + lineHeight: normalize(13.13), + letterSpacing: normalize(0.6), + paddingHorizontal: '3.8%', }, }); |
