diff --git a/lib/hooks/inline-engagement-text.react.js b/lib/shared/inline-engagement-utils.js
similarity index 66%
rename from lib/hooks/inline-engagement-text.react.js
rename to lib/shared/inline-engagement-utils.js
index d052d2bd5..67b2cc686 100644
--- a/lib/hooks/inline-engagement-text.react.js
+++ b/lib/shared/inline-engagement-utils.js
@@ -1,13 +1,13 @@
// @flow
import type { ThreadInfo } from '../types/thread-types.js';
-function useInlineEngagementText(threadInfo: ?ThreadInfo): string {
+function getInlineEngagementSidebarText(threadInfo: ?ThreadInfo): string {
if (!threadInfo) {
return '';
}
const repliesCount = threadInfo.repliesCount || 1;
return `${repliesCount} ${repliesCount > 1 ? 'replies' : 'reply'}`;
}
-export default useInlineEngagementText;
+export { getInlineEngagementSidebarText };
diff --git a/native/chat/inline-engagement.react.js b/native/chat/inline-engagement.react.js
index 05092f568..f2f90d076 100644
--- a/native/chat/inline-engagement.react.js
+++ b/native/chat/inline-engagement.react.js
@@ -1,422 +1,422 @@
// @flow
import { useNavigation } from '@react-navigation/native';
import invariant from 'invariant';
import * as React from 'react';
import { Text, View } from 'react-native';
import Animated, {
Extrapolate,
interpolateNode,
} from 'react-native-reanimated';
-import useInlineEngagementText from 'lib/hooks/inline-engagement-text.react.js';
import type { ReactionInfo } from 'lib/selectors/chat-selectors.js';
+import { getInlineEngagementSidebarText } from 'lib/shared/inline-engagement-utils.js';
import { localIDPrefix } from 'lib/shared/message-utils.js';
import type { MessageInfo } from 'lib/types/message-types.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import {
inlineEngagementLabelStyle,
inlineEngagementStyle,
inlineEngagementCenterStyle,
inlineEngagementRightStyle,
composedMessageStyle,
avatarOffset,
} from './chat-constants.js';
import { useNavigateToThread } from './message-list-types.js';
import { useSendReaction } from './reaction-message-utils.js';
import CommIcon from '../components/comm-icon.react.js';
import GestureTouchableOpacity from '../components/gesture-touchable-opacity.react.js';
import { MessageReactionsModalRouteName } from '../navigation/route-names.js';
import { useSelector } from '../redux/redux-utils.js';
import { useStyles } from '../themes/colors.js';
import type { ChatMessageInfoItemWithHeight } from '../types/chat-types.js';
type Props = {
+messageInfo: MessageInfo,
+threadInfo: ThreadInfo,
+sidebarThreadInfo: ?ThreadInfo,
+reactions: ReactionInfo,
+disabled?: boolean,
+positioning?: 'left' | 'right' | 'center',
+label?: ?string,
};
function InlineEngagement(props: Props): React.Node {
const {
messageInfo,
threadInfo,
sidebarThreadInfo,
reactions,
disabled = false,
positioning,
label,
} = props;
const isLeft = positioning === 'left';
const isRight = positioning === 'right';
const isCenter = positioning === 'center';
const navigateToThread = useNavigateToThread();
const { navigate } = useNavigation();
const styles = useStyles(unboundStyles);
const editedLabel = React.useMemo(() => {
if (!label) {
return null;
}
const labelLeftRight = isLeft
? styles.messageLabelLeft
: styles.messageLabelRight;
return (
{label}
);
}, [isLeft, label, styles]);
const unreadStyle = sidebarThreadInfo?.currentUser.unread
? styles.unread
: null;
const repliesStyles = React.useMemo(
() => [styles.repliesText, unreadStyle],
[styles.repliesText, unreadStyle],
);
const onPressSidebar = React.useCallback(() => {
if (sidebarThreadInfo && !disabled) {
navigateToThread({ threadInfo: sidebarThreadInfo });
}
}, [disabled, navigateToThread, sidebarThreadInfo]);
- const repliesText = useInlineEngagementText(sidebarThreadInfo);
+ const repliesText = getInlineEngagementSidebarText(sidebarThreadInfo);
const sidebarStyle = React.useMemo(() => {
const stylesResult = [styles.sidebar];
if (Object.keys(reactions).length === 0) {
return stylesResult;
}
if (isRight) {
stylesResult.push(styles.sidebarMarginLeft);
} else {
stylesResult.push(styles.sidebarMarginRight);
}
return stylesResult;
}, [
isRight,
reactions,
styles.sidebar,
styles.sidebarMarginLeft,
styles.sidebarMarginRight,
]);
const sidebarItem = React.useMemo(() => {
if (!sidebarThreadInfo) {
return null;
}
return (
{repliesText}
);
}, [
sidebarThreadInfo,
onPressSidebar,
sidebarStyle,
styles.icon,
repliesStyles,
repliesText,
]);
const nextLocalID = useSelector(state => state.nextLocalID);
const localID = `${localIDPrefix}${nextLocalID}`;
const sendReaction = useSendReaction(
messageInfo.id,
localID,
threadInfo.id,
reactions,
);
const onPressReaction = React.useCallback(
(reaction: string) => sendReaction(reaction),
[sendReaction],
);
const onLongPressReaction = React.useCallback(() => {
navigate<'MessageReactionsModal'>({
name: MessageReactionsModalRouteName,
params: { reactions },
});
}, [navigate, reactions]);
const reactionStyle = React.useMemo(() => {
const stylesResult = [styles.reactionsContainer];
if (isRight) {
stylesResult.push(styles.reactionsContainerMarginLeft);
} else {
stylesResult.push(styles.reactionsContainerMarginRight);
}
return stylesResult;
}, [
isRight,
styles.reactionsContainer,
styles.reactionsContainerMarginLeft,
styles.reactionsContainerMarginRight,
]);
const reactionList = React.useMemo(() => {
if (Object.keys(reactions).length === 0) {
return null;
}
return Object.keys(reactions).map(reaction => {
const reactionInfo = reactions[reaction];
const numOfReacts = reactionInfo.users.length;
const style = reactionInfo.viewerReacted
? [...reactionStyle, styles.reactionsContainerSelected]
: reactionStyle;
return (
onPressReaction(reaction)}
onLongPress={onLongPressReaction}
activeOpacity={0.7}
key={reaction}
>
{`${reaction} ${numOfReacts}`}
);
});
}, [
onLongPressReaction,
onPressReaction,
reactionStyle,
reactions,
styles.reaction,
styles.reactionsContainerSelected,
]);
const inlineEngagementPositionStyle = React.useMemo(() => {
const styleResult = [styles.inlineEngagement];
if (isRight) {
styleResult.push(styles.rightInlineEngagement);
} else if (isCenter) {
styleResult.push(styles.centerInlineEngagement);
}
return styleResult;
}, [
isCenter,
isRight,
styles.centerInlineEngagement,
styles.inlineEngagement,
styles.rightInlineEngagement,
]);
return (
{editedLabel}
{sidebarItem}
{reactionList}
);
}
const unboundStyles = {
inlineEngagement: {
flexDirection: 'row',
marginBottom: inlineEngagementStyle.marginBottom,
marginLeft: avatarOffset,
flexWrap: 'wrap',
top: inlineEngagementStyle.topOffset,
},
centerInlineEngagement: {
marginLeft: 20,
marginRight: 20,
justifyContent: 'center',
},
rightInlineEngagement: {
flexDirection: 'row-reverse',
marginLeft: inlineEngagementRightStyle.marginLeft,
},
sidebar: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'inlineEngagementBackground',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 8,
marginTop: inlineEngagementStyle.marginTop,
},
sidebarMarginLeft: {
marginLeft: 4,
},
sidebarMarginRight: {
marginRight: 4,
},
icon: {
color: 'inlineEngagementLabel',
marginRight: 4,
},
repliesText: {
color: 'inlineEngagementLabel',
fontSize: 14,
lineHeight: 22,
},
unread: {
color: 'listForegroundLabel',
fontWeight: 'bold',
},
reactionsContainer: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'inlineEngagementBackground',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 8,
marginTop: inlineEngagementStyle.marginTop,
},
reactionsContainerSelected: {
borderWidth: 1,
borderColor: 'inlineEngagementLabel',
paddingHorizontal: 7,
paddingVertical: 3,
},
reactionsContainerMarginLeft: {
marginLeft: 4,
},
reactionsContainerMarginRight: {
marginRight: 4,
},
reaction: {
color: 'inlineEngagementLabel',
fontSize: 14,
lineHeight: 22,
},
messageLabel: {
color: 'messageLabel',
paddingHorizontal: 3,
fontSize: 13,
top: inlineEngagementLabelStyle.topOffset,
height: inlineEngagementLabelStyle.height,
marginTop: inlineEngagementStyle.marginTop,
},
messageLabelLeft: {
marginLeft: 9,
marginRight: 4,
},
messageLabelRight: {
marginRight: 10,
marginLeft: 4,
},
avatarOffset: {
width: avatarOffset,
},
};
type TooltipInlineEngagementProps = {
+item: ChatMessageInfoItemWithHeight,
+isOpeningSidebar: boolean,
+progress: Animated.Node,
+windowWidth: number,
+positioning: 'left' | 'right' | 'center',
+initialCoordinates: {
+x: number,
+y: number,
+width: number,
+height: number,
},
};
function TooltipInlineEngagement(
props: TooltipInlineEngagementProps,
): React.Node {
const {
item,
isOpeningSidebar,
progress,
windowWidth,
initialCoordinates,
positioning,
} = props;
// ESLint doesn't recognize that invariant always throws
// eslint-disable-next-line consistent-return
const inlineEngagementStyles = React.useMemo(() => {
if (positioning === 'left') {
return {
position: 'absolute',
top: inlineEngagementStyle.marginTop + inlineEngagementStyle.topOffset,
left: composedMessageStyle.marginLeft,
};
} else if (positioning === 'right') {
return {
position: 'absolute',
right:
inlineEngagementRightStyle.marginLeft +
composedMessageStyle.marginRight,
top: inlineEngagementStyle.marginTop + inlineEngagementStyle.topOffset,
};
} else if (positioning === 'center') {
return {
alignSelf: 'center',
top: inlineEngagementCenterStyle.topOffset,
};
}
invariant(
false,
`${positioning} is not a valid positioning value for InlineEngagement`,
);
}, [positioning]);
const inlineEngagementContainer = React.useMemo(() => {
const opacity = isOpeningSidebar
? 0
: interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [1, 0],
extrapolate: Extrapolate.CLAMP,
});
return {
position: 'absolute',
width: windowWidth,
top: initialCoordinates.height,
left: -initialCoordinates.x,
opacity,
};
}, [
initialCoordinates.height,
initialCoordinates.x,
isOpeningSidebar,
progress,
windowWidth,
]);
return (
);
}
export { InlineEngagement, TooltipInlineEngagement };
diff --git a/web/chat/inline-engagement.react.js b/web/chat/inline-engagement.react.js
index 9f20ebb58..c56bc8986 100644
--- a/web/chat/inline-engagement.react.js
+++ b/web/chat/inline-engagement.react.js
@@ -1,138 +1,138 @@
// @flow
import classNames from 'classnames';
import * as React from 'react';
import { useModalContext } from 'lib/components/modal-provider.react.js';
-import useInlineEngagementText from 'lib/hooks/inline-engagement-text.react.js';
import type { ReactionInfo } from 'lib/selectors/chat-selectors.js';
+import { getInlineEngagementSidebarText } from 'lib/shared/inline-engagement-utils.js';
import { stringForReactionList } from 'lib/shared/reaction-utils.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import css from './inline-engagement.css';
import CommIcon from '../CommIcon.react.js';
import MessageReactionsModal from '../modals/chat/message-reactions-modal.react.js';
import { useOnClickThread } from '../selectors/thread-selectors.js';
type Props = {
+threadInfo: ?ThreadInfo,
+reactions?: ReactionInfo,
+positioning: 'left' | 'center' | 'right',
+label?: ?string,
};
function InlineEngagement(props: Props): React.Node {
const { threadInfo, reactions, positioning, label } = props;
const { pushModal, popModal } = useModalContext();
- const repliesText = useInlineEngagementText(threadInfo);
+ const repliesText = getInlineEngagementSidebarText(threadInfo);
const containerClasses = classNames([
css.inlineEngagementContainer,
{
[css.leftContainer]: positioning === 'left',
[css.centerContainer]: positioning === 'center',
[css.rightContainer]: positioning === 'right',
},
]);
const reactionsExist = reactions && Object.keys(reactions).length > 0;
const threadsContainerClasses = classNames({
[css.threadsContainer]: threadInfo && !reactionsExist,
[css.threadsSplitContainer]: threadInfo && reactionsExist,
});
const reactionsContainerClasses = classNames({
[css.reactionsContainer]: reactionsExist && !threadInfo,
[css.reactionsSplitContainer]: reactionsExist && threadInfo,
});
const onClickThreadInner = useOnClickThread(threadInfo);
const onClickThread = React.useCallback(
(event: SyntheticEvent) => {
popModal();
onClickThreadInner(event);
},
[popModal, onClickThreadInner],
);
const sidebarItem = React.useMemo(() => {
if (!threadInfo || !repliesText) {
return null;
}
return (
{repliesText}
);
}, [threadInfo, repliesText, onClickThread, threadsContainerClasses]);
const onClickReactions = React.useCallback(
(event: SyntheticEvent) => {
event.preventDefault();
if (!reactions) {
return;
}
pushModal(
,
);
},
[popModal, pushModal, reactions],
);
const reactionsList = React.useMemo(() => {
if (!reactions || Object.keys(reactions).length === 0) {
return null;
}
const reactionText = stringForReactionList(reactions);
return (
{reactionText}
);
}, [reactions, onClickReactions, reactionsContainerClasses]);
const isLeft = positioning === 'left';
const labelClasses = classNames({
[css.messageLabel]: true,
[css.messageLabelLeft]: isLeft,
[css.messageLabelRight]: !isLeft,
[css.onlyMessageLabel]: !sidebarItem && !reactionsList,
});
const messageLabel = React.useMemo(() => {
if (!label) {
return null;
}
return (
{label}
);
}, [label, labelClasses]);
let body;
if (isLeft) {
body = (
<>
{messageLabel}
{sidebarItem}
{reactionsList}
>
);
} else {
body = (
<>
{sidebarItem}
{reactionsList}
{messageLabel}
>
);
}
return {body}
;
}
export default InlineEngagement;