From a13dcb5110245bb554d79e779c4942e6f5aaf18a Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 27 Apr 2021 10:46:17 -0400 Subject: refactored avatar --- src/components/comments/AddComment.tsx | 11 ++------ src/components/common/Avatar.tsx | 18 ++++++++++++++ src/components/common/AvatarTitle.tsx | 14 +++-------- src/components/common/index.ts | 1 + src/components/messages/ChannelPreview.tsx | 9 +++---- src/components/messages/ChatHeader.tsx | 11 +++----- src/components/messages/ChatInput.tsx | 12 +++------ src/components/messages/MessageAvatar.tsx | 15 +++++------ src/components/moments/MomentPostHeader.tsx | 4 +-- src/components/notifications/Notification.tsx | 10 ++------ src/components/profile/Avatar.tsx | 36 --------------------------- src/components/profile/ProfileHeader.tsx | 4 +-- src/components/profile/ProfilePreview.tsx | 12 +++------ src/components/profile/TaggAvatar.tsx | 35 ++++++++++++++++++++++++++ src/components/profile/index.ts | 1 + src/components/search/ExploreSectionUser.tsx | 12 +++------ src/components/taggs/TwitterTaggPost.tsx | 20 +++------------ src/screens/profile/SocialMediaTaggs.tsx | 2 +- src/utils/users.ts | 5 ---- 19 files changed, 93 insertions(+), 139 deletions(-) create mode 100644 src/components/common/Avatar.tsx delete mode 100644 src/components/profile/Avatar.tsx create mode 100644 src/components/profile/TaggAvatar.tsx (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index 3b195a2b..2a8c773e 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -1,6 +1,5 @@ import React, {useEffect, useRef} from 'react'; import { - Image, Keyboard, KeyboardAvoidingView, Platform, @@ -15,6 +14,7 @@ import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import {Avatar} from '../common'; /** * This file provides the add comment view for a user. @@ -101,14 +101,7 @@ const AddComment: React.FC = ({ keyboardVisible ? styles.whiteBackround : {}, ]}> - + ; + uri: string | undefined; +}; +const Avatar: FC = ({style, uri}) => { + return ( + + ); +}; + +export default Avatar; diff --git a/src/components/common/AvatarTitle.tsx b/src/components/common/AvatarTitle.tsx index 81351327..a2a7c0aa 100644 --- a/src/components/common/AvatarTitle.tsx +++ b/src/components/common/AvatarTitle.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import {Image, StyleSheet, View} from 'react-native'; +import {StyleSheet, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import {TAGGS_GRADIENT} from '../../constants'; +import Avatar from './Avatar'; type AvatarTitleProps = { - avatar: string | null; + avatar: string | undefined; }; const AvatarTitle: React.FC = ({avatar}) => { return ( @@ -16,14 +17,7 @@ const AvatarTitle: React.FC = ({avatar}) => { angleCenter={{x: 0.5, y: 0.5}} style={styles.gradient} /> - + ); }; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 5a601f1d..802cf505 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -23,3 +23,4 @@ export {default as FriendsButton} from './FriendsButton'; export {default as TaggSquareButton} from './TaggSquareButton'; export {default as GradientBorderButton} from './GradientBorderButton'; export {default as BasicButton} from './BasicButton'; +export {default as Avatar} from './Avatar'; diff --git a/src/components/messages/ChannelPreview.tsx b/src/components/messages/ChannelPreview.tsx index 3d31d42a..878e5a6b 100644 --- a/src/components/messages/ChannelPreview.tsx +++ b/src/components/messages/ChannelPreview.tsx @@ -22,6 +22,7 @@ import { } from '../../types'; import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {getMember, isOnline} from '../../utils/messages'; +import {Avatar} from '../common'; const ChannelPreview: React.FC< ChannelPreviewMessengerProps< @@ -87,13 +88,9 @@ const ChannelPreview: React.FC< navigation.navigate('Chat'); }}> - {online && } diff --git a/src/components/messages/ChatHeader.tsx b/src/components/messages/ChatHeader.tsx index 7ddaa7e6..37dab0fd 100644 --- a/src/components/messages/ChatHeader.tsx +++ b/src/components/messages/ChatHeader.tsx @@ -1,6 +1,6 @@ import {useNavigation} from '@react-navigation/native'; import React, {useContext} from 'react'; -import {Image, StyleSheet, View} from 'react-native'; +import {StyleSheet, View} from 'react-native'; import {Text} from 'react-native-animatable'; import {TouchableOpacity} from 'react-native-gesture-handler'; import {useDispatch, useStore} from 'react-redux'; @@ -15,6 +15,7 @@ import { userXInStore, } from '../../utils'; import {formatLastSeenText, getMember, isOnline} from '../../utils/messages'; +import {Avatar} from '../common'; type ChatHeaderProps = { screenType: ScreenType; @@ -50,13 +51,9 @@ const ChatHeader: React.FC = (props) => { - {online && } diff --git a/src/components/messages/ChatInput.tsx b/src/components/messages/ChatInput.tsx index a6e5c458..5585d246 100644 --- a/src/components/messages/ChatInput.tsx +++ b/src/components/messages/ChatInput.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {Image, StyleSheet, View} from 'react-native'; +import {StyleSheet, View} from 'react-native'; import {useStore} from 'react-redux'; import { AutoCompleteInput, @@ -17,6 +17,7 @@ import { LocalUserType, } from '../../types'; import {normalize} from '../../utils'; +import {Avatar} from '../common'; import {ChatInputSubmit} from '../messages'; const ChatInput: React.FC< @@ -65,14 +66,7 @@ const ChatInput: React.FC< return ( - + {/* TODO: Not working, toggled off for now */} diff --git a/src/components/messages/MessageAvatar.tsx b/src/components/messages/MessageAvatar.tsx index d275eae5..bcd4e7a8 100644 --- a/src/components/messages/MessageAvatar.tsx +++ b/src/components/messages/MessageAvatar.tsx @@ -1,9 +1,10 @@ import React, {useContext} from 'react'; -import {Image, StyleSheet, View} from 'react-native'; -import {getMember, normalize} from '../../utils'; -import {useMessageContext} from 'stream-chat-react-native-core'; +import {StyleSheet, View} from 'react-native'; import {useStore} from 'react-redux'; +import {useMessageContext} from 'stream-chat-react-native-core'; import {ChatContext} from '../../App'; +import {getMember, normalize} from '../../utils'; +import {Avatar} from '../common'; const MessageAvatar: React.FC = () => { const {channel} = useContext(ChatContext); @@ -14,13 +15,9 @@ const MessageAvatar: React.FC = () => { return ( {message.lastGroupMessage === true && ( - )} diff --git a/src/components/moments/MomentPostHeader.tsx b/src/components/moments/MomentPostHeader.tsx index 8cf509ab..20d9150a 100644 --- a/src/components/moments/MomentPostHeader.tsx +++ b/src/components/moments/MomentPostHeader.tsx @@ -10,7 +10,7 @@ import {MomentMoreInfoDrawer} from '../profile'; import {loadUserMoments} from '../../store/actions'; import {useDispatch, useSelector, useStore} from 'react-redux'; import {ScreenType} from '../../types'; -import Avatar from '../profile/Avatar'; +import TaggAvatar from '../profile/TaggAvatar'; import {useNavigation} from '@react-navigation/native'; import {RootState} from '../../store/rootReducer'; import {fetchUserX, userXInStore} from '../../utils'; @@ -54,7 +54,7 @@ const MomentPostHeader: React.FC = ({ return ( - = (props) => { - + {notification_type === 'SYSTEM_MSG' ? ( // Only verbage diff --git a/src/components/profile/Avatar.tsx b/src/components/profile/Avatar.tsx deleted file mode 100644 index e57a56a3..00000000 --- a/src/components/profile/Avatar.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import {Image, StyleSheet} from 'react-native'; -import {useSelector} from 'react-redux'; -import {RootState} from '../../store/rootreducer'; -import {ScreenType} from '../../types'; - -const PROFILE_DIM = 100; - -interface AvatarProps { - style?: object; - userXId: string | undefined; - screenType: ScreenType; -} -const Avatar: React.FC = ({style, screenType, userXId}) => { - const {avatar} = useSelector((state: RootState) => - userXId ? state.userX[screenType][userXId] : state.user, - ); - - return ( - - ); -}; - -const styles = StyleSheet.create({ - image: { - height: PROFILE_DIM, - width: PROFILE_DIM, - borderRadius: PROFILE_DIM / 2, - }, -}); - -export default Avatar; diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 35ec0ea9..db56b216 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -8,7 +8,7 @@ import {RootState} from '../../store/rootreducer'; import {ScreenType} from '../../types'; import {hasSeenBadgeTutorial, normalize} from '../../utils'; import BadgeDetailView from '../common/BadgeDetailView'; -import Avatar from './Avatar'; +import TaggAvatar from './TaggAvatar'; import BadgeTutorial from './BadgeTutorial'; import FriendsCount from './FriendsCount'; import ProfileMoreInfoDrawer from './ProfileMoreInfoDrawer'; @@ -107,7 +107,7 @@ const ProfileHeader: React.FC = ({ /> )} - = ({ }) => { const navigation = useNavigation(); const {user: loggedInUser} = useSelector((state: RootState) => state.user); - const [avatar, setAvatar] = useState(null); + const [avatar, setAvatar] = useState(); const dispatch = useDispatch(); useEffect(() => { @@ -187,14 +188,7 @@ const ProfilePreview: React.FC = ({ - + {(previewType === 'Search' || previewType === 'Recent') && ( <> diff --git a/src/components/profile/TaggAvatar.tsx b/src/components/profile/TaggAvatar.tsx new file mode 100644 index 00000000..ea0bdb65 --- /dev/null +++ b/src/components/profile/TaggAvatar.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import {StyleSheet} from 'react-native'; +import {useSelector} from 'react-redux'; +import {RootState} from '../../store/rootreducer'; +import {ScreenType} from '../../types'; +import {Avatar} from '../common'; + +const PROFILE_DIM = 100; + +interface TaggAvatarProps { + style?: object; + userXId: string | undefined; + screenType: ScreenType; +} +const TaggAvatar: React.FC = ({ + style, + screenType, + userXId, +}) => { + const {avatar} = useSelector((state: RootState) => + userXId ? state.userX[screenType][userXId] : state.user, + ); + + return ; +}; + +const styles = StyleSheet.create({ + image: { + height: PROFILE_DIM, + width: PROFILE_DIM, + borderRadius: PROFILE_DIM / 2, + }, +}); + +export default TaggAvatar; diff --git a/src/components/profile/index.ts b/src/components/profile/index.ts index 260f4217..c544c3f2 100644 --- a/src/components/profile/index.ts +++ b/src/components/profile/index.ts @@ -8,3 +8,4 @@ export {default as Friends} from './Friends'; export {default as ProfileMoreInfoDrawer} from './ProfileMoreInfoDrawer'; export {default as MomentMoreInfoDrawer} from './MomentMoreInfoDrawer'; export {default as UniversityIcon} from './UniversityIcon'; +export {default as TaggAvatar} from './TaggAvatar'; diff --git a/src/components/search/ExploreSectionUser.tsx b/src/components/search/ExploreSectionUser.tsx index d8c92be2..c1355e17 100644 --- a/src/components/search/ExploreSectionUser.tsx +++ b/src/components/search/ExploreSectionUser.tsx @@ -15,6 +15,7 @@ import {loadImageFromURL} from '../../services'; import {RootState} from '../../store/rootReducer'; import {ProfilePreviewType, ScreenType} from '../../types'; import {fetchUserX, normalize, userXInStore} from '../../utils'; +import {Avatar} from '../common'; /** * Search Screen for user recommendations and a search @@ -30,7 +31,7 @@ const ExploreSectionUser: React.FC = ({ externalStyles, }) => { const {id, username, first_name, last_name} = user; - const [avatar, setAvatar] = useState(null); + const [avatar, setAvatar] = useState(); const navigation = useNavigation(); const {user: loggedInUser} = useSelector((state: RootState) => state.user); const state: RootState = useStore().getState(); @@ -71,14 +72,7 @@ const ExploreSectionUser: React.FC = ({ angle={90} angleCenter={{x: 0.5, y: 0.5}} style={styles.gradient}> - + {first_name} {last_name} diff --git a/src/components/taggs/TwitterTaggPost.tsx b/src/components/taggs/TwitterTaggPost.tsx index 0a6f53d8..a64fea20 100644 --- a/src/components/taggs/TwitterTaggPost.tsx +++ b/src/components/taggs/TwitterTaggPost.tsx @@ -11,7 +11,7 @@ import { } from '../../constants'; import {TwitterPostType} from '../../types'; import {handleOpenSocialUrlOnBrowser, SCREEN_WIDTH} from '../../utils'; -import {DateLabel, PostCarousel} from '../common'; +import {Avatar, DateLabel, PostCarousel} from '../common'; interface TwitterTaggPostProps { ownerHandle: string; @@ -31,14 +31,7 @@ const TwitterTaggPost: React.FC = ({ )} {/* Post header (avatar and handle) */} - + handleOpenSocialUrlOnBrowser(post.handle, 'Twitter')}> @@ -84,14 +77,7 @@ const TwitterTaggPost: React.FC = ({ {post.in_reply_to.text !== 'This tweet is unavailable' && ( <> - + diff --git a/src/screens/profile/SocialMediaTaggs.tsx b/src/screens/profile/SocialMediaTaggs.tsx index 9186f187..52d20683 100644 --- a/src/screens/profile/SocialMediaTaggs.tsx +++ b/src/screens/profile/SocialMediaTaggs.tsx @@ -59,7 +59,7 @@ const SocialMediaTaggs: React.FC = ({ useEffect(() => { navigation.setOptions({ headerTitle: () => { - return ; + return ; }, }); }, [avatar, navigation]); diff --git a/src/utils/users.ts b/src/utils/users.ts index 7148eb79..abadaf6e 100644 --- a/src/utils/users.ts +++ b/src/utils/users.ts @@ -168,11 +168,6 @@ export const checkIfUserIsBlocked = async ( return await isUserBlocked(userId, loggedInUser.userId, token); }; -export const defaultUserProfile = () => { - const defaultImage = require('../assets/images/avatar-placeholder.png'); - return defaultImage; -}; - /** * Used to determine whether the logged-in user is able to view userX's private * information or not. -- cgit v1.2.3-70-g09d2 From a9b20af3889812b3d44ed1dedb75e4442eb35e8b Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 27 Apr 2021 10:47:35 -0400 Subject: missed a couple --- src/components/search/SearchResultCell.tsx | 8 ++------ src/screens/chat/ChatResultsCell.tsx | 10 +++------- 2 files changed, 5 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/components/search/SearchResultCell.tsx b/src/components/search/SearchResultCell.tsx index 5a6ea110..16e62a53 100644 --- a/src/components/search/SearchResultCell.tsx +++ b/src/components/search/SearchResultCell.tsx @@ -22,10 +22,10 @@ import { } from '../../utils'; import { checkIfUserIsBlocked, - defaultUserProfile, fetchUserX, userXInStore, } from '../../utils/users'; +import {Avatar} from '../common'; interface SearchResults { profileData: ProfilePreviewType; @@ -129,11 +129,7 @@ const SearchResultsCell: React.FC = ({ - + {`@${username}`} diff --git a/src/screens/chat/ChatResultsCell.tsx b/src/screens/chat/ChatResultsCell.tsx index e1eb97dd..14062810 100644 --- a/src/screens/chat/ChatResultsCell.tsx +++ b/src/screens/chat/ChatResultsCell.tsx @@ -1,13 +1,13 @@ import {useNavigation} from '@react-navigation/native'; import React, {useContext, useEffect, useState} from 'react'; -import {Alert, Image, StyleSheet, Text, View} from 'react-native'; +import {Alert, StyleSheet, Text, View} from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; import {ChatContext} from '../../App'; +import {Avatar} from '../../components'; import {ERROR_FAILED_TO_CREATE_CHANNEL} from '../../constants/strings'; import {loadImageFromURL} from '../../services'; import {ProfilePreviewType, UserType} from '../../types'; import {createChannel, normalize, SCREEN_WIDTH} from '../../utils'; -import {defaultUserProfile} from '../../utils/users'; interface ChatResults { profileData: ProfilePreviewType; @@ -58,11 +58,7 @@ const ChatResultsCell: React.FC = ({ - + {`@${username}`} -- cgit v1.2.3-70-g09d2 From 98bf284d278fe4b64d1de937d7047c746a6070dd Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 27 Apr 2021 10:52:04 -0400 Subject: removed profile pic requirement --- src/screens/onboarding/OnboardingStepThree.tsx | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/screens/onboarding/OnboardingStepThree.tsx b/src/screens/onboarding/OnboardingStepThree.tsx index 638f0889..3f88e77d 100644 --- a/src/screens/onboarding/OnboardingStepThree.tsx +++ b/src/screens/onboarding/OnboardingStepThree.tsx @@ -160,10 +160,6 @@ const OnboardingStepThree: React.FC = ({ }; const handleSubmit = async () => { - if (!form.smallPic) { - Alert.alert(ERROR_UPLOAD_SMALL_PROFILE_PIC); - return; - } if (form.classYear === -1) { Alert.alert(ERROR_SELECT_CLASS_YEAR); return; -- cgit v1.2.3-70-g09d2 From 6727fb09c25651627099ccb4abf48fbfd99a59c7 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 27 Apr 2021 10:52:40 -0400 Subject: fixed --- src/screens/onboarding/Login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx index 041fba05..3b970864 100644 --- a/src/screens/onboarding/Login.tsx +++ b/src/screens/onboarding/Login.tsx @@ -168,7 +168,7 @@ const Login: React.FC = ({navigation}: LoginProps) => { //Stores token received in the response into client's AsynStorage try { userLogin(dispatch, {userId: data.UserID, username}); - fcmService.sendFcmTokenToServer(); + fcmService.sendFcmTokenToServer(chatClient); connectChatAccount(data.UserID, chatClient); } catch (err) { Alert.alert(ERROR_INVALID_LOGIN); -- cgit v1.2.3-70-g09d2 From 64dedc2bc0c65fae604ba771ae4c17d1927ff1c2 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 27 Apr 2021 11:06:41 -0400 Subject: linted --- src/components/profile/ProfilePreview.tsx | 1 - src/components/search/ExploreSectionUser.tsx | 4 ++-- src/components/taggs/TwitterTaggPost.tsx | 5 ++++- src/screens/onboarding/OnboardingStepThree.tsx | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/components/profile/ProfilePreview.tsx b/src/components/profile/ProfilePreview.tsx index 242a17e3..66d68d8f 100644 --- a/src/components/profile/ProfilePreview.tsx +++ b/src/components/profile/ProfilePreview.tsx @@ -2,7 +2,6 @@ import {useNavigation} from '@react-navigation/native'; import React, {useEffect, useState} from 'react'; import { Alert, - Image, StyleSheet, Text, TouchableOpacity, diff --git a/src/components/search/ExploreSectionUser.tsx b/src/components/search/ExploreSectionUser.tsx index c1355e17..c949acd4 100644 --- a/src/components/search/ExploreSectionUser.tsx +++ b/src/components/search/ExploreSectionUser.tsx @@ -1,13 +1,13 @@ import {useNavigation} from '@react-navigation/native'; import React, {useEffect, useState} from 'react'; -import {TextStyle, ViewStyle} from 'react-native'; import { - Image, StyleProp, StyleSheet, Text, + TextStyle, TouchableOpacity, ViewProps, + ViewStyle, } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import {useDispatch, useSelector, useStore} from 'react-redux'; diff --git a/src/components/taggs/TwitterTaggPost.tsx b/src/components/taggs/TwitterTaggPost.tsx index a64fea20..db07ba66 100644 --- a/src/components/taggs/TwitterTaggPost.tsx +++ b/src/components/taggs/TwitterTaggPost.tsx @@ -77,7 +77,10 @@ const TwitterTaggPost: React.FC = ({ {post.in_reply_to.text !== 'This tweet is unavailable' && ( <> - + diff --git a/src/screens/onboarding/OnboardingStepThree.tsx b/src/screens/onboarding/OnboardingStepThree.tsx index 3f88e77d..34173b39 100644 --- a/src/screens/onboarding/OnboardingStepThree.tsx +++ b/src/screens/onboarding/OnboardingStepThree.tsx @@ -27,7 +27,6 @@ import { ERROR_SELECT_CLASS_YEAR, ERROR_SELECT_GENDER, ERROR_SELECT_UNIVERSITY, - ERROR_UPLOAD_SMALL_PROFILE_PIC, } from '../../constants/strings'; import {OnboardingStackParams} from '../../routes/onboarding'; import {patchEditProfile} from '../../services'; -- cgit v1.2.3-70-g09d2 From 745d04cd5a9709d1ca4dc417fa1cf0c882ea587c Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 27 Apr 2021 11:22:20 -0400 Subject: added numOfLines --- src/components/profile/ProfileHeader.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 35ec0ea9..62c2e1e3 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -45,7 +45,6 @@ const ProfileHeader: React.FC = ({ const [drawerVisible, setDrawerVisible] = useState(false); const [showBadgeView, setBadgeViewVisible] = useState(false); - const [firstName, lastName] = [...name.split(' ')]; const containerRef = useRef(null); const childRef = useRef(null); @@ -113,15 +112,9 @@ const ProfileHeader: React.FC = ({ screenType={screenType} /> - {name.length <= 16 ? ( - {name} - ) : ( - - {firstName} - {lastName} - - )} - + + {name} + Date: Mon, 3 May 2021 11:44:23 -0400 Subject: removed "University" --- src/screens/badge/BadgeScreenHeader.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/screens/badge/BadgeScreenHeader.tsx b/src/screens/badge/BadgeScreenHeader.tsx index aef49c85..81bbf42b 100644 --- a/src/screens/badge/BadgeScreenHeader.tsx +++ b/src/screens/badge/BadgeScreenHeader.tsx @@ -15,9 +15,7 @@ const BadgeScreenHeader: React.FC = ({university}) => { style={styles.icon} /> - - {university} University Badges - + {university} Badges -- cgit v1.2.3-70-g09d2 From 840e144e48fe90b33ce9ab713ca02a696887b8ea Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Mon, 3 May 2021 10:18:00 -0700 Subject: tma-822-base changes --- src/assets/images/no_chats.png | Bin 0 -> 28760 bytes src/assets/images/no_chats@2x.png | Bin 0 -> 12709 bytes src/assets/images/no_chats@3x.png | Bin 0 -> 46139 bytes src/constants/strings.ts | 2 + src/screens/chat/ChatListScreen.tsx | 4 + src/screens/main/NotificationsScreen.tsx | 2 +- .../main/notification/EmptyNotificationView.tsx | 105 ++++++++++++++++++--- src/types/types.ts | 3 + 8 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 src/assets/images/no_chats.png create mode 100644 src/assets/images/no_chats@2x.png create mode 100644 src/assets/images/no_chats@3x.png (limited to 'src') diff --git a/src/assets/images/no_chats.png b/src/assets/images/no_chats.png new file mode 100644 index 00000000..4e321f17 Binary files /dev/null and b/src/assets/images/no_chats.png differ diff --git a/src/assets/images/no_chats@2x.png b/src/assets/images/no_chats@2x.png new file mode 100644 index 00000000..d906df09 Binary files /dev/null and b/src/assets/images/no_chats@2x.png differ diff --git a/src/assets/images/no_chats@3x.png b/src/assets/images/no_chats@3x.png new file mode 100644 index 00000000..f0f05c7b Binary files /dev/null and b/src/assets/images/no_chats@3x.png differ diff --git a/src/constants/strings.ts b/src/constants/strings.ts index 50e4518b..e8f2725d 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -56,6 +56,8 @@ export const ERROR_UPLOAD_MOMENT = 'Unable to upload moment. Please retry'; export const ERROR_UPLOAD_SMALL_PROFILE_PIC = "Can't have a profile without a pic to represent you, please upload a small profile picture"; export const ERROR_UPLOAD_SP_PHOTO = 'Unable to update suggested people photo. Please retry!'; export const ERROR_VERIFICATION_FAILED_SHORT = 'Verification failed 😓'; +export const FIRST_MESSAGE = 'How about sending your first message to your friend'; +export const START_CHATTING = 'Let’s Start Chatting!'; export const MARKED_AS_MSG = (str: string) => `Marked as ${str}`; export const MOMENT_DELETED_MSG = 'Moment deleted....Some moments have to go, to create space for greater ones'; export const NO_NEW_NOTIFICATIONS = 'You have no new notifications'; diff --git a/src/screens/chat/ChatListScreen.tsx b/src/screens/chat/ChatListScreen.tsx index d2cfcb5d..810ebdb2 100644 --- a/src/screens/chat/ChatListScreen.tsx +++ b/src/screens/chat/ChatListScreen.tsx @@ -9,6 +9,7 @@ import {TabsGradient} from '../../components'; import {ChannelPreview, MessagesHeader} from '../../components/messages'; import {MainStackParams} from '../../routes'; import {RootState} from '../../store/rootReducer'; +import EmptyNotificationView from '../../screens/main/notification/EmptyNotificationView'; import { LocalAttachmentType, LocalChannelType, @@ -99,6 +100,9 @@ const ChatListScreen: React.FC = () => { sort={{last_message_at: -1}} maxUnreadCount={99} Preview={ChannelPreview} + EmptyStateIndicator={() => { + return ; + }} /> diff --git a/src/screens/main/NotificationsScreen.tsx b/src/screens/main/NotificationsScreen.tsx index 3efd9af8..06a61f04 100644 --- a/src/screens/main/NotificationsScreen.tsx +++ b/src/screens/main/NotificationsScreen.tsx @@ -304,7 +304,7 @@ const NotificationsScreen: React.FC = () => { extraData={requestLimit} ListEmptyComponent={ - + } /> diff --git a/src/screens/main/notification/EmptyNotificationView.tsx b/src/screens/main/notification/EmptyNotificationView.tsx index f43cfb2a..b80bb203 100644 --- a/src/screens/main/notification/EmptyNotificationView.tsx +++ b/src/screens/main/notification/EmptyNotificationView.tsx @@ -1,12 +1,19 @@ import React from 'react'; import {Image, Text, StyleSheet, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; -import {UP_TO_DATE, NO_NEW_NOTIFICATIONS} from '../../../constants/strings'; +import { + UP_TO_DATE, + NO_NEW_NOTIFICATIONS, + FIRST_MESSAGE, + START_CHATTING, +} from '../../../constants/strings'; import {NOTIFICATION_GRADIENT} from '../../../constants/constants'; -import {SCREEN_HEIGHT, normalize} from '../../../utils'; -const EmptyNotificationView: React.FC = () => { - return ( - +import {SCREEN_HEIGHT, normalize, SCREEN_WIDTH} from '../../../utils'; +import {EmptyViewProps} from '../../../types/index'; + +const EmptyNotificationView: React.FC = ({viewType}) => { + const _getNotificationImage = () => { + return ( { source={require('../../../assets/images/empty_notifications.png')} /> - - {UP_TO_DATE} - - - {NO_NEW_NOTIFICATIONS} + ); + }; + + const _getChatImage = () => { + return ( + + + + ); + }; + + const _getImageForType = () => { + switch (viewType) { + case 'Notification': + return _getNotificationImage(); + case 'ChatList': + return _getChatImage(); + } + }; + + const _getTextForNotification = () => { + return ( + <> + + {UP_TO_DATE} + + + {NO_NEW_NOTIFICATIONS} + + + ); + }; + + const _getTextForChat = () => { + return ( + + + {START_CHATTING} + + + {FIRST_MESSAGE} + + ); + }; + + const _getTextForType = () => { + switch (viewType) { + case 'Notification': + return _getTextForNotification(); + case 'ChatList': + return _getTextForChat(); + } + }; + + return ( + + {_getImageForType()} + {_getTextForType()} ); }; const styles = StyleSheet.create({ - container: {alignItems: 'center'}, + container: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, topMargin: {marginTop: SCREEN_HEIGHT * 0.025}, upperTextStyle: { + textAlign: 'center', fontWeight: '700', fontSize: normalize(23), lineHeight: normalize(40), }, + chatTextStyles: { + width: '85%', + }, bottomTextStyle: { + textAlign: 'center', color: '#2D3B45', fontWeight: '600', fontSize: normalize(20), lineHeight: normalize(40), }, + imageStyles: { + width: SCREEN_WIDTH * 0.72, + height: SCREEN_WIDTH * 0.72, + }, backgroundLinearView: { - borderRadius: 135.5, + borderRadius: (SCREEN_WIDTH * 0.72) / 2, }, }); diff --git a/src/types/types.ts b/src/types/types.ts index e7f651dc..ce39947c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -23,6 +23,9 @@ export interface CategoryPreviewType { } export type FriendshipStatusType = 'friends' | 'requested' | 'no_record'; +export interface EmptyViewProps { + viewType: 'Notification' | 'ChatList'; +} export enum UniversityType { Brown = 'Brown University', -- cgit v1.2.3-70-g09d2 From ef2c8f57971b6ca55a6a3ef58609b0c80fe55869 Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Mon, 3 May 2021 10:23:01 -0700 Subject: tma-822-base UI changes --- src/components/common/EmptyContentView.tsx | 129 +++++++++++++++++++++ src/screens/chat/ChatListScreen.tsx | 4 +- src/screens/main/NotificationsScreen.tsx | 4 +- .../main/notification/EmptyNotificationView.tsx | 129 --------------------- 4 files changed, 133 insertions(+), 133 deletions(-) create mode 100644 src/components/common/EmptyContentView.tsx delete mode 100644 src/screens/main/notification/EmptyNotificationView.tsx (limited to 'src') diff --git a/src/components/common/EmptyContentView.tsx b/src/components/common/EmptyContentView.tsx new file mode 100644 index 00000000..1416c556 --- /dev/null +++ b/src/components/common/EmptyContentView.tsx @@ -0,0 +1,129 @@ +import React from 'react'; +import {Image, Text, StyleSheet, View} from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import { + UP_TO_DATE, + NO_NEW_NOTIFICATIONS, + FIRST_MESSAGE, + START_CHATTING, +} from '../../constants/strings'; +import {NOTIFICATION_GRADIENT} from '../../constants/constants'; +import {SCREEN_HEIGHT, normalize, SCREEN_WIDTH} from '../../utils'; +import {EmptyViewProps} from '../../types/index'; + +const EmptyNotificationView: React.FC = ({viewType}) => { + const _getNotificationImage = () => { + return ( + + + + ); + }; + + const _getChatImage = () => { + return ( + + + + ); + }; + + const _getImageForType = () => { + switch (viewType) { + case 'Notification': + return _getNotificationImage(); + case 'ChatList': + return _getChatImage(); + } + }; + + const _getTextForNotification = () => { + return ( + <> + + {UP_TO_DATE} + + + {NO_NEW_NOTIFICATIONS} + + + ); + }; + + const _getTextForChat = () => { + return ( + + + {START_CHATTING} + + + {FIRST_MESSAGE} + + + ); + }; + + const _getTextForType = () => { + switch (viewType) { + case 'Notification': + return _getTextForNotification(); + case 'ChatList': + return _getTextForChat(); + } + }; + + return ( + + {_getImageForType()} + {_getTextForType()} + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + topMargin: {marginTop: SCREEN_HEIGHT * 0.025}, + upperTextStyle: { + textAlign: 'center', + fontWeight: '700', + fontSize: normalize(23), + lineHeight: normalize(40), + }, + chatTextStyles: { + width: '85%', + }, + bottomTextStyle: { + textAlign: 'center', + color: '#2D3B45', + fontWeight: '600', + fontSize: normalize(20), + lineHeight: normalize(40), + }, + imageStyles: { + width: SCREEN_WIDTH * 0.72, + height: SCREEN_WIDTH * 0.72, + }, + backgroundLinearView: { + borderRadius: (SCREEN_WIDTH * 0.72) / 2, + }, +}); + +export default EmptyNotificationView; diff --git a/src/screens/chat/ChatListScreen.tsx b/src/screens/chat/ChatListScreen.tsx index 810ebdb2..1df5c2da 100644 --- a/src/screens/chat/ChatListScreen.tsx +++ b/src/screens/chat/ChatListScreen.tsx @@ -9,7 +9,7 @@ import {TabsGradient} from '../../components'; import {ChannelPreview, MessagesHeader} from '../../components/messages'; import {MainStackParams} from '../../routes'; import {RootState} from '../../store/rootReducer'; -import EmptyNotificationView from '../../screens/main/notification/EmptyNotificationView'; +import EmptyContentView from '../../components/common/EmptyContentView'; import { LocalAttachmentType, LocalChannelType, @@ -101,7 +101,7 @@ const ChatListScreen: React.FC = () => { maxUnreadCount={99} Preview={ChannelPreview} EmptyStateIndicator={() => { - return ; + return ; }} /> diff --git a/src/screens/main/NotificationsScreen.tsx b/src/screens/main/NotificationsScreen.tsx index 06a61f04..9fbc4cfe 100644 --- a/src/screens/main/NotificationsScreen.tsx +++ b/src/screens/main/NotificationsScreen.tsx @@ -28,7 +28,7 @@ import { import {RootState} from '../../store/rootReducer'; import {NotificationType, ScreenType} from '../../types'; import {getDateAge, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import EmptyNotificationView from './notification/EmptyNotificationView'; +import EmptyContentView from '../../components/common/EmptyContentView'; const NotificationsScreen: React.FC = () => { const {newNotificationReceived} = useSelector( @@ -304,7 +304,7 @@ const NotificationsScreen: React.FC = () => { extraData={requestLimit} ListEmptyComponent={ - + } /> diff --git a/src/screens/main/notification/EmptyNotificationView.tsx b/src/screens/main/notification/EmptyNotificationView.tsx deleted file mode 100644 index b80bb203..00000000 --- a/src/screens/main/notification/EmptyNotificationView.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React from 'react'; -import {Image, Text, StyleSheet, View} from 'react-native'; -import LinearGradient from 'react-native-linear-gradient'; -import { - UP_TO_DATE, - NO_NEW_NOTIFICATIONS, - FIRST_MESSAGE, - START_CHATTING, -} from '../../../constants/strings'; -import {NOTIFICATION_GRADIENT} from '../../../constants/constants'; -import {SCREEN_HEIGHT, normalize, SCREEN_WIDTH} from '../../../utils'; -import {EmptyViewProps} from '../../../types/index'; - -const EmptyNotificationView: React.FC = ({viewType}) => { - const _getNotificationImage = () => { - return ( - - - - ); - }; - - const _getChatImage = () => { - return ( - - - - ); - }; - - const _getImageForType = () => { - switch (viewType) { - case 'Notification': - return _getNotificationImage(); - case 'ChatList': - return _getChatImage(); - } - }; - - const _getTextForNotification = () => { - return ( - <> - - {UP_TO_DATE} - - - {NO_NEW_NOTIFICATIONS} - - - ); - }; - - const _getTextForChat = () => { - return ( - - - {START_CHATTING} - - - {FIRST_MESSAGE} - - - ); - }; - - const _getTextForType = () => { - switch (viewType) { - case 'Notification': - return _getTextForNotification(); - case 'ChatList': - return _getTextForChat(); - } - }; - - return ( - - {_getImageForType()} - {_getTextForType()} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - }, - topMargin: {marginTop: SCREEN_HEIGHT * 0.025}, - upperTextStyle: { - textAlign: 'center', - fontWeight: '700', - fontSize: normalize(23), - lineHeight: normalize(40), - }, - chatTextStyles: { - width: '85%', - }, - bottomTextStyle: { - textAlign: 'center', - color: '#2D3B45', - fontWeight: '600', - fontSize: normalize(20), - lineHeight: normalize(40), - }, - imageStyles: { - width: SCREEN_WIDTH * 0.72, - height: SCREEN_WIDTH * 0.72, - }, - backgroundLinearView: { - borderRadius: (SCREEN_WIDTH * 0.72) / 2, - }, -}); - -export default EmptyNotificationView; -- cgit v1.2.3-70-g09d2 From 2abe11e6b231410c7bd531adabfe9158109411cb Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Mon, 3 May 2021 12:16:35 -0700 Subject: File name changes --- src/components/common/EmptyContentView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/components/common/EmptyContentView.tsx b/src/components/common/EmptyContentView.tsx index 1416c556..e3aaf621 100644 --- a/src/components/common/EmptyContentView.tsx +++ b/src/components/common/EmptyContentView.tsx @@ -11,7 +11,7 @@ import {NOTIFICATION_GRADIENT} from '../../constants/constants'; import {SCREEN_HEIGHT, normalize, SCREEN_WIDTH} from '../../utils'; import {EmptyViewProps} from '../../types/index'; -const EmptyNotificationView: React.FC = ({viewType}) => { +const EmptyContentView: React.FC = ({viewType}) => { const _getNotificationImage = () => { return ( Date: Mon, 3 May 2021 12:38:59 -0700 Subject: TMA-815-Effect for monitoring Changes in Drawer --- src/components/profile/ProfileHeader.tsx | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 35ec0ea9..1231717f 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -86,6 +86,10 @@ const ProfileHeader: React.FC = ({ } }; + useEffect(() => { + setDrawerVisible(drawerVisible); + }, [drawerVisible]); + return ( = ({ userXName={userXName} setIsOpen={setDrawerVisible} /> + {userId === loggedInUserId && measure && ( Date: Mon, 3 May 2021 12:56:57 -0700 Subject: tma-816-tab hidden --- src/screens/chat/ChatScreen.tsx | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/screens/chat/ChatScreen.tsx b/src/screens/chat/ChatScreen.tsx index 5874b8b6..efd16cc3 100644 --- a/src/screens/chat/ChatScreen.tsx +++ b/src/screens/chat/ChatScreen.tsx @@ -1,6 +1,6 @@ -import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs'; +import {useFocusEffect} from '@react-navigation/core'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useContext, useEffect} from 'react'; +import React, {useCallback, useContext, useEffect} from 'react'; import {StyleSheet} from 'react-native'; import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'; import { @@ -9,8 +9,8 @@ import { DeepPartial, MessageInput, MessageList, - useAttachmentPickerContext, Theme, + useAttachmentPickerContext, } from 'stream-chat-react-native'; import {ChatContext} from '../../App'; import { @@ -19,12 +19,11 @@ import { DateHeader, MessageAvatar, MessageFooter, - TabsGradient, TypingIndicator, } from '../../components'; import {MainStackParams} from '../../routes'; import {ScreenType} from '../../types'; -import {HeaderHeight, isIPhoneX, normalize, SCREEN_WIDTH} from '../../utils'; +import {HeaderHeight, normalize, SCREEN_WIDTH} from '../../utils'; type ChatScreenNavigationProp = StackNavigationProp; interface ChatScreenProps { @@ -33,9 +32,8 @@ interface ChatScreenProps { /* * Screen that displays all of the user's active conversations. */ -const ChatScreen: React.FC = () => { +const ChatScreen: React.FC = ({navigation}) => { const {channel, chatClient} = useContext(ChatContext); - const tabbarHeight = useBottomTabBarHeight(); const {setTopInset} = useAttachmentPickerContext(); const insets = useSafeAreaInsets(); const chatTheme: DeepPartial = { @@ -129,12 +127,25 @@ const ChatScreen: React.FC = () => { setTopInset(insets.top + HeaderHeight); }); + //Function to get the parent TabBar navigator and setting the option for this screen. + useFocusEffect( + useCallback(() => { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: false, + }); + return () => { + navigation.dangerouslyGetParent()?.setOptions({ + tabBarVisible: true, + }); + }; + }, [navigation]), + ); + return ( @@ -157,7 +168,6 @@ const ChatScreen: React.FC = () => { - ); }; @@ -167,6 +177,7 @@ const styles = StyleSheet.create({ backgroundColor: 'white', flex: 1, }, + textBoxStyles: {paddingBottom: 60}, }); export default ChatScreen; -- cgit v1.2.3-70-g09d2 From 355829d9983b839db342881011cc3fe9ff3792f4 Mon Sep 17 00:00:00 2001 From: Shravya Ramesh Date: Tue, 4 May 2021 07:45:08 -0700 Subject: style to look more like figma --- src/components/common/EmptyContentView.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/components/common/EmptyContentView.tsx b/src/components/common/EmptyContentView.tsx index e3aaf621..14ad4af1 100644 --- a/src/components/common/EmptyContentView.tsx +++ b/src/components/common/EmptyContentView.tsx @@ -100,7 +100,10 @@ const styles = StyleSheet.create({ justifyContent: 'center', alignItems: 'center', }, - topMargin: {marginTop: SCREEN_HEIGHT * 0.025}, + topMargin: { + marginTop: SCREEN_HEIGHT * 0.025, + paddingBottom: '5%', + }, upperTextStyle: { textAlign: 'center', fontWeight: '700', @@ -112,10 +115,10 @@ const styles = StyleSheet.create({ }, bottomTextStyle: { textAlign: 'center', - color: '#2D3B45', + color: '#808080', fontWeight: '600', fontSize: normalize(20), - lineHeight: normalize(40), + lineHeight: normalize(30), }, imageStyles: { width: SCREEN_WIDTH * 0.72, -- cgit v1.2.3-70-g09d2 From f1d3cd4d27cac2d800c21986dc77ab327602268f Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Tue, 4 May 2021 09:37:45 -0700 Subject: updated Comment --- src/screens/chat/ChatScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/screens/chat/ChatScreen.tsx b/src/screens/chat/ChatScreen.tsx index efd16cc3..a8e975eb 100644 --- a/src/screens/chat/ChatScreen.tsx +++ b/src/screens/chat/ChatScreen.tsx @@ -145,7 +145,7 @@ const ChatScreen: React.FC = ({navigation}) => { -- cgit v1.2.3-70-g09d2 From f0c5a226d4ae02cd92d92372708056f63d3cd976 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 29 Apr 2021 16:32:30 -0400 Subject: added ParsedText, initial work for typeahead --- package.json | 1 + src/components/comments/AddComment.tsx | 48 +++++++++++++++++++++++----- src/components/common/TaggTypeahead.tsx | 25 +++++++++++++++ src/components/common/index.ts | 1 + src/screens/profile/MomentCommentsScreen.tsx | 34 +------------------- yarn.lock | 9 +++++- 6 files changed, 76 insertions(+), 42 deletions(-) create mode 100644 src/components/common/TaggTypeahead.tsx (limited to 'src') diff --git a/package.json b/package.json index 2612e67f..b3d69c76 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "react-native-image-resizer": "^1.4.4", "react-native-inappbrowser-reborn": "^3.4.0", "react-native-linear-gradient": "^2.5.6", + "react-native-parsed-text": "^0.0.22", "react-native-picker-select": "^7.0.0", "react-native-push-notifications": "^3.0.10", "react-native-reanimated": "2.0.0-rc.0", diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index 2a8c773e..79d4d970 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -1,12 +1,14 @@ -import React, {useEffect, useRef} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import { Keyboard, KeyboardAvoidingView, Platform, StyleSheet, + TextInput, View, } from 'react-native'; -import {TextInput, TouchableOpacity} from 'react-native-gesture-handler'; +import {TouchableOpacity} from 'react-native-gesture-handler'; +import ParsedText from 'react-native-parsed-text'; import {useDispatch, useSelector} from 'react-redux'; import UpArrowIcon from '../../assets/icons/up_arrow.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; @@ -14,7 +16,7 @@ import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import {Avatar} from '../common'; +import {Avatar, TaggTypeahead} from '../common'; /** * This file provides the add comment view for a user. @@ -36,7 +38,9 @@ const AddComment: React.FC = ({ isCommentInFocus, }) => { const [comment, setComment] = React.useState(''); - const [keyboardVisible, setKeyboardVisible] = React.useState(false); + const [keyboardVisible, setKeyboardVisible] = useState(false); + const [isMentioning, setIsMentioning] = useState(false); + const [mentionSearch, setMentionSearch] = useState(''); const {avatar} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); @@ -95,6 +99,7 @@ const AddComment: React.FC = ({ + {isMentioning && } = ({ style={styles.text} placeholder={placeholderText} placeholderTextColor="grey" - onChangeText={setComment} - value={comment} + onChangeText={(newText: string) => { + const newestChar = newText[newText.length - 1]; + if (newestChar === ' ') { + setIsMentioning(false); + setMentionSearch(''); + } + if (newestChar === '@') { + setIsMentioning(true); + } + if (isMentioning) { + const match = newText.match(/@(.*)$/); + if (match) { + setMentionSearch(match[1]); + } + } + setComment(newText); + }} multiline={true} - ref={ref} - /> + ref={ref}> + '@fooo', + }, + ]}> + {comment} + + @@ -141,6 +172,7 @@ const styles = StyleSheet.create({ flex: 1, padding: '1%', marginHorizontal: '1%', + maxHeight: 100, }, avatar: { height: 35, diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx new file mode 100644 index 00000000..8a68c9a5 --- /dev/null +++ b/src/components/common/TaggTypeahead.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {StyleSheet, Text, View} from 'react-native'; +import {SCREEN_WIDTH} from '../../utils'; + +type TaggTypeaheadProps = { + search: string; +}; + +const TaggTypeahead: React.FC = ({search}) => { + return ( + + searching for {search} + + ); +}; + +const styles = StyleSheet.create({ + container: { + width: SCREEN_WIDTH * 0.9, + height: 100, + borderWidth: 1, + }, +}); + +export default TaggTypeahead; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 802cf505..4a49339b 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -24,3 +24,4 @@ export {default as TaggSquareButton} from './TaggSquareButton'; export {default as GradientBorderButton} from './GradientBorderButton'; export {default as BasicButton} from './BasicButton'; export {default as Avatar} from './Avatar'; +export {default as TaggTypeahead} from './TaggTypeahead'; diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx index b0208f6f..effbbb63 100644 --- a/src/screens/profile/MomentCommentsScreen.tsx +++ b/src/screens/profile/MomentCommentsScreen.tsx @@ -8,12 +8,7 @@ import CommentsContainer from '../../components/comments/CommentsContainer'; import {ADD_COMMENT_TEXT} from '../../constants/strings'; import {headerBarOptions, MainStackParams} from '../../routes/main'; import {CommentType} from '../../types'; -import { - HeaderHeight, - normalize, - SCREEN_HEIGHT, - SCREEN_WIDTH, -} from '../../utils'; +import {HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; /** * Comments Screen for an image uploaded @@ -88,39 +83,12 @@ const styles = StyleSheet.create({ backgroundColor: 'white', height: '100%', }, - header: {justifyContent: 'center', padding: '3%'}, - headerText: { - position: 'absolute', - alignSelf: 'center', - fontSize: normalize(18), - fontWeight: '700', - lineHeight: normalize(21.48), - letterSpacing: normalize(1.3), - }, - headerButton: { - width: '5%', - aspectRatio: 1, - padding: 0, - marginLeft: '5%', - alignSelf: 'flex-start', - }, - headerButtonText: { - color: 'black', - fontSize: 18, - fontWeight: '400', - }, body: { marginTop: HeaderHeight, width: SCREEN_WIDTH * 0.9, height: SCREEN_HEIGHT * 0.8, paddingTop: '3%', }, - scrollView: { - paddingHorizontal: 20, - }, - scrollViewContent: { - justifyContent: 'center', - }, }); export default MomentCommentsScreen; diff --git a/yarn.lock b/yarn.lock index d9aab38e..ef159ae5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6148,7 +6148,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.7.x: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -6367,6 +6367,13 @@ react-native-markdown-package@1.8.1: react-native-lightbox "^0.7.0" simple-markdown "^0.7.1" +react-native-parsed-text@^0.0.22: + version "0.0.22" + resolved "https://registry.yarnpkg.com/react-native-parsed-text/-/react-native-parsed-text-0.0.22.tgz#a23c756eaa5d6724296814755085127f9072e5f5" + integrity sha512-hfD83RDXZf9Fvth3DowR7j65fMnlqM9PpxZBGWkzVcUTFtqe6/yPcIoIAgrJbKn6YmtzkivmhWE2MCE4JKBXrQ== + dependencies: + prop-types "^15.7.x" + react-native-picker-select@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/react-native-picker-select/-/react-native-picker-select-7.0.0.tgz#4808a1177f997e234bb8505849dfffe1a01fedac" -- cgit v1.2.3-70-g09d2 From bdc7186d869ff969c3e19cb9a4a8839df51a7f32 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 29 Apr 2021 19:34:10 -0400 Subject: completed typeahead missed import --- src/components/common/TaggTypeahead.tsx | 57 ++++++++++++++++++++++++++----- src/components/common/TaggUserRowCell.tsx | 55 +++++++++++++++++++++++++++++ src/components/common/index.ts | 1 + 3 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 src/components/common/TaggUserRowCell.tsx (limited to 'src') diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx index 8a68c9a5..6239971e 100644 --- a/src/components/common/TaggTypeahead.tsx +++ b/src/components/common/TaggTypeahead.tsx @@ -1,23 +1,64 @@ -import React from 'react'; -import {StyleSheet, Text, View} from 'react-native'; +import React, {Fragment, useEffect, useState} from 'react'; +import {ScrollView, StyleSheet} from 'react-native'; +import {SEARCH_ENDPOINT_MESSAGES} from '../../constants'; +import {loadSearchResults} from '../../services'; +import {ProfilePreviewType} from '../../types'; import {SCREEN_WIDTH} from '../../utils'; +import TaggUserRowCell from './TaggUserRowCell'; type TaggTypeaheadProps = { - search: string; + query: string; + setSelectedMention: (user: ProfilePreviewType) => void; }; -const TaggTypeahead: React.FC = ({search}) => { +const TaggTypeahead: React.FC = ({ + query, + setSelectedMention, +}) => { + const [results, setResults] = useState([]); + + useEffect(() => { + getQuerySuggested(); + }, [query]); + + const getQuerySuggested = async () => { + if (query.length < 3) { + setResults([]); + return; + } + const searchResults = await loadSearchResults( + `${SEARCH_ENDPOINT_MESSAGES}?query=${query}`, + ); + if (searchResults && searchResults.users) { + setResults(searchResults.users); + } + }; + + if (results.length === 0) { + return ; + } + return ( - - searching for {search} - + + {results.map((user) => ( + { + setSelectedMention(user); + setResults([]); + }} + user={user} + /> + ))} + ); }; const styles = StyleSheet.create({ container: { + marginLeft: SCREEN_WIDTH * 0.05, width: SCREEN_WIDTH * 0.9, - height: 100, + maxHeight: 300, + borderRadius: 10, borderWidth: 1, }, }); diff --git a/src/components/common/TaggUserRowCell.tsx b/src/components/common/TaggUserRowCell.tsx new file mode 100644 index 00000000..446f5e87 --- /dev/null +++ b/src/components/common/TaggUserRowCell.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; +import {ProfilePreviewType} from '../../types'; +import {defaultUserProfile, normalize} from '../../utils'; + +type TaggUserRowCellProps = { + onPress: () => void; + user: ProfilePreviewType; +}; +const TaggUserRowCell: React.FC = ({onPress, user}) => { + return ( + + + + {`@${user.username}`} + + {user.first_name} {user.last_name} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + paddingHorizontal: 25, + paddingVertical: 15, + width: '100%', + }, + image: { + width: normalize(30), + height: normalize(30), + borderRadius: 30, + }, + textContent: { + flexDirection: 'column', + justifyContent: 'space-between', + marginLeft: 20, + }, + username: { + fontWeight: '500', + fontSize: normalize(14), + }, + name: { + fontWeight: '500', + fontSize: normalize(12), + color: '#828282', + }, +}); +export default TaggUserRowCell; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 4a49339b..b38056c6 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -25,3 +25,4 @@ export {default as GradientBorderButton} from './GradientBorderButton'; export {default as BasicButton} from './BasicButton'; export {default as Avatar} from './Avatar'; export {default as TaggTypeahead} from './TaggTypeahead'; +export {default as TaggUserRowCell} from './TaggUserRowCell'; -- cgit v1.2.3-70-g09d2 From b7482f61fc5c9d62651842e7647d2ff95d47c785 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Thu, 29 Apr 2021 19:35:42 -0400 Subject: finished logic with bugs --- src/components/comments/AddComment.tsx | 67 ++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index 79d4d970..97c87299 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -8,13 +8,14 @@ import { View, } from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; -import ParsedText from 'react-native-parsed-text'; +import ParsedText, {ParseShape} from 'react-native-parsed-text'; import {useDispatch, useSelector} from 'react-redux'; import UpArrowIcon from '../../assets/icons/up_arrow.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; +import {ProfilePreviewType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {Avatar, TaggTypeahead} from '../common'; @@ -37,13 +38,16 @@ const AddComment: React.FC = ({ placeholderText, isCommentInFocus, }) => { - const [comment, setComment] = React.useState(''); + const [comment, setComment] = useState(''); const [keyboardVisible, setKeyboardVisible] = useState(false); const [isMentioning, setIsMentioning] = useState(false); - const [mentionSearch, setMentionSearch] = useState(''); - + const [mentionQuery, setMentionQuery] = useState(''); + const [selectedMention, setSelectedMention] = useState(); + const [mentions, setMentions] = useState([]); const {avatar} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); + const ref = useRef(null); + const [parsePatterns, setParsePatterns] = useState([]); const addComment = async () => { const trimmed = comment.trim(); @@ -74,6 +78,30 @@ const AddComment: React.FC = ({ } }; + useEffect(() => { + setParsePatterns( + mentions.map((m) => ({ + pattern: new RegExp(`@${m.username}`), + style: {color: TAGG_LIGHT_BLUE}, + })), + ); + }, [mentions]); + + useEffect(() => { + if (selectedMention) { + setComment( + comment.replace( + new RegExp(`@${mentionQuery}`), + `@${selectedMention.username} `, + ), + ); + setMentions([...mentions, selectedMention]); + setSelectedMention(undefined); + setMentionQuery(''); + setIsMentioning(false); + } + }, [selectedMention]); + useEffect(() => { const showKeyboard = () => setKeyboardVisible(true); Keyboard.addListener('keyboardWillShow', showKeyboard); @@ -86,8 +114,6 @@ const AddComment: React.FC = ({ return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard); }, []); - const ref = useRef(null); - //If a comment is in Focus, bring the keyboard up so user is able to type in a reply useEffect(() => { if (isCommentInFocus) { @@ -99,7 +125,12 @@ const AddComment: React.FC = ({ - {isMentioning && } + {isMentioning && ( + + )} = ({ placeholderTextColor="grey" onChangeText={(newText: string) => { const newestChar = newText[newText.length - 1]; - if (newestChar === ' ') { + const deletedChar = + newText.length === comment.length - 1 + ? comment[comment.length - 1] + : undefined; + if (newestChar === ' ' || deletedChar === '@') { setIsMentioning(false); - setMentionSearch(''); + setMentionQuery(''); } if (newestChar === '@') { setIsMentioning(true); } if (isMentioning) { - const match = newText.match(/@(.*)$/); + const match = newText.match(/.*@(.*)$/); if (match) { - setMentionSearch(match[1]); + setMentionQuery(match[1]); } } setComment(newText); }} multiline={true} ref={ref}> - '@fooo', - }, - ]}> + {comment} -- cgit v1.2.3-70-g09d2 From a665c527cf0ab24b1a19b2ed35f38b9acb79cdeb Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 30 Apr 2021 15:16:32 -0400 Subject: removed old code, added controlled-mention --- package.json | 3 +- src/components/comments/AddComment.tsx | 83 +++++++-------------------------- src/components/common/TaggTypeahead.tsx | 37 +++++++++------ 3 files changed, 41 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index b3d69c76..d3c02059 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "react-native-animatable": "^1.3.3", "react-native-confirmation-code-field": "^6.5.0", "react-native-contacts": "^6.0.4", + "react-native-controlled-mentions": "^2.2.5", "react-native-date-picker": "^3.2.5", "react-native-device-info": "^7.3.1", "react-native-document-picker": "^5.0.3", @@ -102,4 +103,4 @@ "./node_modules/react-native-gesture-handler/jestSetup.js" ] } -} +} \ No newline at end of file diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index 97c87299..7576675e 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -7,15 +7,14 @@ import { TextInput, View, } from 'react-native'; +import {MentionInput} from 'react-native-controlled-mentions'; import {TouchableOpacity} from 'react-native-gesture-handler'; -import ParsedText, {ParseShape} from 'react-native-parsed-text'; import {useDispatch, useSelector} from 'react-redux'; import UpArrowIcon from '../../assets/icons/up_arrow.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; -import {ProfilePreviewType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {Avatar, TaggTypeahead} from '../common'; @@ -40,14 +39,9 @@ const AddComment: React.FC = ({ }) => { const [comment, setComment] = useState(''); const [keyboardVisible, setKeyboardVisible] = useState(false); - const [isMentioning, setIsMentioning] = useState(false); - const [mentionQuery, setMentionQuery] = useState(''); - const [selectedMention, setSelectedMention] = useState(); - const [mentions, setMentions] = useState([]); const {avatar} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const ref = useRef(null); - const [parsePatterns, setParsePatterns] = useState([]); const addComment = async () => { const trimmed = comment.trim(); @@ -78,30 +72,6 @@ const AddComment: React.FC = ({ } }; - useEffect(() => { - setParsePatterns( - mentions.map((m) => ({ - pattern: new RegExp(`@${m.username}`), - style: {color: TAGG_LIGHT_BLUE}, - })), - ); - }, [mentions]); - - useEffect(() => { - if (selectedMention) { - setComment( - comment.replace( - new RegExp(`@${mentionQuery}`), - `@${selectedMention.username} `, - ), - ); - setMentions([...mentions, selectedMention]); - setSelectedMention(undefined); - setMentionQuery(''); - setIsMentioning(false); - } - }, [selectedMention]); - useEffect(() => { const showKeyboard = () => setKeyboardVisible(true); Keyboard.addListener('keyboardWillShow', showKeyboard); @@ -125,12 +95,6 @@ const AddComment: React.FC = ({ - {isMentioning && ( - - )} = ({ ]}> - { - const newestChar = newText[newText.length - 1]; - const deletedChar = - newText.length === comment.length - 1 - ? comment[comment.length - 1] - : undefined; - if (newestChar === ' ' || deletedChar === '@') { - setIsMentioning(false); - setMentionQuery(''); - } - if (newestChar === '@') { - setIsMentioning(true); - } - if (isMentioning) { - const match = newText.match(/.*@(.*)$/); - if (match) { - setMentionQuery(match[1]); - } - } - setComment(newText); - }} - multiline={true} - ref={ref}> - - {comment} - - + value={comment} + onChange={setComment} + inputRef={ref} + partTypes={[ + { + trigger: '@', + renderSuggestions: (props) => , + allowedSpacesCount: 0, + isInsertSpaceAfterMention: true, + textStyle: {color: TAGG_LIGHT_BLUE}, + }, + ]} + /> diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx index 6239971e..7cd99278 100644 --- a/src/components/common/TaggTypeahead.tsx +++ b/src/components/common/TaggTypeahead.tsx @@ -1,33 +1,30 @@ import React, {Fragment, useEffect, useState} from 'react'; import {ScrollView, StyleSheet} from 'react-native'; +import {MentionSuggestionsProps} from 'react-native-controlled-mentions'; import {SEARCH_ENDPOINT_MESSAGES} from '../../constants'; import {loadSearchResults} from '../../services'; import {ProfilePreviewType} from '../../types'; import {SCREEN_WIDTH} from '../../utils'; import TaggUserRowCell from './TaggUserRowCell'; -type TaggTypeaheadProps = { - query: string; - setSelectedMention: (user: ProfilePreviewType) => void; -}; - -const TaggTypeahead: React.FC = ({ - query, - setSelectedMention, +const TaggTypeahead: React.FC = ({ + keyword, + onSuggestionPress, }) => { const [results, setResults] = useState([]); + const [height, setHeight] = useState(0); useEffect(() => { getQuerySuggested(); - }, [query]); + }, [keyword]); const getQuerySuggested = async () => { - if (query.length < 3) { + if (!keyword || keyword.length < 3) { setResults([]); return; } const searchResults = await loadSearchResults( - `${SEARCH_ENDPOINT_MESSAGES}?query=${query}`, + `${SEARCH_ENDPOINT_MESSAGES}?query=${keyword}`, ); if (searchResults && searchResults.users) { setResults(searchResults.users); @@ -39,11 +36,19 @@ const TaggTypeahead: React.FC = ({ } return ( - + { + setHeight(event.nativeEvent.layout.height); + }}> {results.map((user) => ( { - setSelectedMention(user); + onSuggestionPress({ + id: user.id, + name: user.username, + }); setResults([]); }} user={user} @@ -57,8 +62,12 @@ const styles = StyleSheet.create({ container: { marginLeft: SCREEN_WIDTH * 0.05, width: SCREEN_WIDTH * 0.9, - maxHeight: 300, + maxHeight: 264, borderRadius: 10, + backgroundColor: 'white', + position: 'absolute', + alignSelf: 'center', + zIndex: 1, borderWidth: 1, }, }); -- cgit v1.2.3-70-g09d2 From 9322aede82815ba5a6bddf5b289692955d6e1d96 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 30 Apr 2021 18:05:28 -0400 Subject: added display text parsing --- src/components/comments/AddComment.tsx | 13 ++----- src/components/comments/CommentTile.tsx | 25 ++++++------ src/utils/comments.tsx | 67 +++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 src/utils/comments.tsx (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index 7576675e..cf5d19ee 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -16,7 +16,8 @@ import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import {Avatar, TaggTypeahead} from '../common'; +import {Avatar} from '../common'; +import {mentionPartTypes} from '../../utils/comments'; /** * This file provides the add comment view for a user. @@ -108,15 +109,7 @@ const AddComment: React.FC = ({ value={comment} onChange={setComment} inputRef={ref} - partTypes={[ - { - trigger: '@', - renderSuggestions: (props) => , - allowedSpacesCount: 0, - isInsertSpaceAfterMention: true, - textStyle: {color: TAGG_LIGHT_BLUE}, - }, - ]} + partTypes={mentionPartTypes} /> diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx index 34eef418..334934f1 100644 --- a/src/components/comments/CommentTile.tsx +++ b/src/components/comments/CommentTile.tsx @@ -1,21 +1,22 @@ /* eslint-disable radix */ import React, {Fragment, useEffect, useRef, useState} from 'react'; -import {Text, View} from 'react-native-animatable'; -import {ProfilePreview} from '../profile'; -import {CommentType, ScreenType, TypeOfComment} from '../../types'; import {Alert, Animated, StyleSheet} from 'react-native'; -import ClockIcon from '../../assets/icons/clock-icon-01.svg'; -import {TAGG_LIGHT_BLUE} from '../../constants'; +import {Text, View} from 'react-native-animatable'; import {RectButton, TouchableOpacity} from 'react-native-gesture-handler'; -import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; +import Swipeable from 'react-native-gesture-handler/Swipeable'; +import {useSelector} from 'react-redux'; import Arrow from '../../assets/icons/back-arrow-colored.svg'; +import ClockIcon from '../../assets/icons/clock-icon-01.svg'; import Trash from '../../assets/ionicons/trash-outline.svg'; -import CommentsContainer from './CommentsContainer'; -import Swipeable from 'react-native-gesture-handler/Swipeable'; -import {deleteComment, getCommentsCount} from '../../services'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {ERROR_FAILED_TO_DELETE_COMMENT} from '../../constants/strings'; -import {useSelector} from 'react-redux'; +import {deleteComment, getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; +import {CommentType, ScreenType, TypeOfComment} from '../../types'; +import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; +import {mentionPartTypes, renderValue} from '../../utils/comments'; +import {ProfilePreview} from '../profile'; +import CommentsContainer from './CommentsContainer'; /** * Displays users's profile picture, comment posted by them and the time difference between now and when a comment was posted. @@ -154,7 +155,9 @@ const CommentTile: React.FC = ({ screenType={screenType} /> - {comment_object.comment} + + {renderValue(comment_object.comment, mentionPartTypes)} + {' ' + timePosted} diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx new file mode 100644 index 00000000..a1f353d6 --- /dev/null +++ b/src/utils/comments.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import {Text} from 'react-native'; +import { + isMentionPartType, + parseValue, + Part, + PartType, +} from 'react-native-controlled-mentions'; +import TaggTypeahead from '../components/common/TaggTypeahead'; +import {TAGG_LIGHT_BLUE} from '../constants'; + +/** + * Part renderer + * + * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value + * + * @param part + * @param index + */ +const renderPart = (part: Part, index: number) => { + // Just plain text + if (!part.partType) { + return {part.text}; + } + + // Mention type part + if (isMentionPartType(part.partType)) { + return ( + console.log('Pressed', part.data)}> + {part.text} + + ); + } + + // Other styled part types + return ( + + {part.text} + + ); +}; + +/** + * Value renderer. Parsing value to parts array and then mapping the array using 'renderPart' + * + * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value + * + * @param value - value from MentionInput + * @param partTypes - the part types array that you providing to MentionInput + */ +export const renderValue: React.FC = (value: string, partTypes: PartType[]) => { + const {parts} = parseValue(value, partTypes); + return {parts.map(renderPart)}; +}; + +export const mentionPartTypes: PartType[] = [ + { + trigger: '@', + renderSuggestions: (props) => , + allowedSpacesCount: 0, + isInsertSpaceAfterMention: true, + textStyle: {color: TAGG_LIGHT_BLUE}, + }, +]; -- cgit v1.2.3-70-g09d2 From e67c5d50ebd33967c069134aa0e03111674e1c5d Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 30 Apr 2021 18:26:19 -0400 Subject: added mentioning for reply --- src/components/comments/AddComment.tsx | 35 ++++++++++++++++++---------- src/screens/profile/MomentCommentsScreen.tsx | 7 +++--- 2 files changed, 27 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index cf5d19ee..caba68d5 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -15,6 +15,7 @@ import {TAGG_LIGHT_BLUE} from '../../constants'; import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; +import {CommentType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {Avatar} from '../common'; import {mentionPartTypes} from '../../utils/comments'; @@ -27,33 +28,36 @@ import {mentionPartTypes} from '../../utils/comments'; export interface AddCommentProps { setNewCommentsAvailable: Function; - objectId: string; + // we either have a comment object if we're replying to a comment, + // or a momentId if we're just commenting a moment. + commentInReplyTo: CommentType | undefined; + momentId: string | undefined; + isReplying: boolean; placeholderText: string; - isCommentInFocus: boolean; } const AddComment: React.FC = ({ setNewCommentsAvailable, - objectId, + commentInReplyTo, + momentId, placeholderText, - isCommentInFocus, + isReplying, }) => { const [comment, setComment] = useState(''); const [keyboardVisible, setKeyboardVisible] = useState(false); const {avatar} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const ref = useRef(null); + const objectId: string = isReplying + ? commentInReplyTo!.comment_id + : momentId!; const addComment = async () => { const trimmed = comment.trim(); if (trimmed === '') { return; } - const postedComment = await postComment( - trimmed, - objectId, - isCommentInFocus, - ); + const postedComment = await postComment(trimmed, objectId, isReplying); if (postedComment) { setComment(''); @@ -61,7 +65,7 @@ const AddComment: React.FC = ({ //Set new reply posted object //This helps us show the latest reply on top //Data set is kind of stale but it works - if (isCommentInFocus) { + if (isReplying) { dispatch( updateReplyPosted({ comment_id: postedComment.comment_id, @@ -73,6 +77,13 @@ const AddComment: React.FC = ({ } }; + useEffect(() => { + if (isReplying && commentInReplyTo) { + const commenter = commentInReplyTo.commenter; + setComment(`@[${commenter.username}](${commenter.id}) `); + } + }, [isReplying, commentInReplyTo]); + useEffect(() => { const showKeyboard = () => setKeyboardVisible(true); Keyboard.addListener('keyboardWillShow', showKeyboard); @@ -87,10 +98,10 @@ const AddComment: React.FC = ({ //If a comment is in Focus, bring the keyboard up so user is able to type in a reply useEffect(() => { - if (isCommentInFocus) { + if (isReplying) { ref.current?.focus(); } - }, [isCommentInFocus]); + }, [isReplying]); return ( = ({route}) => { : ADD_COMMENT_TEXT() } setNewCommentsAvailable={setNewCommentsAvailable} - objectId={ - commentObjectInFocus ? commentObjectInFocus.comment_id : moment_id + commentInReplyTo={ + commentObjectInFocus ? commentObjectInFocus : undefined } - isCommentInFocus={commentObjectInFocus ? true : false} + momentId={commentObjectInFocus ? undefined : moment_id} + isReplying={commentObjectInFocus ? true : false} /> -- cgit v1.2.3-70-g09d2 From c5cfb608eeb512c3b00c20776b63faf70943b76e Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 15:02:12 -0400 Subject: fixed issue where mention doesn't go away --- src/components/comments/AddComment.tsx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index caba68d5..f9c5669b 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -43,6 +43,7 @@ const AddComment: React.FC = ({ placeholderText, isReplying, }) => { + const [inReplyToMention, setInReplyToMention] = useState(''); const [comment, setComment] = useState(''); const [keyboardVisible, setKeyboardVisible] = useState(false); const {avatar} = useSelector((state: RootState) => state.user); @@ -77,13 +78,6 @@ const AddComment: React.FC = ({ } }; - useEffect(() => { - if (isReplying && commentInReplyTo) { - const commenter = commentInReplyTo.commenter; - setComment(`@[${commenter.username}](${commenter.id}) `); - } - }, [isReplying, commentInReplyTo]); - useEffect(() => { const showKeyboard = () => setKeyboardVisible(true); Keyboard.addListener('keyboardWillShow', showKeyboard); @@ -96,12 +90,16 @@ const AddComment: React.FC = ({ return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard); }, []); - //If a comment is in Focus, bring the keyboard up so user is able to type in a reply useEffect(() => { - if (isReplying) { + if (isReplying && commentInReplyTo) { + // bring up keyboard ref.current?.focus(); + const commenter = commentInReplyTo.commenter; + setInReplyToMention(`@[${commenter.username}](${commenter.id}) `); + } else { + setInReplyToMention(''); } - }, [isReplying]); + }, [isReplying, commentInReplyTo]); return ( = ({ Date: Tue, 4 May 2021 17:12:22 -0400 Subject: fixed merge issues --- src/components/common/TaggUserRowCell.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/components/common/TaggUserRowCell.tsx b/src/components/common/TaggUserRowCell.tsx index 446f5e87..446dedc9 100644 --- a/src/components/common/TaggUserRowCell.tsx +++ b/src/components/common/TaggUserRowCell.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; +import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'; import {ProfilePreviewType} from '../../types'; -import {defaultUserProfile, normalize} from '../../utils'; +import {normalize} from '../../utils'; +import Avatar from './Avatar'; type TaggUserRowCellProps = { onPress: () => void; @@ -10,11 +11,7 @@ type TaggUserRowCellProps = { const TaggUserRowCell: React.FC = ({onPress, user}) => { return ( - + {`@${user.username}`} -- cgit v1.2.3-70-g09d2 From de9f7b13babb5f9656da797c863064578a3a49c3 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 17:18:54 -0400 Subject: fixed multiple mentions getting populated --- src/components/comments/AddComment.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index f9c5669b..cce846e5 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -116,7 +116,12 @@ const AddComment: React.FC = ({ containerStyle={styles.text} placeholder={placeholderText} value={inReplyToMention + comment} - onChange={setComment} + onChange={(newText: string) => { + // skipping the `inReplyToMention` text + setComment( + newText.substring(inReplyToMention.length, newText.length), + ); + }} inputRef={ref} partTypes={mentionPartTypes} /> -- cgit v1.2.3-70-g09d2 From eb756ec715a96b96bde8f13a910335d508f06948 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 19:55:59 -0400 Subject: removed unused type --- src/types/types.ts | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/types/types.ts b/src/types/types.ts index ce39947c..690d6fb9 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -220,8 +220,6 @@ export type NotificationType = { unread: boolean; }; -export type TypeOfComment = 'Comment' | 'Thread'; - export type TypeOfNotification = // notification_object is undefined | 'DFT' -- cgit v1.2.3-70-g09d2 From 2832decea88ed14325f92759617ae8ee8a588d22 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 19:56:21 -0400 Subject: fixed bug, cleaned up code, mention working --- src/components/comments/AddComment.tsx | 62 +++++++------- src/components/comments/CommentTile.tsx | 103 ++++++++++------------- src/components/comments/CommentsContainer.tsx | 116 ++++++++++---------------- src/screens/profile/MomentCommentsScreen.tsx | 91 ++++++++++++-------- 4 files changed, 174 insertions(+), 198 deletions(-) (limited to 'src') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index cce846e5..dd016109 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React, {useContext, useEffect, useRef, useState} from 'react'; import { Keyboard, KeyboardAvoidingView, @@ -12,61 +12,59 @@ import {TouchableOpacity} from 'react-native-gesture-handler'; import {useDispatch, useSelector} from 'react-redux'; import UpArrowIcon from '../../assets/icons/up_arrow.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; +import {CommentContext} from '../../screens/profile/MomentCommentsScreen'; import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; -import {CommentType} from '../../types'; +import {CommentThreadType, CommentType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import {Avatar} from '../common'; import {mentionPartTypes} from '../../utils/comments'; - -/** - * This file provides the add comment view for a user. - * Displays the logged in user's profile picture to the left and then provides space to add a comment. - * Comment is posted when enter is pressed as requested by product team. - */ +import {Avatar} from '../common'; export interface AddCommentProps { - setNewCommentsAvailable: Function; - // we either have a comment object if we're replying to a comment, - // or a momentId if we're just commenting a moment. - commentInReplyTo: CommentType | undefined; - momentId: string | undefined; - isReplying: boolean; + momentId: string; placeholderText: string; } -const AddComment: React.FC = ({ - setNewCommentsAvailable, - commentInReplyTo, - momentId, - placeholderText, - isReplying, -}) => { +const AddComment: React.FC = ({momentId, placeholderText}) => { + const {setShouldUpdateAllComments, commentTapped} = useContext( + CommentContext, + ); const [inReplyToMention, setInReplyToMention] = useState(''); const [comment, setComment] = useState(''); const [keyboardVisible, setKeyboardVisible] = useState(false); const {avatar} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const ref = useRef(null); - const objectId: string = isReplying - ? commentInReplyTo!.comment_id - : momentId!; + const isReplyingToComment = + commentTapped !== undefined && !('parent_comment' in commentTapped); + const isReplyingToReply = + commentTapped !== undefined && 'parent_comment' in commentTapped; + const objectId: string = commentTapped + ? 'parent_comment' in commentTapped + ? (commentTapped as CommentThreadType).parent_comment.comment_id + : (commentTapped as CommentType).comment_id + : momentId; const addComment = async () => { const trimmed = comment.trim(); if (trimmed === '') { return; } - const postedComment = await postComment(trimmed, objectId, isReplying); + const postedComment = await postComment( + inReplyToMention + trimmed, + objectId, + isReplyingToComment || isReplyingToReply, + ); if (postedComment) { setComment(''); + setInReplyToMention(''); //Set new reply posted object //This helps us show the latest reply on top //Data set is kind of stale but it works - if (isReplying) { + if (isReplyingToComment || isReplyingToReply) { dispatch( updateReplyPosted({ comment_id: postedComment.comment_id, @@ -74,7 +72,7 @@ const AddComment: React.FC = ({ }), ); } - setNewCommentsAvailable(true); + setShouldUpdateAllComments(true); } }; @@ -91,15 +89,17 @@ const AddComment: React.FC = ({ }, []); useEffect(() => { - if (isReplying && commentInReplyTo) { + if (isReplyingToComment || isReplyingToReply) { // bring up keyboard ref.current?.focus(); - const commenter = commentInReplyTo.commenter; + } + if (commentTapped && isReplyingToReply) { + const commenter = (commentTapped as CommentThreadType).commenter; setInReplyToMention(`@[${commenter.username}](${commenter.id}) `); } else { setInReplyToMention(''); } - }, [isReplying, commentInReplyTo]); + }, [isReplyingToComment, isReplyingToReply, commentTapped]); return ( void; - newCommentsAvailable: boolean; - setNewCommentsAvailable: (available: boolean) => void; + isThread: boolean; + shouldUpdateParent: boolean; + setShouldUpdateParent: (update: boolean) => void; canDelete: boolean; } const CommentTile: React.FC = ({ - comment_object, + commentObject, screenType, - typeOfComment, - setCommentObjectInFocus, - newCommentsAvailable, - setNewCommentsAvailable, + setShouldUpdateParent, + shouldUpdateParent, canDelete, + isThread, }) => { - const timePosted = getTimePosted(comment_object.date_created); + const {setCommentTapped} = useContext(CommentContext); + const timePosted = getTimePosted(commentObject.date_created); const [showReplies, setShowReplies] = useState(false); const [showKeyboard, setShowKeyboard] = useState(false); - const [newThreadAvailable, setNewThreadAvailable] = useState(true); + const [shouldUpdateChild, setShouldUpdateChild] = useState(true); const swipeRef = useRef(null); - const isThread = typeOfComment === 'Thread'; - const {replyPosted} = useSelector((state: RootState) => state.user); - /** - * Bubbling up, for handling a new comment in a thread. - */ useEffect(() => { - if (newCommentsAvailable) { - setNewThreadAvailable(true); + if (shouldUpdateParent) { + setShouldUpdateChild(true); } - }, [newCommentsAvailable]); + }, [shouldUpdateParent]); useEffect(() => { - if (replyPosted && typeOfComment === 'Comment') { - if (replyPosted.parent_comment.comment_id === comment_object.comment_id) { + if (replyPosted && !isThread) { + if (replyPosted.parent_comment.comment_id === commentObject.comment_id) { setShowReplies(true); } } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [replyPosted]); - /** - * Case : A COMMENT IS IN FOCUS && REPLY SECTION IS HIDDEN - * Bring the current comment to focus - * Case : No COMMENT IS IN FOCUS && REPLY SECTION IS SHOWN - * Unfocus comment in focus - */ const toggleAddComment = () => { - //Do not allow user to reply to a thread - if (!isThread) { - if (setCommentObjectInFocus) { - if (!showKeyboard) { - setCommentObjectInFocus(comment_object); - } else { - setCommentObjectInFocus(undefined); - } - } - setShowKeyboard(!showKeyboard); - } + setCommentTapped(commentObject); + setShowKeyboard(!showKeyboard); }; const toggleReplies = async () => { - if (showReplies) { + if (showReplies && isThread) { + const comment = (commentObject as CommentThreadType).parent_comment; //To update count of replies in case we deleted a reply - comment_object.replies_count = parseInt( - await getCommentsCount(comment_object.comment_id, true), + comment.replies_count = parseInt( + await getCommentsCount(comment.comment_id, true), + 10, ); } - setNewThreadAvailable(true); + setShouldUpdateChild(true); setShowReplies(!showReplies); }; /** * Method to compute text to be shown for replies button */ - const getRepliesText = () => + const getRepliesText = (comment: CommentType) => showReplies ? 'Hide' - : comment_object.replies_count > 0 - ? `Replies (${comment_object.replies_count})` + : comment.replies_count > 0 + ? `Replies (${comment.replies_count})` : 'Replies'; const renderRightAction = (text: string, color: string) => { const pressHandler = async () => { swipeRef.current?.close(); - const success = await deleteComment(comment_object.comment_id, isThread); + const success = await deleteComment(commentObject.comment_id, isThread); if (success) { - setNewCommentsAvailable(true); + setShouldUpdateParent(true); } else { Alert.alert(ERROR_FAILED_TO_DELETE_COMMENT); } @@ -150,13 +130,13 @@ const CommentTile: React.FC = ({ - {renderValue(comment_object.comment, mentionPartTypes)} + {renderValue(commentObject.comment, mentionPartTypes)} @@ -165,11 +145,13 @@ const CommentTile: React.FC = ({ {/*** Show replies text only if there are some replies present */} - {typeOfComment === 'Comment' && comment_object.replies_count > 0 && ( + {!isThread && (commentObject as CommentType).replies_count > 0 && ( - {getRepliesText()} + + {getRepliesText(commentObject as CommentType)} + = ({ {showReplies && ( )} diff --git a/src/components/comments/CommentsContainer.tsx b/src/components/comments/CommentsContainer.tsx index 3dc8a71c..d5d02a92 100644 --- a/src/components/comments/CommentsContainer.tsx +++ b/src/components/comments/CommentsContainer.tsx @@ -1,24 +1,23 @@ -import React, {useEffect, useRef, useState} from 'react'; +import moment from 'moment'; +import React, {useContext, useEffect, useRef, useState} from 'react'; import {StyleSheet} from 'react-native'; import {FlatList} from 'react-native-gesture-handler'; import {useDispatch, useSelector} from 'react-redux'; -import CommentTile from './CommentTile'; +import {CommentContext} from '../../screens/profile/MomentCommentsScreen'; import {getComments} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; -import {CommentType, ScreenType, TypeOfComment} from '../../types'; +import {CommentThreadType, CommentType, ScreenType} from '../../types'; import {SCREEN_HEIGHT} from '../../utils'; +import CommentTile from './CommentTile'; + export type CommentsContainerProps = { screenType: ScreenType; - //objectId can be either moment_id or comment_id objectId: string; commentId?: string; - setCommentsLength?: (count: number) => void; - newCommentsAvailable: boolean; - setNewCommentsAvailable: (value: boolean) => void; - typeOfComment: TypeOfComment; - setCommentObjectInFocus?: (comment: CommentType | undefined) => void; - commentObjectInFocus?: CommentType; + shouldUpdate: boolean; + setShouldUpdate: (update: boolean) => void; + isThread: boolean; }; /** @@ -28,14 +27,12 @@ export type CommentsContainerProps = { const CommentsContainer: React.FC = ({ screenType, objectId, - setCommentsLength, - newCommentsAvailable, - setNewCommentsAvailable, - typeOfComment, - setCommentObjectInFocus, - commentObjectInFocus, + isThread, + shouldUpdate, + setShouldUpdate, commentId, }) => { + const {setCommentsLength, commentTapped} = useContext(CommentContext); const {username: loggedInUsername} = useSelector( (state: RootState) => state.user.user, ); @@ -45,57 +42,30 @@ const CommentsContainer: React.FC = ({ useEffect(() => { const loadComments = async () => { - await getComments(objectId, typeOfComment === 'Thread').then( - (comments) => { - if (comments && subscribedToLoadComments) { - setCommentsList(comments); - if (setCommentsLength) { - setCommentsLength(comments.length); - } - setNewCommentsAvailable(false); + await getComments(objectId, isThread).then((comments) => { + if (comments && subscribedToLoadComments) { + setCommentsList(comments); + if (setCommentsLength) { + setCommentsLength(comments.length); } - }, - ); + setShouldUpdate(false); + } + }); }; let subscribedToLoadComments = true; - if (newCommentsAvailable) { + if (shouldUpdate) { loadComments(); } return () => { subscribedToLoadComments = false; }; - }, [ - dispatch, - objectId, - newCommentsAvailable, - setNewCommentsAvailable, - setCommentsLength, - typeOfComment, - ]); - - // eslint-disable-next-line no-shadow - const swapCommentTo = (commentId: string, toIndex: number) => { - const index = commentsList.findIndex( - (item) => item.comment_id === commentId, - ); - if (index > 0) { - let comments = [...commentsList]; - const temp = comments[index]; - comments[index] = comments[toIndex]; - comments[toIndex] = temp; - setCommentsList(comments); - } - }; + }, [shouldUpdate]); useEffect(() => { - //Scroll only if a new comment and not a reply was posted - const shouldScroll = () => - typeOfComment === 'Comment' && !commentObjectInFocus; - const performAction = () => { if (commentId) { swapCommentTo(commentId, 0); - } else if (shouldScroll()) { + } else if (!isThread && !commentTapped) { setTimeout(() => { ref.current?.scrollToEnd({animated: true}); }, 500); @@ -108,41 +78,47 @@ const CommentsContainer: React.FC = ({ //Clean up the reply id present in store return () => { - if (commentId && typeOfComment === 'Thread') { + if (commentId && isThread) { setTimeout(() => { dispatch(updateReplyPosted(undefined)); }, 200); } }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [commentsList, commentId]); - //WIP : TODO : Bring the comment in focus above the keyboard - // useEffect(() => { - // if (commentObjectInFocus && commentsList.length >= 3) { - // swapCommentTo(commentObjectInFocus.comment_id, 2); - // } - // // eslint-disable-next-line react-hooks/exhaustive-deps - // }, [commentObjectInFocus]); + // eslint-disable-next-line no-shadow + const swapCommentTo = (commentId: string, toIndex: number) => { + const index = commentsList.findIndex( + (item) => item.comment_id === commentId, + ); + if (index > 0) { + let comments = [...commentsList]; + const temp = comments[index]; + comments[index] = comments[toIndex]; + comments[toIndex] = temp; + setCommentsList(comments); + } + }; const ITEM_HEIGHT = SCREEN_HEIGHT / 7.0; - const renderComment = ({item}: {item: CommentType}) => ( + const renderComment = ({item}: {item: CommentType | CommentThreadType}) => ( ); return ( moment(a.date_created).unix() - moment(b.date_created).unix(), + )} ref={ref} keyExtractor={(item, index) => index.toString()} decelerationRate={'fast'} diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx index 73266a39..1a913e58 100644 --- a/src/screens/profile/MomentCommentsScreen.tsx +++ b/src/screens/profile/MomentCommentsScreen.tsx @@ -7,7 +7,7 @@ import {AddComment} from '../../components/'; import CommentsContainer from '../../components/comments/CommentsContainer'; import {ADD_COMMENT_TEXT} from '../../constants/strings'; import {headerBarOptions, MainStackParams} from '../../routes/main'; -import {CommentType} from '../../types'; +import {CommentThreadType, CommentType} from '../../types'; import {HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; /** @@ -25,18 +25,35 @@ interface MomentCommentsScreenProps { route: MomentCommentsScreenRouteProps; } +type MomentCommentContextType = { + commentTapped: CommentType | CommentThreadType | undefined; + setCommentTapped: ( + comment: CommentType | CommentThreadType | undefined, + ) => void; + shouldUpdateAllComments: boolean; + setShouldUpdateAllComments: (available: boolean) => void; + commentsLength: number; + setCommentsLength: (length: number) => void; +}; + +export const CommentContext = React.createContext( + {} as MomentCommentContextType, +); + const MomentCommentsScreen: React.FC = ({route}) => { const navigation = useNavigation(); const {moment_id, screenType, comment_id} = route.params; //Receives comment length from child CommentsContainer const [commentsLength, setCommentsLength] = useState(0); - const [newCommentsAvailable, setNewCommentsAvailable] = React.useState(true); + const [shouldUpdateAllComments, setShouldUpdateAllComments] = React.useState( + true, + ); //Keeps track of the current comments object in focus so that the application knows which comment to post a reply to - const [commentObjectInFocus, setCommentObjectInFocus] = useState< - CommentType | undefined - >(undefined); + const [commentTapped, setCommentTapped] = useState< + CommentType | CommentThreadType | undefined + >(); useEffect(() => { navigation.setOptions({ @@ -45,37 +62,39 @@ const MomentCommentsScreen: React.FC = ({route}) => { }, [commentsLength, navigation]); return ( - - - - - - - - - + + + + + + + + + + + ); }; -- cgit v1.2.3-70-g09d2 From c23feea922da063d88d031f25b72b53ba593ec04 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 20:07:03 -0400 Subject: fixed type warning --- src/components/comments/CommentTile.tsx | 7 +++++-- src/utils/comments.tsx | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx index 37a249a8..12f32c95 100644 --- a/src/components/comments/CommentTile.tsx +++ b/src/components/comments/CommentTile.tsx @@ -14,7 +14,7 @@ import {deleteComment, getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; import {CommentThreadType, CommentType, ScreenType} from '../../types'; import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; -import {mentionPartTypes, renderValue} from '../../utils/comments'; +import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; import {ProfilePreview} from '../profile'; import CommentsContainer from './CommentsContainer'; @@ -136,7 +136,10 @@ const CommentTile: React.FC = ({ /> - {renderValue(commentObject.comment, mentionPartTypes)} + {renderTextWithMentions({ + value: commentObject.comment, + partTypes: mentionPartTypes, + })} diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index a1f353d6..c0b522f2 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -43,6 +43,11 @@ const renderPart = (part: Part, index: number) => { ); }; +interface RenderProps { + value: string; + partTypes: PartType[]; +} + /** * Value renderer. Parsing value to parts array and then mapping the array using 'renderPart' * @@ -51,7 +56,10 @@ const renderPart = (part: Part, index: number) => { * @param value - value from MentionInput * @param partTypes - the part types array that you providing to MentionInput */ -export const renderValue: React.FC = (value: string, partTypes: PartType[]) => { +export const renderTextWithMentions: React.FC = ({ + value, + partTypes, +}) => { const {parts} = parseValue(value, partTypes); return {parts.map(renderPart)}; }; -- cgit v1.2.3-70-g09d2 From 7f9799c6693254f8eb6729c0977826694c28c437 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 20:46:27 -0400 Subject: added navigation to profile --- src/components/comments/CommentTile.tsx | 34 ++++++++++++++------ src/components/moments/MomentPostContent.tsx | 23 ++++++++++++-- src/screens/profile/CaptionScreen.tsx | 17 +++++----- src/utils/comments.tsx | 46 ++++++++++++++++++---------- src/utils/users.ts | 24 +++++++++++++++ 5 files changed, 105 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx index 12f32c95..ce346af5 100644 --- a/src/components/comments/CommentTile.tsx +++ b/src/components/comments/CommentTile.tsx @@ -1,9 +1,10 @@ +import {useNavigation} from '@react-navigation/native'; import React, {Fragment, useContext, useEffect, useRef, useState} from 'react'; import {Alert, Animated, StyleSheet} from 'react-native'; import {Text, View} from 'react-native-animatable'; import {RectButton, TouchableOpacity} from 'react-native-gesture-handler'; import Swipeable from 'react-native-gesture-handler/Swipeable'; -import {useSelector} from 'react-redux'; +import {useDispatch, useSelector, useStore} from 'react-redux'; import Arrow from '../../assets/icons/back-arrow-colored.svg'; import ClockIcon from '../../assets/icons/clock-icon-01.svg'; import Trash from '../../assets/ionicons/trash-outline.svg'; @@ -12,9 +13,19 @@ import {ERROR_FAILED_TO_DELETE_COMMENT} from '../../constants/strings'; import {CommentContext} from '../../screens/profile/MomentCommentsScreen'; import {deleteComment, getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; -import {CommentThreadType, CommentType, ScreenType} from '../../types'; -import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; -import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; +import { + CommentThreadType, + CommentType, + ScreenType, + UserType, +} from '../../types'; +import { + getTimePosted, + navigateToProfile, + normalize, + SCREEN_WIDTH, +} from '../../utils'; +import {renderTextWithMentions} from '../../utils/comments'; import {ProfilePreview} from '../profile'; import CommentsContainer from './CommentsContainer'; @@ -46,6 +57,9 @@ const CommentTile: React.FC = ({ const [shouldUpdateChild, setShouldUpdateChild] = useState(true); const swipeRef = useRef(null); const {replyPosted} = useSelector((state: RootState) => state.user); + const state: RootState = useStore().getState(); + const navigation = useNavigation(); + const dispatch = useDispatch(); useEffect(() => { if (shouldUpdateParent) { @@ -135,12 +149,12 @@ const CommentTile: React.FC = ({ screenType={screenType} /> - - {renderTextWithMentions({ - value: commentObject.comment, - partTypes: mentionPartTypes, - })} - + {renderTextWithMentions({ + value: commentObject.comment, + styles: styles.comment, + onPress: (user: UserType) => + navigateToProfile(state, dispatch, navigation, screenType, user), + })} {' ' + timePosted} diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx index d68ceaa3..03034925 100644 --- a/src/components/moments/MomentPostContent.tsx +++ b/src/components/moments/MomentPostContent.tsx @@ -1,8 +1,17 @@ +import {useNavigation} from '@react-navigation/native'; import React, {useEffect} from 'react'; import {Image, StyleSheet, Text, View, ViewProps} from 'react-native'; +import {useDispatch, useStore} from 'react-redux'; import {getCommentsCount} from '../../services'; -import {ScreenType} from '../../types'; -import {getTimePosted, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import {RootState} from '../../store/rootReducer'; +import {ScreenType, UserType} from '../../types'; +import { + getTimePosted, + navigateToProfile, + SCREEN_HEIGHT, + SCREEN_WIDTH, +} from '../../utils'; +import {renderTextWithMentions} from '../../utils/comments'; import {CommentsCount} from '../comments'; interface MomentPostContentProps extends ViewProps { @@ -22,6 +31,9 @@ const MomentPostContent: React.FC = ({ }) => { const [elapsedTime, setElapsedTime] = React.useState(); const [comments_count, setCommentsCount] = React.useState(''); + const state: RootState = useStore().getState(); + const navigation = useNavigation(); + const dispatch = useDispatch(); useEffect(() => { const fetchCommentsCount = async () => { @@ -47,7 +59,12 @@ const MomentPostContent: React.FC = ({ /> {elapsedTime} - {caption} + {renderTextWithMentions({ + value: caption, + styles: styles.captionText, + onPress: (user: UserType) => + navigateToProfile(state, dispatch, navigation, screenType, user), + })} ); }; diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 156ee41c..041f0da2 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -11,9 +11,10 @@ import { TouchableWithoutFeedback, View, } from 'react-native'; +import {MentionInput} from 'react-native-controlled-mentions'; import {Button} from 'react-native-elements'; import {useDispatch, useSelector} from 'react-redux'; -import {SearchBackground, TaggBigInput} from '../../components'; +import {SearchBackground} from '../../components'; import {CaptionScreenHeader} from '../../components/'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import {TAGG_LIGHT_BLUE_2} from '../../constants'; @@ -26,6 +27,7 @@ import { } from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {SCREEN_WIDTH, StatusBarHeight} from '../../utils'; +import {mentionPartTypes} from '../../utils/comments'; /** * Upload Screen to allow users to upload posts to Tagg @@ -49,10 +51,6 @@ const CaptionScreen: React.FC = ({route, navigation}) => { const [caption, setCaption] = useState(''); const [loading, setLoading] = useState(false); - const handleCaptionUpdate = (newCaption: string) => { - setCaption(newCaption); - }; - const navigateToProfile = () => { //Since the logged In User is navigating to own profile, useXId is not required navigation.navigate('Profile', { @@ -112,12 +110,13 @@ const CaptionScreen: React.FC = ({route, navigation}) => { source={{uri: image.path}} resizeMode={'cover'} /> - diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index c0b522f2..47d26bb5 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -1,23 +1,26 @@ import React from 'react'; -import {Text} from 'react-native'; +import {StyleProp, Text, TextStyle} from 'react-native'; import { isMentionPartType, parseValue, Part, PartType, } from 'react-native-controlled-mentions'; +import {TouchableOpacity} from 'react-native-gesture-handler'; import TaggTypeahead from '../components/common/TaggTypeahead'; import {TAGG_LIGHT_BLUE} from '../constants'; +import {UserType} from '../types'; /** * Part renderer * * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value - * - * @param part - * @param index */ -const renderPart = (part: Part, index: number) => { +const renderPart = ( + part: Part, + index: number, + handlePress: (user: UserType) => void, +) => { // Just plain text if (!part.partType) { return {part.text}; @@ -26,12 +29,18 @@ const renderPart = (part: Part, index: number) => { // Mention type part if (isMentionPartType(part.partType)) { return ( - console.log('Pressed', part.data)}> - {part.text} - + onPress={() => { + if (part.data) { + handlePress({ + userId: part.data.id, + username: part.data.name, + }); + } + }}> + {part.text} + ); } @@ -45,23 +54,26 @@ const renderPart = (part: Part, index: number) => { interface RenderProps { value: string; - partTypes: PartType[]; + styles: StyleProp; + onPress: (user: UserType) => void; } /** * Value renderer. Parsing value to parts array and then mapping the array using 'renderPart' * * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value - * - * @param value - value from MentionInput - * @param partTypes - the part types array that you providing to MentionInput */ export const renderTextWithMentions: React.FC = ({ value, - partTypes, + styles, + onPress, }) => { - const {parts} = parseValue(value, partTypes); - return {parts.map(renderPart)}; + const {parts} = parseValue(value, mentionPartTypes); + return ( + + {parts.map((part, index) => renderPart(part, index, onPress))} + + ); }; export const mentionPartTypes: PartType[] = [ diff --git a/src/utils/users.ts b/src/utils/users.ts index abadaf6e..c167ac2f 100644 --- a/src/utils/users.ts +++ b/src/utils/users.ts @@ -1,4 +1,6 @@ import AsyncStorage from '@react-native-community/async-storage'; +import {NavigationProp} from '@react-navigation/core'; +import {Dispatch} from 'react'; import {INTEGRATED_SOCIAL_LIST} from '../constants'; import {isUserBlocked, loadSocialPosts} from '../services'; import { @@ -199,3 +201,25 @@ export const canViewProfile = ( } return false; }; + +export const navigateToProfile = async ( + state: RootState, + dispatch: any, + navigation: any, + screenType: ScreenType, + user: UserType, +) => { + const loggedInUserId = state.user.user.userId; + const {userId, username} = user; + if (!userXInStore(state, screenType, userId)) { + await fetchUserX( + dispatch, + {userId: userId, username: username}, + screenType, + ); + } + navigation.push('Profile', { + userXId: userId === loggedInUserId ? undefined : userId, + screenType, + }); +}; -- cgit v1.2.3-70-g09d2 From 25206461714659a3ca26fdbc799c6206e2f792be Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 20:46:42 -0400 Subject: fixed linter issue --- src/utils/users.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/utils/users.ts b/src/utils/users.ts index c167ac2f..754382b3 100644 --- a/src/utils/users.ts +++ b/src/utils/users.ts @@ -1,6 +1,4 @@ import AsyncStorage from '@react-native-community/async-storage'; -import {NavigationProp} from '@react-navigation/core'; -import {Dispatch} from 'react'; import {INTEGRATED_SOCIAL_LIST} from '../constants'; import {isUserBlocked, loadSocialPosts} from '../services'; import { @@ -19,8 +17,8 @@ import {loadUserX} from './../store/actions/userX'; import {AppDispatch} from './../store/configureStore'; import {RootState} from './../store/rootReducer'; import { - ProfilePreviewType, ProfileInfoType, + ProfilePreviewType, ScreenType, UserType, } from './../types/types'; -- cgit v1.2.3-70-g09d2 From 05bcb134a026ee98919438044a63a656f60e74f2 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 21:06:48 -0400 Subject: fixed layout issue --- src/utils/comments.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index 47d26bb5..a71e3857 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -6,7 +6,6 @@ import { Part, PartType, } from 'react-native-controlled-mentions'; -import {TouchableOpacity} from 'react-native-gesture-handler'; import TaggTypeahead from '../components/common/TaggTypeahead'; import {TAGG_LIGHT_BLUE} from '../constants'; import {UserType} from '../types'; @@ -29,8 +28,9 @@ const renderPart = ( // Mention type part if (isMentionPartType(part.partType)) { return ( - { if (part.data) { handlePress({ @@ -39,8 +39,8 @@ const renderPart = ( }); } }}> - {part.text} - + {part.text} + ); } -- cgit v1.2.3-70-g09d2