aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-06-11 17:01:43 -0400
committerIvan Chen <ivan@tagg.id>2021-06-11 17:01:43 -0400
commitbcfbaf00f60b65ab5f8c38ff8766644a2496bed1 (patch)
treeaf2fd669cf1f4cd427e832a39ac9deb2cd7a1430 /src/components
parent47b087816844473be858adf766b2f538ecf6d0aa (diff)
parent17d3f1255bd7692772b675b09685a92b305e8d9b (diff)
Merge branch 'master' into tma904-moment-comment-revamp
# Conflicts: # src/components/moments/MomentPost.tsx # src/components/moments/MomentPostContent.tsx # src/services/MomentService.ts
Diffstat (limited to 'src/components')
-rw-r--r--src/components/common/GenericMoreInfoDrawer.tsx14
-rw-r--r--src/components/moments/MomentPost.tsx5
-rw-r--r--src/components/moments/MomentPostContent.tsx2
-rw-r--r--src/components/moments/MomentPostHeader.tsx12
-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
9 files changed, 289 insertions, 60 deletions
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/MomentPost.tsx b/src/components/moments/MomentPost.tsx
index 3b2721a0..d87028e3 100644
--- a/src/components/moments/MomentPost.tsx
+++ b/src/components/moments/MomentPost.tsx
@@ -74,13 +74,14 @@ const MomentPost: React.FC<MomentPostProps> = ({
return (
<>
<MomentPostHeader
+ style={styles.postHeader}
userXId={userXId}
screenType={screenType}
username={isOwnProfile ? loggedInUsername : username}
- momentId={moment.moment_id}
- style={styles.postHeader}
momentTagId={momentTagId}
removeTag={removeTag}
+ moment={moment}
+ tags={tags}
/>
<MomentPostContent
style={styles.postContent}
diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx
index f1ab5db4..e43cba4f 100644
--- a/src/components/moments/MomentPostContent.tsx
+++ b/src/components/moments/MomentPostContent.tsx
@@ -38,10 +38,10 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
momentTags,
index,
}) => {
+ const [tags, setTags] = useState<MomentTagType[]>(momentTags);
const state: RootState = useStore().getState();
const navigation = useNavigation();
const dispatch = useDispatch();
- const [tags, setTags] = useState<MomentTagType[]>(momentTags);
const imageRef = useRef(null);
const [visible, setVisible] = useState(false);
const [fadeValue, setFadeValue] = useState<Animated.Value<number>>(
diff --git a/src/components/moments/MomentPostHeader.tsx b/src/components/moments/MomentPostHeader.tsx
index 64581e06..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();
@@ -69,7 +71,6 @@ const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({
<MomentMoreInfoDrawer
isOpen={drawerVisible}
setIsOpen={setDrawerVisible}
- momentId={momentId}
isOwnProfile={isOwnProfile}
momentTagId={momentTagId}
removeTag={removeTag}
@@ -77,6 +78,9 @@ const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({
dispatch(loadUserMoments(loggedInUserId));
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'},
],
]}
/>