aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-05-21 19:18:44 -0400
committerIvan Chen <ivan@tagg.id>2021-05-21 19:18:44 -0400
commitb9930f62d502332ae3cf2e0e20a7743a3d7e1df1 (patch)
tree68ebd2dacc7b61d60cfaac7f68cd3bc5c4a133a8 /src
parentace995d7d0ceb3b0c012f6c29808c05bdb26fe71 (diff)
parent9315aa31ad4d6c305e62853a3ab7e4a484ecce98 (diff)
Merge branch 'master' into tma884-moment-view-tags
Diffstat (limited to 'src')
-rw-r--r--src/assets/icons/tag-icon.pngbin0 -> 1708 bytes
-rw-r--r--src/assets/images/Tagg-Triangle.pngbin0 -> 561 bytes
-rw-r--r--src/assets/images/draggableX.pngbin0 -> 1780 bytes
-rw-r--r--src/assets/images/tagg-rectangle.pngbin0 -> 521 bytes
-rw-r--r--src/assets/images/tagg-tip.pngbin0 -> 233 bytes
-rw-r--r--src/assets/images/tagg-x-button.pngbin0 -> 549 bytes
-rw-r--r--src/components/common/Draggable.tsx364
-rw-r--r--src/components/notifications/Notification.tsx147
-rw-r--r--src/components/taggs/TaggDraggable.tsx129
-rw-r--r--src/screens/profile/CaptionScreen.tsx68
-rw-r--r--src/types/types.ts2
11 files changed, 639 insertions, 71 deletions
diff --git a/src/assets/icons/tag-icon.png b/src/assets/icons/tag-icon.png
new file mode 100644
index 00000000..9111b036
--- /dev/null
+++ b/src/assets/icons/tag-icon.png
Binary files differ
diff --git a/src/assets/images/Tagg-Triangle.png b/src/assets/images/Tagg-Triangle.png
new file mode 100644
index 00000000..dfe97282
--- /dev/null
+++ b/src/assets/images/Tagg-Triangle.png
Binary files differ
diff --git a/src/assets/images/draggableX.png b/src/assets/images/draggableX.png
new file mode 100644
index 00000000..b99c5dfc
--- /dev/null
+++ b/src/assets/images/draggableX.png
Binary files differ
diff --git a/src/assets/images/tagg-rectangle.png b/src/assets/images/tagg-rectangle.png
new file mode 100644
index 00000000..24bfd67e
--- /dev/null
+++ b/src/assets/images/tagg-rectangle.png
Binary files differ
diff --git a/src/assets/images/tagg-tip.png b/src/assets/images/tagg-tip.png
new file mode 100644
index 00000000..eb3c5bad
--- /dev/null
+++ b/src/assets/images/tagg-tip.png
Binary files differ
diff --git a/src/assets/images/tagg-x-button.png b/src/assets/images/tagg-x-button.png
new file mode 100644
index 00000000..5ce3846d
--- /dev/null
+++ b/src/assets/images/tagg-x-button.png
Binary files differ
diff --git a/src/components/common/Draggable.tsx b/src/components/common/Draggable.tsx
new file mode 100644
index 00000000..edd29b78
--- /dev/null
+++ b/src/components/common/Draggable.tsx
@@ -0,0 +1,364 @@
+/**
+ * * https://github.com/tongyy/react-native-draggable
+ *
+ */
+
+import React from 'react';
+import {
+ Animated,
+ Dimensions,
+ GestureResponderEvent,
+ Image,
+ PanResponder,
+ PanResponderGestureState,
+ StyleProp,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+ ViewStyle,
+} from 'react-native';
+
+function clamp(number: number, min: number, max: number) {
+ return Math.max(min, Math.min(number, max));
+}
+
+interface IProps {
+ /**** props that should probably be removed in favor of "children" */
+ renderText?: string;
+ isCircle?: boolean;
+ renderSize?: number;
+ imageSource?: number;
+ renderColor?: string;
+ /**** */
+ children?: React.ReactNode;
+ shouldReverse?: boolean;
+ disabled?: boolean;
+ debug?: boolean;
+ animatedViewProps?: object;
+ touchableOpacityProps?: object;
+ onDrag?: (
+ e: GestureResponderEvent,
+ gestureState: PanResponderGestureState,
+ ) => void;
+ onShortPressRelease?: (event: GestureResponderEvent) => void;
+ onDragRelease?: (
+ e: GestureResponderEvent,
+ gestureState: PanResponderGestureState,
+ ) => void;
+ onLongPress?: (event: GestureResponderEvent) => void;
+ onPressIn?: (event: GestureResponderEvent) => void;
+ onPressOut?: (event: GestureResponderEvent) => void;
+ onRelease?: (event: GestureResponderEvent, wasDragging: boolean) => void;
+ onReverse?: () => {x: number; y: number};
+ x?: number;
+ y?: number;
+ // z/elevation should be removed because it doesn't sync up visually and haptically
+ z?: number;
+ minX?: number;
+ minY?: number;
+ maxX?: number;
+ maxY?: number;
+ onDragStart?: () => void;
+}
+
+export default function Draggable(props: IProps) {
+ const {
+ renderText,
+ isCircle,
+ renderSize,
+ imageSource,
+ renderColor,
+ children,
+ shouldReverse,
+ disabled,
+ debug,
+ animatedViewProps,
+ touchableOpacityProps,
+ onDrag,
+ onShortPressRelease,
+ onDragRelease,
+ onLongPress,
+ onPressIn,
+ onPressOut,
+ onRelease,
+ x,
+ y,
+ z,
+ minX,
+ minY,
+ maxX,
+ maxY,
+ onDragStart,
+ } = props;
+
+ // The Animated object housing our xy value so that we can spring back
+ const pan = React.useRef(new Animated.ValueXY());
+ // Always set to xy value of pan, would like to remove
+ const offsetFromStart = React.useRef({x: 0, y: 0});
+ // Width/Height of Draggable (renderSize is arbitrary if children are passed in)
+ const childSize = React.useRef({x: renderSize, y: renderSize});
+ // Top/Left/Right/Bottom location on screen from start of most recent touch
+ const startBounds = React.useRef({top: 0, bottom: 0, left: 0, right: 0});
+ // Whether we're currently dragging or not
+ const isDragging = React.useRef(false);
+
+ // const [zIndex, setZIndex] = React.useState(z);
+ const zIndex = z;
+
+ const getBounds = React.useCallback(() => {
+ const left = x + offsetFromStart.current.x;
+ const top = y + offsetFromStart.current.y;
+ return {
+ left,
+ top,
+ right: left + childSize.current.x,
+ bottom: top + childSize.current.y,
+ };
+ }, [x, y]);
+
+ const shouldStartDrag = React.useCallback(
+ (gs) => {
+ return !disabled && (Math.abs(gs.dx) > 2 || Math.abs(gs.dy) > 2);
+ },
+ [disabled],
+ );
+
+ const reversePosition = React.useCallback(() => {
+ Animated.spring(pan.current, {
+ toValue: {x: 0, y: 0},
+ useNativeDriver: false,
+ }).start();
+ }, [pan]);
+
+ const onPanResponderRelease = React.useCallback(
+ (e: GestureResponderEvent, gestureState: PanResponderGestureState) => {
+ isDragging.current = false;
+ if (onDragRelease) {
+ onDragRelease(e, gestureState);
+ onRelease(e, true);
+ }
+ if (!shouldReverse) {
+ pan.current.flattenOffset();
+ } else {
+ reversePosition();
+ }
+ },
+ [onDragRelease, shouldReverse, onRelease, reversePosition],
+ );
+
+ const onPanResponderGrant = React.useCallback(
+ (_: GestureResponderEvent) => {
+ startBounds.current = getBounds();
+ isDragging.current = true;
+ if (!shouldReverse) {
+ pan.current.setOffset(offsetFromStart.current);
+ pan.current.setValue({x: 0, y: 0});
+ }
+ onDragStart();
+ },
+ [getBounds, shouldReverse],
+ );
+
+ const handleOnDrag = React.useCallback(
+ (e: GestureResponderEvent, gestureState: PanResponderGestureState) => {
+ const {dx, dy} = gestureState;
+ const {top, right, left, bottom} = startBounds.current;
+ const far = 999999999;
+ const changeX = clamp(
+ dx,
+ Number.isFinite(minX) ? minX - left : -far,
+ Number.isFinite(maxX) ? maxX - right : far,
+ );
+ const changeY = clamp(
+ dy,
+ Number.isFinite(minY) ? minY - top : -far,
+ Number.isFinite(maxY) ? maxY - bottom : far,
+ );
+ pan.current.setValue({x: changeX, y: changeY});
+ onDrag(e, gestureState);
+ },
+ [maxX, maxY, minX, minY, onDrag],
+ );
+
+ const panResponder = React.useMemo(() => {
+ return PanResponder.create({
+ onMoveShouldSetPanResponder: (_, gestureState) =>
+ shouldStartDrag(gestureState),
+ onMoveShouldSetPanResponderCapture: (_, gestureState) =>
+ shouldStartDrag(gestureState),
+ onPanResponderGrant,
+ // onPanResponderStart,
+ onPanResponderMove: Animated.event([], {
+ // Typed incorrectly https://reactnative.dev/docs/panresponder
+ listener: handleOnDrag,
+ useNativeDriver: false,
+ }),
+ // onPanResponderRelease: (_) => {
+ // // console.log('end');
+ // // setZIndex(1);
+ // },
+ });
+ }, [
+ handleOnDrag,
+ onPanResponderGrant,
+ onPanResponderRelease,
+ shouldStartDrag,
+ ]);
+
+ // TODO Figure out a way to destroy and remove offsetFromStart entirely
+ React.useEffect(() => {
+ const curPan = pan.current; // Using an instance to avoid losing the pointer before the cleanup
+ if (!shouldReverse) {
+ curPan.addListener((c) => (offsetFromStart.current = c));
+ }
+ return () => {
+ // Typed incorrectly
+ curPan.removeAllListeners();
+ };
+ }, [shouldReverse]);
+
+ const positionCss: StyleProp<ViewStyle> = React.useMemo(() => {
+ const Window = Dimensions.get('window');
+
+ return {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ width: Window.width,
+ height: Window.height,
+ elevation: zIndex,
+ zIndex: zIndex,
+ };
+ }, [zIndex]);
+
+ const dragItemCss = React.useMemo(() => {
+ const style: StyleProp<ViewStyle> = {
+ top: y,
+ left: x,
+ elevation: zIndex,
+ zIndex: zIndex,
+ };
+ if (renderColor) {
+ style.backgroundColor = renderColor;
+ }
+ if (isCircle) {
+ style.borderRadius = renderSize;
+ }
+
+ if (children) {
+ return {
+ ...style,
+ alignSelf: 'baseline',
+ };
+ }
+ return {
+ ...style,
+ justifyContent: 'center',
+ width: renderSize,
+ height: renderSize,
+ };
+ }, [children, isCircle, renderColor, renderSize, x, y, zIndex]);
+
+ const touchableContent = React.useMemo(() => {
+ if (children) {
+ return children;
+ } else if (imageSource) {
+ return (
+ <Image
+ style={{width: renderSize, height: renderSize}}
+ source={imageSource}
+ />
+ );
+ } else {
+ return <Text style={styles.text}>{renderText}</Text>;
+ }
+ }, [children, imageSource, renderSize, renderText]);
+
+ const handleOnLayout = React.useCallback((event) => {
+ const {height, width} = event.nativeEvent.layout;
+ childSize.current = {x: width, y: height};
+ }, []);
+
+ // const handlePressOut = React.useCallback(
+ // (event: GestureResponderEvent) => {
+ // onPressOut(event);
+ // if (!isDragging.current) {
+ // onRelease(event, false);
+ // }
+ // },
+ // [onPressOut, onRelease],
+ // );
+
+ const getDebugView = React.useCallback(() => {
+ const {width, height} = Dimensions.get('window');
+ const far = 9999;
+ const constrained = minX || maxX || minY || maxY;
+ if (!constrained) {
+ return null;
+ } // could show other debug info here
+ const left = minX || -far;
+ const right = maxX ? width - maxX : -far;
+ const top = minY || -far;
+ const bottom = maxY ? height - maxY : -far;
+ return (
+ <View
+ pointerEvents="box-none"
+ style={{left, right, top, bottom, ...styles.debugView}}
+ />
+ );
+ }, [maxX, maxY, minX, minY]);
+
+ return (
+ <View pointerEvents="box-none" style={positionCss}>
+ {debug && getDebugView()}
+ <Animated.View
+ pointerEvents="box-none"
+ {...animatedViewProps}
+ {...panResponder.panHandlers}
+ style={pan.current.getLayout()}>
+ <TouchableOpacity
+ {...touchableOpacityProps}
+ onLayout={handleOnLayout}
+ style={dragItemCss}
+ disabled={true}
+ onPress={onShortPressRelease}
+ onLongPress={onLongPress}
+ onPressIn={onPressIn}
+ onPressOut={onPressOut}>
+ {touchableContent}
+ </TouchableOpacity>
+ </Animated.View>
+ </View>
+ );
+}
+
+/***** Default props and types */
+
+Draggable.defaultProps = {
+ renderText: '+',
+ renderSize: 36,
+ shouldReverse: false,
+ disabled: false,
+ debug: false,
+ onDrag: () => {},
+ onShortPressRelease: () => {},
+ onDragRelease: () => {},
+ onLongPress: () => {},
+ onPressIn: () => {},
+ onPressOut: () => {},
+ onRelease: () => {},
+ x: 0,
+ y: 0,
+ z: 1,
+};
+
+const styles = StyleSheet.create({
+ text: {color: '#fff', textAlign: 'center'},
+ debugView: {
+ backgroundColor: '#ff000044',
+ position: 'absolute',
+ borderColor: '#fced0ecc',
+ borderWidth: 4,
+ },
+});
diff --git a/src/components/notifications/Notification.tsx b/src/components/notifications/Notification.tsx
index cb62047a..3f9cc56a 100644
--- a/src/components/notifications/Notification.tsx
+++ b/src/components/notifications/Notification.tsx
@@ -84,7 +84,8 @@ const Notification: React.FC<NotificationProps> = (props) => {
let obj;
if (
notification_type === 'MOM_3+' ||
- notification_type === 'MOM_FRIEND'
+ notification_type === 'MOM_FRIEND' ||
+ notification_type === 'MOM_TAG'
) {
obj = notification_object as MomentType;
url = obj.thumbnail_url;
@@ -185,6 +186,7 @@ const Notification: React.FC<NotificationProps> = (props) => {
break;
case 'MOM_3+':
case 'MOM_FRIEND':
+ case 'MOM_TAG':
const object = notification_object as MomentType;
await fetchUserX(
dispatch,
@@ -236,83 +238,88 @@ const Notification: React.FC<NotificationProps> = (props) => {
style={styles.avatarContainer}>
<Avatar style={styles.avatar} uri={avatar} />
</TouchableWithoutFeedback>
- {notification_type === 'SYSTEM_MSG' ? (
- // Only verbage
+ {notification_type === 'SYSTEM_MSG' || notification_type === 'MOM_TAG' ? (
+ // Single-line body text with timestamp
<View style={styles.contentContainer}>
<View style={styles.textContainerStyles}>
- <Text style={styles.actorName}>{verbage}</Text>
- <Text style={styles.timeStampStyles}>
- {getTimeInShorthand(timestamp)}
- </Text>
- </View>
- </View>
- ) : (
- <>
- {/* Text content: Actor name and verbage*/}
- <View style={styles.contentContainer}>
- <TouchableWithoutFeedback onPress={navigateToProfile}>
- <Text style={styles.actorName}>{notification_title}</Text>
- </TouchableWithoutFeedback>
- <TouchableWithoutFeedback
- style={styles.textContainerStyles}
- onPress={onNotificationTap}>
- <Text style={styles.verbageStyles}>
- {verbage}
+ {notification_type === 'SYSTEM_MSG' ? (
+ <Text style={styles.actorName}>{verbage}</Text>
+ ) : (
+ <Text>
+ <Text style={styles.actorName}>{notification_title} </Text>
+ <Text style={styles.verbageStyles}>{verbage} </Text>
<Text style={styles.timeStampStyles}>
- {' '}
{getTimeInShorthand(timestamp)}
</Text>
</Text>
- {/* <Text style={styles.verbageStyles}>{verbage}</Text> */}
- </TouchableWithoutFeedback>
- </View>
- {/* Friend request accept/decline button */}
- {notification_type === 'FRD_REQ' && (
- <View style={styles.buttonsContainer}>
- <AcceptDeclineButtons
- requester={{id, username, first_name, last_name}}
- onAccept={handleAcceptRequest}
- onReject={handleDeclineFriendRequest}
- />
- </View>
- )}
- {notification_type === 'FRD_ACPT' && (
- <View style={styles.buttonsContainer}>
- <MessageButton
- userXId={id}
- isBlocked={false}
- friendship_status={'friends'}
- externalStyles={{
- container: {
- width: normalize(63),
- height: normalize(21),
- marginTop: '7%',
- },
- buttonTitle: {
- fontSize: normalize(11),
- lineHeight: normalize(13.13),
- letterSpacing: normalize(0.5),
- fontWeight: '700',
- textAlign: 'center',
- },
- }}
- solid
- />
- </View>
- )}
- {/* Moment Image Preview */}
- {(notification_type === 'CMT' ||
- notification_type === 'MOM_3+' ||
- notification_type === 'MOM_FRIEND') &&
- notification_object && (
- <TouchableWithoutFeedback
- style={styles.moment}
- onPress={onNotificationTap}>
- <Image style={styles.imageFlex} source={{uri: momentURI}} />
- </TouchableWithoutFeedback>
)}
- </>
+ </View>
+ </View>
+ ) : (
+ // Two-line title and body text with timestamp
+ <View style={styles.contentContainer}>
+ <TouchableWithoutFeedback onPress={navigateToProfile}>
+ <Text style={styles.actorName}>{notification_title}</Text>
+ </TouchableWithoutFeedback>
+ <TouchableWithoutFeedback
+ style={styles.textContainerStyles}
+ onPress={onNotificationTap}>
+ <Text>
+ <Text style={styles.verbageStyles}>{verbage} </Text>
+ <Text style={styles.timeStampStyles}>
+ {getTimeInShorthand(timestamp)}
+ </Text>
+ </Text>
+ </TouchableWithoutFeedback>
+ </View>
+ )}
+ {/* Friend request accept/decline button */}
+ {notification_type === 'FRD_REQ' && (
+ <View style={styles.buttonsContainer}>
+ <AcceptDeclineButtons
+ requester={{id, username, first_name, last_name}}
+ onAccept={handleAcceptRequest}
+ onReject={handleDeclineFriendRequest}
+ />
+ </View>
+ )}
+ {/* Message button when user accepts friend request */}
+ {notification_type === 'FRD_ACPT' && (
+ <View style={styles.buttonsContainer}>
+ <MessageButton
+ userXId={id}
+ isBlocked={false}
+ friendship_status={'friends'}
+ externalStyles={{
+ container: {
+ width: normalize(63),
+ height: normalize(21),
+ marginTop: '7%',
+ },
+ buttonTitle: {
+ fontSize: normalize(11),
+ lineHeight: normalize(13.13),
+ letterSpacing: normalize(0.5),
+ fontWeight: '700',
+ textAlign: 'center',
+ },
+ }}
+ solid
+ />
+ </View>
)}
+ {/* Moment Image Preview */}
+ {(notification_type === 'CMT' ||
+ notification_type === 'MOM_3+' ||
+ notification_type === 'MOM_TAG' ||
+ notification_type === 'MOM_FRIEND') &&
+ notification_object && (
+ <TouchableWithoutFeedback
+ style={styles.moment}
+ onPress={onNotificationTap}>
+ <Image style={styles.imageFlex} source={{uri: momentURI}} />
+ </TouchableWithoutFeedback>
+ )}
</View>
);
diff --git a/src/components/taggs/TaggDraggable.tsx b/src/components/taggs/TaggDraggable.tsx
new file mode 100644
index 00000000..a6ffb1ef
--- /dev/null
+++ b/src/components/taggs/TaggDraggable.tsx
@@ -0,0 +1,129 @@
+import {useNavigation} from '@react-navigation/native';
+import React, {useEffect, useRef} from 'react';
+import {
+ Image,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ TouchableWithoutFeedback,
+ View,
+} from 'react-native';
+import {useDispatch, useSelector} from 'react-redux';
+import {RootState} from 'src/store/rootReducer';
+import Avatar from '../../components/common/Avatar';
+import {ProfilePreviewType, ScreenType, UserType} from '../../types';
+import {normalize} from '../../utils';
+import {navigateToProfile} from '../../utils/users';
+
+interface TaggDraggableProps {
+ taggedUser: ProfilePreviewType;
+ editingView: boolean;
+ deleteFromList: () => void;
+ setStart: Function;
+}
+
+const TaggDraggable: React.FC<TaggDraggableProps> = (
+ props: TaggDraggableProps,
+) => {
+ const navigation = useNavigation();
+ const dispatch = useDispatch();
+ const state = useSelector((rs: RootState) => rs);
+ const {taggedUser, editingView, deleteFromList, setStart} = props;
+ let uriX = require('../../assets/images/draggableX.png');
+ let uriTip = require('../../assets/images/Tagg-Triangle.png');
+
+ const draggableRef = useRef(null);
+
+ useEffect(() => {
+ draggableRef.current.measure((width: number, height: number) => {
+ setStart([width, height]);
+ });
+ }, []);
+
+ const user: UserType = {
+ userId: taggedUser.id,
+ username: taggedUser.username,
+ };
+
+ return (
+ <TouchableWithoutFeedback>
+ <View style={styles.container}>
+ <Image style={styles.imageTip} source={uriTip} />
+ <TouchableOpacity
+ onPressIn={() =>
+ navigateToProfile(
+ state,
+ dispatch,
+ navigation,
+ ScreenType.Profile,
+ user,
+ )
+ }
+ disabled={editingView}
+ style={styles.content}
+ ref={draggableRef}>
+ <Avatar style={styles.avatar} uri={taggedUser.thumbnail_url} />
+ <Text style={editingView ? styles.buttonTitle : styles.buttonTitleX}>
+ @{taggedUser.username}
+ </Text>
+ {editingView && (
+ <TouchableOpacity
+ disabled={false}
+ onPressIn={deleteFromList}
+ style={styles.imageX}>
+ <Image style={styles.imageX} source={uriX} />
+ </TouchableOpacity>
+ )}
+ </TouchableOpacity>
+ </View>
+ </TouchableWithoutFeedback>
+ );
+};
+
+const styles = StyleSheet.create({
+ imageTip: {
+ height: normalize(12),
+ aspectRatio: 12 / 8,
+ },
+ container: {
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ buttonTitle: {
+ color: 'white',
+ fontSize: normalize(11),
+ fontWeight: '700',
+ lineHeight: normalize(13.13),
+ letterSpacing: normalize(0.6),
+ paddingHorizontal: '1%',
+ },
+ buttonTitleX: {
+ color: 'white',
+ fontSize: normalize(11),
+ fontWeight: '700',
+ lineHeight: normalize(13.13),
+ letterSpacing: normalize(0.6),
+ paddingHorizontal: '1%',
+ },
+ avatar: {
+ height: normalize(20),
+ width: normalize(20),
+ borderRadius: normalize(20) / 2,
+ },
+ imageX: {
+ width: normalize(15),
+ height: normalize(15),
+ },
+ content: {
+ justifyContent: 'space-evenly',
+ alignItems: 'center',
+ flexDirection: 'row',
+ borderRadius: 20,
+ paddingVertical: normalize(5),
+ paddingHorizontal: normalize(8),
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
+ },
+});
+
+export default TaggDraggable;
diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx
index a41abba6..8e972e07 100644
--- a/src/screens/profile/CaptionScreen.tsx
+++ b/src/screens/profile/CaptionScreen.tsx
@@ -1,6 +1,6 @@
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React, {Fragment, useState} from 'react';
+import React, {Fragment, useEffect, useRef, useState} from 'react';
import {
Alert,
Image,
@@ -16,7 +16,9 @@ import {Button} from 'react-native-elements';
import {useDispatch, useSelector} from 'react-redux';
import {SearchBackground} from '../../components';
import {CaptionScreenHeader} from '../../components/';
+import Draggable from '../../components/common/Draggable';
import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator';
+import TaggDraggable from '../../components/taggs/TaggDraggable';
import {TAGG_LIGHT_BLUE_2} from '../../constants';
import {ERROR_UPLOAD, SUCCESS_PIC_UPLOAD} from '../../constants/strings';
import {MainStackParams} from '../../routes';
@@ -50,6 +52,39 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
const dispatch = useDispatch();
const [caption, setCaption] = useState('');
const [loading, setLoading] = useState(false);
+ const imageRef = useRef(null);
+
+ // state variables used to position draggables
+ const [offset, setOffset] = useState([0, 0]);
+ const [curStart, setCurStart] = useState([0, 0]);
+ const [imageDimensions, setImageDimensions] = useState([0, 0]);
+
+ const [taggList, setTaggList] = useState([
+ {
+ first_name: 'Ivan',
+ id: 'cee45bf8-7f3d-43c8-99bb-ec04908efe58',
+ last_name: 'Chen',
+ thumbnail_url:
+ 'https://tagg-dev.s3.us-east-2.amazonaws.com/thumbnails/smallProfilePicture/spp-cee45bf8-7f3d-43c8-99bb-ec04908efe58-thumbnail.jpg',
+ username: 'ivan.tagg',
+ },
+ {
+ first_name: 'Ankit',
+ id: '3bcf6947-bee6-46b0-ad02-6f4d25eaeac3',
+ last_name: 'Thanekar',
+ thumbnail_url:
+ 'https://tagg-dev.s3.us-east-2.amazonaws.com/thumbnails/smallProfilePicture/spp-3bcf6947-bee6-46b0-ad02-6f4d25eaeac3-thumbnail.jpg',
+ username: 'ankit.thanekar',
+ },
+ {
+ first_name: 'Ankit',
+ id: '3bcf6947-bee6-46b0-ad02-6f4d25eaeac3',
+ last_name: 'Thanekar',
+ thumbnail_url:
+ 'https://tagg-dev.s3.us-east-2.amazonaws.com/thumbnails/smallProfilePicture/spp-3bcf6947-bee6-46b0-ad02-6f4d25eaeac3-thumbnail.jpg',
+ username: 'ankit.thanekar',
+ },
+ ]);
const navigateToProfile = () => {
//Since the logged In User is navigating to own profile, useXId is not required
@@ -59,6 +94,16 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
});
};
+ /**
+ * need a handler to take care of creating a tagged user object, append that object to the taggList state variable.
+ */
+ useEffect(() => {
+ imageRef.current.measure((fx, fy, width, height, px, py) => {
+ setOffset([px, py]);
+ setImageDimensions([width, height]);
+ });
+ }, []);
+
const handleShare = async () => {
setLoading(true);
if (!image.filename) {
@@ -105,7 +150,9 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
/>
</View>
<CaptionScreenHeader style={styles.header} {...{title: title}} />
+ {/* this is the image we want to center our tags' initial location within */}
<Image
+ ref={imageRef}
style={styles.image}
source={{uri: image.path}}
resizeMode={'cover'}
@@ -118,6 +165,25 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
onChange={setCaption}
partTypes={mentionPartTypes('blue')}
/>
+ {taggList.map((user) => (
+ <Draggable
+ x={imageDimensions[0] / 2 - curStart[0] / 2 + offset[0]}
+ y={offset[1] + imageDimensions[1] / 2 - curStart[1] / 2}
+ minX={offset[0]}
+ minY={offset[1]}
+ maxX={imageDimensions[0] + offset[0]}
+ maxY={imageDimensions[1] + offset[1]}
+ onDragStart={() => null}>
+ <TaggDraggable
+ taggedUser={user}
+ editingView={true}
+ deleteFromList={() =>
+ setTaggList(taggList.filter((u) => u.id !== user.id))
+ }
+ setStart={setCurStart}
+ />
+ </Draggable>
+ ))}
</View>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
diff --git a/src/types/types.ts b/src/types/types.ts
index e9975529..e1935d26 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -240,6 +240,8 @@ export type TypeOfNotification =
| 'MOM_FRIEND'
// notification_object is undefined
| 'INVT_ONBRD'
+ // notification_object is MomentType
+ | 'MOM_TAG'
// notification_object is undefined
| 'SYSTEM_MSG';