aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/comments/AddComment.tsx96
-rw-r--r--src/components/comments/CommentsCount.tsx58
-rw-r--r--src/components/comments/index.ts1
-rw-r--r--src/components/common/BottomDrawer.tsx2
-rw-r--r--src/components/common/GenericMoreInfoDrawer.tsx14
-rw-r--r--src/components/moments/IndividualMomentTitleBar.tsx12
-rw-r--r--src/components/moments/MomentCommentPreview.tsx97
-rw-r--r--src/components/moments/MomentPost.tsx67
-rw-r--r--src/components/moments/MomentPostContent.tsx157
-rw-r--r--src/components/moments/MomentPostHeader.tsx15
-rw-r--r--src/components/notifications/Notification.tsx11
-rw-r--r--src/components/notifications/NotificationPill.tsx209
-rw-r--r--src/components/notifications/index.ts1
-rw-r--r--src/components/profile/MomentMoreInfoDrawer.tsx90
-rw-r--r--src/components/profile/ProfileMoreInfoDrawer.tsx5
-rw-r--r--src/components/suggestedPeople/legacy/BadgesDropdown.tsx6
16 files changed, 577 insertions, 264 deletions
diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index b229d010..9667046c 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -24,10 +24,21 @@ import {MentionInputControlled} from './MentionInputControlled';
export interface AddCommentProps {
momentId: string;
placeholderText: string;
+ callback?: (message: string) => void;
+ onFocus?: () => void;
+ isKeyboardAvoiding?: boolean;
+ theme?: 'dark' | 'white';
}
-const AddComment: React.FC<AddCommentProps> = ({momentId, placeholderText}) => {
- const {setShouldUpdateAllComments, commentTapped} =
+const AddComment: React.FC<AddCommentProps> = ({
+ momentId,
+ placeholderText,
+ callback = (_) => null,
+ onFocus = () => null,
+ isKeyboardAvoiding = true,
+ theme = 'white',
+}) => {
+ const {setShouldUpdateAllComments = () => null, commentTapped} =
useContext(CommentContext);
const [inReplyToMention, setInReplyToMention] = useState('');
const [comment, setComment] = useState('');
@@ -50,13 +61,15 @@ const AddComment: React.FC<AddCommentProps> = ({momentId, placeholderText}) => {
if (trimmed === '') {
return;
}
+ const message = inReplyToMention + trimmed;
const postedComment = await postComment(
- inReplyToMention + trimmed,
+ message,
objectId,
isReplyingToComment || isReplyingToReply,
);
if (postedComment) {
+ callback(message);
setComment('');
setInReplyToMention('');
@@ -100,43 +113,63 @@ const AddComment: React.FC<AddCommentProps> = ({momentId, placeholderText}) => {
}
}, [isReplyingToComment, isReplyingToReply, commentTapped]);
- return (
- <KeyboardAvoidingView
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
- keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}>
- <View
- style={[
- styles.container,
- keyboardVisible ? styles.whiteBackround : {},
- ]}>
- <View style={styles.textContainer}>
- <Avatar style={styles.avatar} uri={avatar} />
- <MentionInputControlled
- containerStyle={styles.text}
- placeholder={placeholderText}
- value={inReplyToMention + comment}
- onChange={(newText: string) => {
- // skipping the `inReplyToMention` text
- setComment(
- newText.substring(inReplyToMention.length, newText.length),
- );
- }}
- inputRef={ref}
- partTypes={mentionPartTypes('blue')}
- />
+ const mainContent = () => (
+ <View
+ style={[
+ theme === 'white' ? styles.containerWhite : styles.containerDark,
+ keyboardVisible && theme !== 'dark' ? styles.whiteBackround : {},
+ ]}>
+ <View style={styles.textContainer}>
+ <Avatar style={styles.avatar} uri={avatar} />
+ <MentionInputControlled
+ containerStyle={styles.text}
+ placeholderTextColor={theme === 'dark' ? '#828282' : undefined}
+ placeholder={placeholderText}
+ value={inReplyToMention + comment}
+ onFocus={onFocus}
+ onChange={(newText: string) => {
+ // skipping the `inReplyToMention` text
+ setComment(
+ newText.substring(inReplyToMention.length, newText.length),
+ );
+ }}
+ inputRef={ref}
+ partTypes={mentionPartTypes('blue')}
+ />
+ {(theme === 'white' || (theme === 'dark' && keyboardVisible)) && (
<View style={styles.submitButton}>
- <TouchableOpacity style={styles.submitButton} onPress={addComment}>
+ <TouchableOpacity
+ style={
+ comment === ''
+ ? [styles.submitButton, styles.greyButton]
+ : styles.submitButton
+ }
+ disabled={comment === ''}
+ onPress={addComment}>
<UpArrowIcon width={35} height={35} color={'white'} />
</TouchableOpacity>
</View>
- </View>
+ )}
</View>
+ </View>
+ );
+ return isKeyboardAvoiding ? (
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}>
+ {mainContent()}
</KeyboardAvoidingView>
+ ) : (
+ mainContent()
);
};
const styles = StyleSheet.create({
- container: {
+ containerDark: {
+ alignItems: 'center',
+ width: SCREEN_WIDTH,
+ },
+ containerWhite: {
backgroundColor: '#f7f7f7',
alignItems: 'center',
width: SCREEN_WIDTH,
@@ -176,6 +209,9 @@ const styles = StyleSheet.create({
marginVertical: '2%',
alignSelf: 'flex-end',
},
+ greyButton: {
+ backgroundColor: 'grey',
+ },
whiteBackround: {
backgroundColor: '#fff',
},
diff --git a/src/components/comments/CommentsCount.tsx b/src/components/comments/CommentsCount.tsx
deleted file mode 100644
index f4f8197d..00000000
--- a/src/components/comments/CommentsCount.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import {useNavigation} from '@react-navigation/native';
-import * as React from 'react';
-import {StyleSheet, TouchableOpacity} from 'react-native';
-import {Text} from 'react-native-animatable';
-import CommentIcon from '../../assets/icons/moment-comment-icon.svg';
-import {ScreenType} from '../../types';
-
-/**
- * Provides a view for the comment icon and the comment count.
- * When the user clicks on this view, a new screen opens to display all the comments.
- */
-
-type CommentsCountProps = {
- commentsCount: string;
- momentId: string;
- screenType: ScreenType;
-};
-
-const CommentsCount: React.FC<CommentsCountProps> = ({
- commentsCount,
- momentId,
- screenType,
-}) => {
- const navigation = useNavigation();
- const navigateToCommentsScreen = async () => {
- navigation.push('MomentCommentsScreen', {
- moment_id: momentId,
- screenType,
- });
- };
- return (
- <>
- <TouchableOpacity onPress={navigateToCommentsScreen}>
- <CommentIcon style={styles.image} />
- <Text style={styles.count}>
- {commentsCount !== '0' ? commentsCount : ''}
- </Text>
- </TouchableOpacity>
- </>
- );
-};
-
-const styles = StyleSheet.create({
- image: {
- position: 'relative',
- width: 21,
- height: 21,
- },
- count: {
- position: 'relative',
- fontWeight: 'bold',
- color: 'white',
- paddingTop: '3%',
- textAlign: 'center',
- },
-});
-
-export default CommentsCount;
diff --git a/src/components/comments/index.ts b/src/components/comments/index.ts
index 6293f799..ebd93844 100644
--- a/src/components/comments/index.ts
+++ b/src/components/comments/index.ts
@@ -1,3 +1,2 @@
-export {default as CommentsCount} from '../comments/CommentsCount';
export {default as CommentTile} from './CommentTile';
export {default as AddComment} from './AddComment';
diff --git a/src/components/common/BottomDrawer.tsx b/src/components/common/BottomDrawer.tsx
index 16e98690..b79b8820 100644
--- a/src/components/common/BottomDrawer.tsx
+++ b/src/components/common/BottomDrawer.tsx
@@ -23,7 +23,7 @@ const BottomDrawer: React.FC<BottomDrawerProps> = (props) => {
const {isOpen, setIsOpen, showHeader, initialSnapPosition} = props;
const drawerRef = useRef<BottomSheet>(null);
const [modalVisible, setModalVisible] = useState(isOpen);
- const bgAlpha = useValue(isOpen ? 1 : 0);
+ const bgAlpha = useValue(isOpen ? 0 : 1);
useEffect(() => {
if (isOpen) {
diff --git a/src/components/common/GenericMoreInfoDrawer.tsx b/src/components/common/GenericMoreInfoDrawer.tsx
index 0928ed44..cfc45131 100644
--- a/src/components/common/GenericMoreInfoDrawer.tsx
+++ b/src/components/common/GenericMoreInfoDrawer.tsx
@@ -3,15 +3,16 @@ import {
GestureResponderEvent,
StyleSheet,
Text,
+ TextStyle,
TouchableOpacity,
View,
ViewProps,
ViewStyle,
} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
-import BottomDrawer from './BottomDrawer';
import {TAGG_LIGHT_BLUE} from '../../constants';
import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import BottomDrawer from './BottomDrawer';
// conforms the JSX onPress attribute type
type OnPressHandler = (event: GestureResponderEvent) => void;
@@ -20,13 +21,12 @@ interface GenericMoreInfoDrawerProps extends ViewProps {
isOpen: boolean;
setIsOpen: (visible: boolean) => void;
showIcons: boolean;
- textColor: string;
// An array of title, onPressHandler, and icon component
- buttons: [string, OnPressHandler, JSX.Element?][];
+ buttons: [string, OnPressHandler, JSX.Element?, TextStyle?][];
}
const GenericMoreInfoDrawer: React.FC<GenericMoreInfoDrawerProps> = (props) => {
- const {buttons, showIcons, textColor} = props;
+ const {buttons, showIcons} = props;
// each button is 80px high, cancel button is always there
const initialSnapPosition =
(buttons.length + 1) * 80 + useSafeAreaInsets().bottom;
@@ -44,13 +44,11 @@ const GenericMoreInfoDrawer: React.FC<GenericMoreInfoDrawerProps> = (props) => {
showHeader={false}
initialSnapPosition={initialSnapPosition}>
<View style={styles.panel}>
- {buttons.map(([title, action, icon], index) => (
+ {buttons.map(([title, action, icon, textStyle], index) => (
<View key={index}>
<TouchableOpacity style={panelButtonStyle} onPress={action}>
{showIcons && <View style={styles.icon}>{icon}</View>}
- <Text style={[styles.panelButtonTitle, {color: textColor}]}>
- {title}
- </Text>
+ <Text style={[styles.panelButtonTitle, textStyle]}>{title}</Text>
</TouchableOpacity>
<View style={styles.divider} />
</View>
diff --git a/src/components/moments/IndividualMomentTitleBar.tsx b/src/components/moments/IndividualMomentTitleBar.tsx
index 79453ade..4ae9471f 100644
--- a/src/components/moments/IndividualMomentTitleBar.tsx
+++ b/src/components/moments/IndividualMomentTitleBar.tsx
@@ -1,8 +1,13 @@
import React from 'react';
-import {TouchableOpacity} from 'react-native';
-import {Text, View, StyleSheet, ViewProps} from 'react-native';
-import {normalize} from '../../utils';
+import {
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+ ViewProps,
+} from 'react-native';
import CloseIcon from '../../assets/ionicons/close-outline.svg';
+import {normalize} from '../../utils';
interface IndividualMomentTitleBarProps extends ViewProps {
title: string;
@@ -30,7 +35,6 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
- height: '5%',
},
headerContainer: {
width: '80%',
diff --git a/src/components/moments/MomentCommentPreview.tsx b/src/components/moments/MomentCommentPreview.tsx
new file mode 100644
index 00000000..e53ed258
--- /dev/null
+++ b/src/components/moments/MomentCommentPreview.tsx
@@ -0,0 +1,97 @@
+import {useNavigation} from '@react-navigation/native';
+import React from 'react';
+import {Image, StyleSheet, Text, View} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {useDispatch, useStore} from 'react-redux';
+import {MomentCommentPreviewType, ScreenType, UserType} from '../../types';
+import {navigateToProfile, normalize} from '../../utils';
+import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments';
+
+interface MomentCommentPreviewProps {
+ momentId: string;
+ commentsCount: number;
+ commentPreview: MomentCommentPreviewType | null;
+ screenType: ScreenType;
+}
+
+const MomentCommentPreview: React.FC<MomentCommentPreviewProps> = ({
+ momentId,
+ commentsCount,
+ commentPreview,
+ screenType,
+}) => {
+ const navigation = useNavigation();
+ const state = useStore().getState();
+ const commentCountText =
+ commentsCount === 0 ? 'No Comments' : commentsCount + ' comments';
+
+ return (
+ <TouchableOpacity
+ style={styles.commentsPreviewContainer}
+ onPress={() =>
+ navigation.push('MomentCommentsScreen', {
+ moment_id: momentId,
+ screenType,
+ })
+ }>
+ <Text style={styles.whiteBold}>{commentCountText}</Text>
+ {commentPreview !== null && (
+ <View style={styles.previewContainer}>
+ <Image
+ source={{
+ uri: commentPreview.commenter.thumbnail_url,
+ }}
+ style={styles.avatar}
+ />
+ <Text style={styles.whiteBold} numberOfLines={1}>
+ <Text> </Text>
+ <Text>{commentPreview.commenter.username}</Text>
+ <Text> </Text>
+ {renderTextWithMentions({
+ value: commentPreview.comment,
+ styles: styles.normalFont,
+ partTypes: mentionPartTypes('white'),
+ onPress: (user: UserType) =>
+ navigateToProfile(
+ state,
+ useDispatch,
+ navigation,
+ screenType,
+ user,
+ ),
+ })}
+ </Text>
+ </View>
+ )}
+ </TouchableOpacity>
+ );
+};
+
+const styles = StyleSheet.create({
+ commentsPreviewContainer: {
+ height: normalize(50),
+ flexDirection: 'column',
+ justifyContent: 'space-around',
+ marginHorizontal: '5%',
+ marginBottom: '2%',
+ },
+ whiteBold: {
+ fontWeight: '700',
+ color: 'white',
+ fontSize: normalize(13),
+ },
+ previewContainer: {
+ flexDirection: 'row',
+ width: '95%',
+ },
+ avatar: {
+ height: normalize(16),
+ width: normalize(16),
+ borderRadius: 99,
+ },
+ normalFont: {
+ fontWeight: 'normal',
+ },
+});
+
+export default MomentCommentPreview;
diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx
index 7149a5b4..d87028e3 100644
--- a/src/components/moments/MomentPost.tsx
+++ b/src/components/moments/MomentPost.tsx
@@ -1,21 +1,25 @@
import React, {useEffect, useState} from 'react';
-import {StyleSheet, View} from 'react-native';
+import {StyleSheet} from 'react-native';
import {useSelector} from 'react-redux';
import {MomentPostContent, MomentPostHeader} from '.';
import {deleteMomentTag, loadMomentTags} from '../../services';
import {RootState} from '../../store/rootReducer';
-import {MomentTagType, MomentType, ScreenType} from '../../types';
-import {SCREEN_HEIGHT, SCREEN_WIDTH, StatusBarHeight} from '../../utils';
+import {MomentPostType, MomentTagType, ScreenType} from '../../types';
+import {normalize, SCREEN_HEIGHT} from '../../utils';
interface MomentPostProps {
- item: MomentType;
+ moment: MomentPostType;
userXId: string | undefined;
screenType: ScreenType;
+ index: number;
}
-const ITEM_HEIGHT = SCREEN_HEIGHT * 0.9;
-
-const MomentPost: React.FC<MomentPostProps> = ({item, userXId, screenType}) => {
+const MomentPost: React.FC<MomentPostProps> = ({
+ moment,
+ userXId,
+ screenType,
+ index,
+}) => {
const {userId: loggedInUserId, username: loggedInUsername} = useSelector(
(state: RootState) => state.user.user,
);
@@ -30,16 +34,13 @@ const MomentPost: React.FC<MomentPostProps> = ({item, userXId, screenType}) => {
const isOwnProfile = username === loggedInUsername;
- const loadTags = async () => {
- const response = await loadMomentTags(item.moment_id);
- setTags(response ? response : []);
- };
-
/*
* Load tags on initial render to pass tags data to moment header and content
*/
useEffect(() => {
- loadTags();
+ loadMomentTags(moment.moment_id).then((response) => {
+ setTags(response ? response : []);
+ });
}, []);
/*
@@ -71,52 +72,34 @@ const MomentPost: React.FC<MomentPostProps> = ({item, userXId, screenType}) => {
};
return (
- <View style={styles.postContainer}>
+ <>
<MomentPostHeader
+ style={styles.postHeader}
userXId={userXId}
screenType={screenType}
username={isOwnProfile ? loggedInUsername : username}
- momentId={item.moment_id}
- style={styles.postHeader}
momentTagId={momentTagId}
removeTag={removeTag}
+ moment={moment}
+ tags={tags}
/>
<MomentPostContent
style={styles.postContent}
- momentId={item.moment_id}
- caption={item.caption}
- pathHash={item.moment_url}
- dateTime={item.date_created}
+ moment={moment}
screenType={screenType}
momentTags={tags}
+ index={index}
/>
- </View>
+ </>
);
};
const styles = StyleSheet.create({
- contentContainer: {
- width: SCREEN_WIDTH,
- height: SCREEN_HEIGHT,
- paddingTop: StatusBarHeight,
- flex: 1,
- paddingBottom: 0,
- },
- content: {
- flex: 9,
- },
- header: {
- flex: 1,
- },
- postContainer: {
- height: ITEM_HEIGHT,
- width: SCREEN_WIDTH,
- flex: 1,
- },
- postHeader: {
- flex: 1,
+ postHeader: {},
+ postContent: {
+ minHeight: SCREEN_HEIGHT * 0.8,
+ paddingBottom: normalize(20),
},
- postContent: {flex: 9},
});
export default MomentPost;
diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx
index 4a1f3894..aca2999c 100644
--- a/src/components/moments/MomentPostContent.tsx
+++ b/src/components/moments/MomentPostContent.tsx
@@ -1,77 +1,81 @@
import {useNavigation} from '@react-navigation/native';
-import React, {useEffect, useRef, useState} from 'react';
+import React, {useContext, useEffect, useRef, useState} from 'react';
import {Image, StyleSheet, Text, View, ViewProps} from 'react-native';
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
-import Animated, {Easing} from 'react-native-reanimated';
+import Animated, {EasingNode} from 'react-native-reanimated';
import {useDispatch, useStore} from 'react-redux';
-import {getCommentsCount} from '../../services';
+import {MomentContext} from '../../screens/profile/IndividualMoment';
import {RootState} from '../../store/rootReducer';
-import {MomentTagType, ScreenType, UserType} from '../../types';
import {
+ MomentCommentPreviewType,
+ MomentPostType,
+ MomentTagType,
+ ScreenType,
+ UserType,
+} from '../../types';
+import {
+ getLoggedInUserAsProfilePreview,
getTimePosted,
navigateToProfile,
normalize,
- SCREEN_HEIGHT,
SCREEN_WIDTH,
} from '../../utils';
import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments';
-import {CommentsCount} from '../comments';
+import {AddComment} from '../comments';
import {MomentTags} from '../common';
+import MomentCommentPreview from './MomentCommentPreview';
interface MomentPostContentProps extends ViewProps {
screenType: ScreenType;
- momentId: string;
- caption: string;
- pathHash: string;
- dateTime: string;
+ moment: MomentPostType;
momentTags: MomentTagType[];
+ index: number;
}
const MomentPostContent: React.FC<MomentPostContentProps> = ({
screenType,
- momentId,
- caption,
- pathHash,
- dateTime,
+ moment,
style,
momentTags,
+ index,
}) => {
+ const [tags, setTags] = useState<MomentTagType[]>(momentTags);
const state: RootState = useStore().getState();
const navigation = useNavigation();
const dispatch = useDispatch();
- const [elapsedTime, setElapsedTime] = useState('');
- const [comments_count, setCommentsCount] = useState('');
- const [tags, setTags] = useState<MomentTagType[]>(momentTags);
const imageRef = useRef(null);
const [visible, setVisible] = useState(false);
-
const [fadeValue, setFadeValue] = useState<Animated.Value<number>>(
new Animated.Value(0),
);
+ const [commentCount, setCommentCount] = useState<number>(
+ moment.comments_count,
+ );
+ const [commentPreview, setCommentPreview] =
+ useState<MomentCommentPreviewType | null>(moment.comment_preview);
+ const {keyboardVisible, scrollTo} = useContext(MomentContext);
+ const [hideText, setHideText] = useState(false);
useEffect(() => {
setTags(momentTags);
}, [momentTags]);
useEffect(() => {
- const fetchCommentsCount = async () => {
- const count = await getCommentsCount(momentId, false);
- setCommentsCount(count);
- };
- setElapsedTime(getTimePosted(dateTime));
- fetchCommentsCount();
- }, [dateTime, momentId]);
-
- useEffect(() => {
const fade = async () => {
Animated.timing(fadeValue, {
toValue: 1,
duration: 250,
- easing: Easing.linear,
+ easing: EasingNode.linear,
}).start();
};
fade();
}, [fadeValue]);
+ useEffect(() => {
+ if (!keyboardVisible && hideText) {
+ setHideText(false);
+ }
+ }, [keyboardVisible, hideText]);
+
return (
<View style={[styles.container, style]}>
<TouchableWithoutFeedback
@@ -82,76 +86,95 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
<Image
ref={imageRef}
style={styles.image}
- source={{uri: pathHash}}
+ source={{uri: moment.moment_url}}
resizeMode={'cover'}
/>
{tags.length > 0 && (
<Image
source={require('../../assets/icons/tag_indicate.png')}
- style={[styles.tagIcon]}
+ style={styles.tagIcon}
/>
)}
</TouchableWithoutFeedback>
{visible && (
<Animated.View style={[styles.tapTag, {opacity: fadeValue}]}>
- <MomentTags editing={false} tags={tags} imageRef={imageRef} />
+ <MomentTags
+ editing={false}
+ tags={tags}
+ setTags={() => null}
+ imageRef={imageRef}
+ />
</Animated.View>
)}
- <View style={styles.footerContainer}>
- <CommentsCount
- commentsCount={comments_count}
- momentId={momentId}
- screenType={screenType}
- />
- <Text style={styles.text}>{elapsedTime}</Text>
- </View>
- {renderTextWithMentions({
- value: caption,
- styles: styles.captionText,
- partTypes: mentionPartTypes('white'),
- onPress: (user: UserType) =>
- navigateToProfile(state, dispatch, navigation, screenType, user),
- })}
+ {!hideText && (
+ <>
+ {moment.caption !== '' &&
+ renderTextWithMentions({
+ value: moment.caption,
+ styles: styles.captionText,
+ partTypes: mentionPartTypes('white'),
+ onPress: (user: UserType) =>
+ navigateToProfile(
+ state,
+ dispatch,
+ navigation,
+ screenType,
+ user,
+ ),
+ })}
+ <MomentCommentPreview
+ momentId={moment.moment_id}
+ commentsCount={commentCount}
+ commentPreview={commentPreview}
+ screenType={screenType}
+ />
+ </>
+ )}
+ <AddComment
+ placeholderText={'Add a comment here!'}
+ momentId={moment.moment_id}
+ callback={(message) => {
+ setCommentPreview({
+ commenter: getLoggedInUserAsProfilePreview(state),
+ comment: message,
+ });
+ setCommentCount(commentCount + 1);
+ }}
+ onFocus={() => {
+ setHideText(true);
+ scrollTo(index);
+ }}
+ isKeyboardAvoiding={false}
+ theme={'dark'}
+ />
+ <Text style={styles.text}>{getTimePosted(moment.date_created)}</Text>
</View>
);
};
const styles = StyleSheet.create({
- container: {
- height: SCREEN_HEIGHT,
- },
+ container: {},
image: {
width: SCREEN_WIDTH,
aspectRatio: 1,
marginBottom: '3%',
},
- footerContainer: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- marginLeft: '7%',
- marginRight: '5%',
- marginBottom: '2%',
- },
text: {
- position: 'relative',
- paddingBottom: '1%',
- paddingTop: '1%',
- marginLeft: '7%',
- marginRight: '2%',
- color: '#ffffff',
- fontWeight: 'bold',
+ marginHorizontal: '5%',
+ color: 'white',
+ fontWeight: '500',
+ textAlign: 'right',
+ marginTop: 5,
},
captionText: {
position: 'relative',
- paddingBottom: '34%',
- paddingTop: '1%',
- marginLeft: '5%',
- marginRight: '5%',
+ marginHorizontal: '5%',
color: '#ffffff',
fontWeight: '500',
fontSize: normalize(13),
lineHeight: normalize(15.51),
letterSpacing: normalize(0.6),
+ marginBottom: normalize(18),
},
tapTag: {
position: 'absolute',
diff --git a/src/components/moments/MomentPostHeader.tsx b/src/components/moments/MomentPostHeader.tsx
index dc6a3cd9..5f26951a 100644
--- a/src/components/moments/MomentPostHeader.tsx
+++ b/src/components/moments/MomentPostHeader.tsx
@@ -10,7 +10,7 @@ import {
import {useDispatch, useSelector, useStore} from 'react-redux';
import {loadUserMoments} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import {ScreenType} from '../../types';
+import {MomentTagType, MomentType, ScreenType} from '../../types';
import {fetchUserX, userXInStore} from '../../utils';
import {MomentMoreInfoDrawer} from '../profile';
import TaggAvatar from '../profile/TaggAvatar';
@@ -19,19 +19,21 @@ interface MomentPostHeaderProps extends ViewProps {
userXId?: string;
screenType: ScreenType;
username: string;
- momentId: string;
momentTagId: string;
removeTag: () => Promise<void>;
+ moment: MomentType;
+ tags: MomentTagType[];
}
const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({
userXId,
screenType,
username,
- momentId,
style,
momentTagId,
removeTag,
+ moment,
+ tags,
}) => {
const [drawerVisible, setDrawerVisible] = useState(false);
const dispatch = useDispatch();
@@ -62,20 +64,23 @@ const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({
style={styles.avatar}
userXId={userXId}
screenType={screenType}
+ editable={false}
/>
<Text style={styles.headerText}>{username}</Text>
</TouchableOpacity>
<MomentMoreInfoDrawer
isOpen={drawerVisible}
setIsOpen={setDrawerVisible}
- momentId={momentId}
isOwnProfile={isOwnProfile}
momentTagId={momentTagId}
removeTag={removeTag}
dismissScreenAndUpdate={() => {
dispatch(loadUserMoments(loggedInUserId));
- navigation.pop();
+ navigation.goBack();
}}
+ screenType={screenType}
+ moment={moment}
+ tags={tags}
/>
</View>
);
diff --git a/src/components/notifications/Notification.tsx b/src/components/notifications/Notification.tsx
index 3f9cc56a..fd1b11ac 100644
--- a/src/components/notifications/Notification.tsx
+++ b/src/components/notifications/Notification.tsx
@@ -2,9 +2,7 @@ import {useNavigation} from '@react-navigation/native';
import React, {useEffect, useState} from 'react';
import {Alert, Image, StyleSheet, Text, View} from 'react-native';
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
-import LinearGradient from 'react-native-linear-gradient';
import {useDispatch, useStore} from 'react-redux';
-import {BACKGROUND_GRADIENT_MAP} from '../../constants';
import {ERROR_DELETED_OBJECT} from '../../constants/strings';
import {loadImageFromURL} from '../../services';
import {
@@ -49,7 +47,6 @@ const Notification: React.FC<NotificationProps> = (props) => {
verbage,
notification_type,
notification_object,
- unread,
timestamp,
},
screenType,
@@ -323,13 +320,7 @@ const Notification: React.FC<NotificationProps> = (props) => {
</View>
);
- return unread ? (
- <LinearGradient colors={BACKGROUND_GRADIENT_MAP[2]} useAngle angle={90}>
- {renderContent()}
- </LinearGradient>
- ) : (
- renderContent()
- );
+ return renderContent();
};
const styles = StyleSheet.create({
diff --git a/src/components/notifications/NotificationPill.tsx b/src/components/notifications/NotificationPill.tsx
new file mode 100644
index 00000000..525cd7fa
--- /dev/null
+++ b/src/components/notifications/NotificationPill.tsx
@@ -0,0 +1,209 @@
+import React, {useEffect, useState, useRef} from 'react';
+import {Image, StyleSheet, Text, View} from 'react-native';
+import LinearGradient from 'react-native-linear-gradient';
+import {SCREEN_WIDTH, isIPhoneX, numberWithCommas} from '../../utils';
+import {
+ NOTIFICATION_ICON_GRADIENT,
+ CHIN_HEIGHT,
+ NAV_BAR_HEIGHT,
+} from '../../constants';
+import {getNotificationsUnreadCount} from '../../services';
+import {normalize} from 'react-native-elements';
+import PillIcon4 from '../../assets/images/Group 479.svg';
+
+interface NotificationPillProps {
+ showIcon: boolean;
+}
+
+export const NotificationPill: React.FC<NotificationPillProps> = ({
+ showIcon,
+}) => {
+ const [iconStart, setIconStart] = useState<number[]>([0, -100]);
+ const [tipStart, setTipStart] = useState<number[]>([0, -100]);
+ const [notificationSets, setNotificationSets] = useState<{
+ CMT?: number;
+ FRD_REQ?: number;
+ P_VIEW?: number;
+ MOM_TAG?: number;
+ }>({});
+ const [timeCount, setTimeCount] = useState<boolean>(false);
+ const [timeOut, setTimeOut] = useState<boolean>(false);
+ const iconRef = useRef(null);
+ const tipRef = useRef(null);
+ const pillTip = require('../../assets/images/purple-tip.png');
+
+ const navBarPos = 20;
+
+ // If there are notifications, determines the size of the pill
+ // and sets points for correct placement
+ useEffect(() => {
+ setTimeout(() => {
+ if (iconRef.current) {
+ iconRef.current.measure(
+ (
+ _fx: number,
+ _fy: number,
+ width: number,
+ height: number,
+ _px: number,
+ _py: number,
+ ) => {
+ if (tipRef.current) {
+ tipRef.current.measure(
+ (
+ __fx: number,
+ __fy: number,
+ width2: number,
+ __height: number,
+ __px: number,
+ __py: number,
+ ) => {
+ const x = SCREEN_WIDTH / 2 - width / 2;
+ const y = isIPhoneX()
+ ? CHIN_HEIGHT + NAV_BAR_HEIGHT + navBarPos
+ : NAV_BAR_HEIGHT + navBarPos;
+ setIconStart([x, y]);
+ setTipStart([width / 2 - width2 / 2, height - 1]);
+ setTimeCount(true);
+ },
+ );
+ }
+ },
+ );
+ } else {
+ }
+ }, 100);
+ }, [notificationSets, iconRef, tipRef]);
+
+ // Used so that pill disappears after 10 seconds
+ useEffect(() => {
+ if (timeCount) {
+ setTimeout(() => {
+ setTimeOut(true);
+ }, 10000);
+ }
+ }, [timeCount]);
+
+ // Gets data from backend to check for unreads
+ useEffect(() => {
+ const getCount = async () => {
+ const data = await getNotificationsUnreadCount();
+ setTimeout(() => {
+ if (data) {
+ setNotificationSets(data);
+ }
+ }, 100);
+ };
+
+ getCount();
+ }, []);
+
+ return (
+ <>
+ {notificationSets &&
+ Object.keys(notificationSets).length !== 0 &&
+ showIcon &&
+ !timeOut && (
+ <View
+ style={[
+ styles.purpleContainer,
+ {bottom: iconStart[1], left: iconStart[0]},
+ ]}
+ ref={iconRef}>
+ <LinearGradient
+ colors={NOTIFICATION_ICON_GRADIENT}
+ style={styles.iconPurple}>
+ {notificationSets.CMT && (
+ <>
+ <Image
+ source={require('../../assets/images/pill-icon-1.png')}
+ style={styles.indicationIcon}
+ />
+ <Text style={styles.text}>
+ {numberWithCommas(notificationSets.CMT)}
+ </Text>
+ </>
+ )}
+ {notificationSets.FRD_REQ && (
+ <>
+ <Image
+ source={require('../../assets/images/pill-icon-2.png')}
+ style={styles.indicationIcon}
+ />
+ <Text style={styles.text}>
+ {numberWithCommas(notificationSets.FRD_REQ)}
+ </Text>
+ </>
+ )}
+ {notificationSets.P_VIEW && (
+ <>
+ <Image
+ source={require('../../assets/images/pill-icon-3.png')}
+ style={styles.indicationIcon}
+ />
+ <Text style={styles.text}>
+ {numberWithCommas(notificationSets.P_VIEW)}
+ </Text>
+ </>
+ )}
+ {notificationSets.MOM_TAG && (
+ <>
+ <PillIcon4 style={styles.indicationIcon} />
+ <Text style={styles.text}>
+ {numberWithCommas(notificationSets.MOM_TAG)}
+ </Text>
+ </>
+ )}
+ </LinearGradient>
+ <Image
+ style={[styles.tip, {top: tipStart[1], left: tipStart[0]}]}
+ source={pillTip}
+ ref={tipRef}
+ />
+ </View>
+ )}
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ purpleContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ position: 'absolute',
+ zIndex: 999,
+ },
+ iconPurple: {
+ padding: 5,
+ borderRadius: 15,
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ text: {
+ margin: 2,
+ color: 'white',
+ fontSize: normalize(10),
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginRight: 5,
+ },
+ tip: {
+ position: 'absolute',
+ zIndex: 999,
+ height: 12,
+ flex: 1,
+ resizeMode: 'contain',
+ },
+ indicationIcon: {
+ height: 14,
+ width: 14,
+ margin: 2,
+ marginLeft: 5,
+ },
+ svgIndicationIcon: {
+ height: 14,
+ width: 14,
+ margin: 3,
+ },
+});
diff --git a/src/components/notifications/index.ts b/src/components/notifications/index.ts
index 733b56f1..077c26a4 100644
--- a/src/components/notifications/index.ts
+++ b/src/components/notifications/index.ts
@@ -1,2 +1,3 @@
export {default as Notification} from './Notification';
export {InviteFriendsPrompt} from './NotificationPrompts';
+export {NotificationPill} from './NotificationPill';
diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx
index 1265497e..a796ffd8 100644
--- a/src/components/profile/MomentMoreInfoDrawer.tsx
+++ b/src/components/profile/MomentMoreInfoDrawer.tsx
@@ -1,44 +1,58 @@
+import {useNavigation} from '@react-navigation/core';
import React, {useEffect, useState} from 'react';
import {
Alert,
GestureResponderEvent,
StyleSheet,
+ TextStyle,
TouchableOpacity,
ViewProps,
} from 'react-native';
import MoreIcon from '../../assets/icons/more_horiz-24px.svg';
import {ERROR_DELETE_MOMENT, MOMENT_DELETED_MSG} from '../../constants/strings';
import {deleteMoment, sendReport} from '../../services';
+import {MomentTagType, MomentType, ScreenType} from '../../types/types';
import {GenericMoreInfoDrawer} from '../common';
enum MomentDrawerOptions {
DeleteMoment = 'Delete Moment',
ReportIssue = 'Report an Issue',
RemoveTag = 'Remove yourself from moment',
+ EditMoment = 'Edit Moment',
}
interface MomentMoreInfoDrawerProps extends ViewProps {
isOpen: boolean;
setIsOpen: (visible: boolean) => void;
- momentId: string;
isOwnProfile: boolean;
momentTagId: string;
removeTag: () => Promise<void>;
dismissScreenAndUpdate: () => void;
+ screenType: ScreenType;
+ moment: MomentType;
+ tags: MomentTagType[];
}
const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
const {
- momentId,
setIsOpen,
isOwnProfile,
dismissScreenAndUpdate,
momentTagId,
removeTag,
+ screenType,
+ moment,
+ tags,
} = props;
+ const navigation = useNavigation();
+
+ const [drawerButtons, setDrawerButtons] = useState<
+ [string, (event: GestureResponderEvent) => void, JSX.Element?, TextStyle?][]
+ >([]);
+
const handleDeleteMoment = async () => {
setIsOpen(false);
- deleteMoment(momentId).then((success) => {
+ deleteMoment(moment.moment_id).then((success) => {
if (success) {
// set time out for UI transitions
setTimeout(() => {
@@ -88,7 +102,8 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
[
{
text: 'Mark as inappropriate',
- onPress: () => sendReport(momentId, 'Mark as inappropriate'),
+ onPress: () =>
+ sendReport(moment.moment_id, 'Mark as inappropriate'),
},
{
text: 'Cancel',
@@ -96,7 +111,7 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
},
{
text: 'Mark as abusive',
- onPress: () => sendReport(momentId, 'Mark as abusive'),
+ onPress: () => sendReport(moment.moment_id, 'Mark as abusive'),
},
],
{cancelable: false},
@@ -104,42 +119,52 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
}, 500);
};
- const [drawerButtons, setDrawerButtons] = useState<
- [string, (event: GestureResponderEvent) => void, JSX.Element?][]
- >([
- isOwnProfile
- ? [MomentDrawerOptions.DeleteMoment, handleDeleteMoment]
- : [MomentDrawerOptions.ReportIssue, handleReportMoment],
- ]);
+ const handleEditMoment = async () => {
+ setIsOpen(false);
+ navigation.navigate('CaptionScreen', {
+ screenType: screenType,
+ selectedTags: tags,
+ moment: moment,
+ });
+ };
/*
* Update bottom drawer options to contain/not contain 'remove tag' option
*/
useEffect(() => {
- const setupBottomDrawer = () => {
- const present = drawerButtons.findIndex(
- (button) => button[0] === MomentDrawerOptions.RemoveTag,
- );
- /*
- * If user is not tagged but button is present, remove button from bottom drawer
- * If user is tagged but button is not present, add button to bottom drawer
- */
- if (momentTagId !== '' && present === -1) {
- const localDrawerButtons = drawerButtons;
- localDrawerButtons.push([
+ let newButtons: [
+ string,
+ (event: GestureResponderEvent) => void,
+ JSX.Element?,
+ TextStyle?,
+ ][] = [];
+ if (!isOwnProfile) {
+ newButtons.push([
+ MomentDrawerOptions.ReportIssue,
+ handleReportMoment,
+ undefined,
+ {color: 'red'},
+ ]);
+ // should we have the "delete moment" option?
+ if (momentTagId !== '') {
+ newButtons.push([
MomentDrawerOptions.RemoveTag,
handleRemoveTag,
+ undefined,
+ {color: 'red'},
]);
- setDrawerButtons(localDrawerButtons);
- } else if (momentTagId === '' && present !== -1) {
- const filteredButtons = drawerButtons.filter(
- (button) => button[0] !== MomentDrawerOptions.RemoveTag,
- );
- setDrawerButtons(filteredButtons);
}
- };
- setupBottomDrawer();
- }, [momentTagId]);
+ } else {
+ newButtons.push([
+ MomentDrawerOptions.DeleteMoment,
+ handleDeleteMoment,
+ undefined,
+ {color: 'red'},
+ ]);
+ newButtons.push([MomentDrawerOptions.EditMoment, handleEditMoment]);
+ }
+ setDrawerButtons(newButtons);
+ }, [tags, momentTagId]);
return (
<>
@@ -153,7 +178,6 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
<GenericMoreInfoDrawer
{...props}
showIcons={false}
- textColor={'red'}
buttons={drawerButtons}
/>
</>
diff --git a/src/components/profile/ProfileMoreInfoDrawer.tsx b/src/components/profile/ProfileMoreInfoDrawer.tsx
index ecc45211..656f81bb 100644
--- a/src/components/profile/ProfileMoreInfoDrawer.tsx
+++ b/src/components/profile/ProfileMoreInfoDrawer.tsx
@@ -55,12 +55,12 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => {
<GenericMoreInfoDrawer
{...props}
showIcons={false}
- textColor={'red'}
buttons={[
[
(isBlocked ? 'Unblock' : 'Block') + ` ${userXName}`,
onBlockUnblock,
undefined,
+ {color: 'red'},
],
]}
/>
@@ -68,7 +68,6 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => {
<GenericMoreInfoDrawer
{...props}
showIcons={true}
- textColor={'black'}
buttons={[
[
'Settings',
@@ -77,6 +76,7 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => {
source={require('../../assets/images/settings/settings.png')}
style={styles.image}
/>,
+ {color: 'black'},
],
[
'Edit Profile',
@@ -85,6 +85,7 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => {
source={require('../../assets/images/settings/edit-profile.png')}
style={styles.image}
/>,
+ {color: 'black'},
],
]}
/>
diff --git a/src/components/suggestedPeople/legacy/BadgesDropdown.tsx b/src/components/suggestedPeople/legacy/BadgesDropdown.tsx
index 2c177e69..307205b8 100644
--- a/src/components/suggestedPeople/legacy/BadgesDropdown.tsx
+++ b/src/components/suggestedPeople/legacy/BadgesDropdown.tsx
@@ -1,7 +1,7 @@
import React, {useEffect, useState} from 'react';
import {StyleSheet} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
-import Animated, {Easing} from 'react-native-reanimated';
+import Animated, {EasingNode} from 'react-native-reanimated';
import {BadgeIcon, UniversityIcon} from '../..';
import {UniversityBadgeDisplayType, UniversityType} from '../../../types';
import {normalize} from '../../../utils';
@@ -41,7 +41,7 @@ const BadgesDropdown: React.FC<BadgesDropdownProps> = ({
Animated.timing(top[i], {
toValue: i * 40 + 50,
duration: 150,
- easing: Easing.linear,
+ easing: EasingNode.linear,
}).start();
}
}
@@ -54,7 +54,7 @@ const BadgesDropdown: React.FC<BadgesDropdownProps> = ({
Animated.timing(top[i], {
toValue: 0,
duration: 150,
- easing: Easing.linear,
+ easing: EasingNode.linear,
}).start();
}
}