aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-05-21 20:34:30 -0400
committerIvan Chen <ivan@tagg.id>2021-05-21 20:34:30 -0400
commit442f5608aeddb5c627183e150a8c79c9d5bd2a57 (patch)
tree4346360538d0a50407ce7d76a5e8ce6b168c52aa /src/components
parentb4a4639f2ed05c02b9061d9febddf8339bc1fe26 (diff)
parent4849c65ff2163e1a77dcb26a12ff68840df225e7 (diff)
Merge branch 'master' into tma853-tag-selection-screen
# Conflicts: # src/components/common/index.ts # src/screens/profile/CaptionScreen.tsx
Diffstat (limited to 'src/components')
-rw-r--r--src/components/common/Draggable.tsx364
-rw-r--r--src/components/common/MomentTags.tsx77
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/moments/MomentPostContent.tsx27
-rw-r--r--src/components/notifications/Notification.tsx147
-rw-r--r--src/components/taggs/TaggDraggable.tsx129
6 files changed, 669 insertions, 76 deletions
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/common/MomentTags.tsx b/src/components/common/MomentTags.tsx
new file mode 100644
index 00000000..fb9ef5be
--- /dev/null
+++ b/src/components/common/MomentTags.tsx
@@ -0,0 +1,77 @@
+import React, {MutableRefObject, useEffect, useState} from 'react';
+import {MomentTagType, ProfilePreviewType} from '../../types';
+import TaggDraggable from '../taggs/TaggDraggable';
+import Draggable from './Draggable';
+
+interface MomentTagsProps {
+ editing: boolean;
+ tags: MomentTagType[];
+ imageRef: MutableRefObject<null>;
+ deleteFromList?: (user: ProfilePreviewType) => void;
+}
+
+const MomentTags: React.FC<MomentTagsProps> = ({
+ editing,
+ tags,
+ imageRef,
+ deleteFromList,
+}) => {
+ const [offset, setOffset] = useState([0, 0]);
+ const [curStart, setCurStart] = useState([0, 0]);
+ const [imageDimensions, setImageDimensions] = useState([0, 0]);
+
+ useEffect(() => {
+ imageRef.current.measure((fx, fy, width, height, px, py) => {
+ setOffset([px, py]);
+ setImageDimensions([width, height]);
+ });
+ }, []);
+
+ if (!tags) {
+ return null;
+ }
+
+ return editing && deleteFromList ? (
+ <>
+ {tags.map((tag) => (
+ <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={tag.user}
+ editingView={true}
+ deleteFromList={() => deleteFromList(tag.user)}
+ setStart={setCurStart}
+ />
+ </Draggable>
+ ))}
+ </>
+ ) : (
+ <>
+ {tags.map((tag) => (
+ <Draggable
+ x={imageDimensions[0] / 2 - curStart[0] / 2 + tag.x}
+ y={imageDimensions[0] / 2 - curStart[0] / 2 + tag.y}
+ minX={offset[0]}
+ minY={offset[1]}
+ maxX={imageDimensions[0] + offset[0]}
+ maxY={imageDimensions[1] + offset[1]}
+ onDragStart={() => null}>
+ <TaggDraggable
+ taggedUser={tag.user}
+ editingView={editing}
+ deleteFromList={() => null}
+ setStart={setCurStart}
+ />
+ </Draggable>
+ ))}
+ </>
+ );
+};
+
+export default MomentTags;
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
index 44edbe5f..4f5c0232 100644
--- a/src/components/common/index.ts
+++ b/src/components/common/index.ts
@@ -28,3 +28,4 @@ export {default as TaggTypeahead} from './TaggTypeahead';
export {default as TaggUserRowCell} from './TaggUserRowCell';
export {default as LikeButton} from './LikeButton';
export {default as TaggUserSelectionCell} from './TaggUserSelectionCell';
+export {default as MomentTags} from './MomentTags';
diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx
index 193bf40c..e702cb68 100644
--- a/src/components/moments/MomentPostContent.tsx
+++ b/src/components/moments/MomentPostContent.tsx
@@ -1,19 +1,20 @@
import {useNavigation} from '@react-navigation/native';
-import React, {useEffect} from 'react';
+import React, {useEffect, useRef, useState} from 'react';
import {Image, StyleSheet, Text, View, ViewProps} from 'react-native';
import {useDispatch, useStore} from 'react-redux';
-import {getCommentsCount} from '../../services';
+import {getCommentsCount, loadMomentTags} from '../../services';
import {RootState} from '../../store/rootReducer';
-import {ScreenType, UserType} from '../../types';
+import {MomentTagType, ScreenType, UserType} from '../../types';
import {
getTimePosted,
navigateToProfile,
+ normalize,
SCREEN_HEIGHT,
SCREEN_WIDTH,
- normalize,
} from '../../utils';
import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments';
import {CommentsCount} from '../comments';
+import {MomentTags} from '../common';
interface MomentPostContentProps extends ViewProps {
screenType: ScreenType;
@@ -30,11 +31,23 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
dateTime,
style,
}) => {
- const [elapsedTime, setElapsedTime] = React.useState<string>();
- const [comments_count, setCommentsCount] = React.useState('');
+ const [elapsedTime, setElapsedTime] = useState('');
+ const [comments_count, setCommentsCount] = useState('');
+ const [tags, setTags] = useState<MomentTagType[]>([]);
const state: RootState = useStore().getState();
const navigation = useNavigation();
const dispatch = useDispatch();
+ const imageRef = useRef(null);
+
+ useEffect(() => {
+ const loadTags = async () => {
+ const response = await loadMomentTags(momentId);
+ if (response) {
+ setTags(response);
+ }
+ };
+ loadTags();
+ }, []);
useEffect(() => {
const fetchCommentsCount = async () => {
@@ -48,10 +61,12 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
return (
<View style={[styles.container, style]}>
<Image
+ ref={imageRef}
style={styles.image}
source={{uri: pathHash}}
resizeMode={'cover'}
/>
+ <MomentTags editing={false} tags={tags} imageRef={imageRef} />
<View style={styles.footerContainer}>
<CommentsCount
commentsCount={comments_count}
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..e4448642
--- /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 Avatar from '../../components/common/Avatar';
+import {RootState} from '../../store/rootReducer';
+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;