diff --git a/native/chat/message-result.react.js b/native/chat/message-result.react.js
index 2492d50e8..4932db7f0 100644
--- a/native/chat/message-result.react.js
+++ b/native/chat/message-result.react.js
@@ -1,76 +1,82 @@
// @flow
import * as React from 'react';
import { Text, View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { type ThreadInfo } from 'lib/types/thread-types.js';
import { longAbsoluteDate } from 'lib/utils/date-utils.js';
import type { ChatNavigationProp } from './chat.react';
import { MessageListContextProvider } from './message-list-types.js';
import { Message } from './message.react.js';
+import { modifyItemForResultScreen } from './utils.js';
import type { AppNavigationProp } from '../navigation/app-navigator.react';
import type { NavigationRoute } from '../navigation/route-names';
import { useStyles } from '../themes/colors.js';
import type { ChatMessageInfoItemWithHeight } from '../types/chat-types.js';
import type { VerticalBounds } from '../types/layout-types.js';
type MessageResultProps = {
+item: ChatMessageInfoItemWithHeight,
+threadInfo: ThreadInfo,
+navigation:
| AppNavigationProp<'TogglePinModal'>
| ChatNavigationProp<'MessageResultsScreen'>,
+route:
| NavigationRoute<'TogglePinModal'>
| NavigationRoute<'MessageResultsScreen'>,
+messageVerticalBounds: ?VerticalBounds,
};
function MessageResult(props: MessageResultProps): React.Node {
const styles = useStyles(unboundStyles);
const onToggleFocus = React.useCallback(() => {}, []);
+ const item = React.useMemo(
+ () => modifyItemForResultScreen(props.item),
+ [props.item],
+ );
+
return (
{longAbsoluteDate(props.item.messageInfo.time)}
);
}
const unboundStyles = {
container: {
marginTop: 5,
backgroundColor: 'panelForeground',
overflow: 'scroll',
maxHeight: 400,
},
viewContainer: {
marginTop: 10,
marginBottom: 10,
},
messageDate: {
color: 'messageLabel',
fontSize: 12,
marginLeft: 55,
},
};
export default MessageResult;
diff --git a/native/chat/message-results-screen.react.js b/native/chat/message-results-screen.react.js
index b519cc957..710e63a9e 100644
--- a/native/chat/message-results-screen.react.js
+++ b/native/chat/message-results-screen.react.js
@@ -1,200 +1,162 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import { View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { fetchPinnedMessages } from 'lib/actions/message-actions.js';
import { messageListData } from 'lib/selectors/chat-selectors.js';
import { createMessageInfo } from 'lib/shared/message-utils.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import { useServerCall } from 'lib/utils/action-utils.js';
import { useHeightMeasurer } from './chat-context.js';
import type { ChatNavigationProp } from './chat.react';
import MessageResult from './message-result.react.js';
import type { NavigationRoute } from '../navigation/route-names';
import { useSelector } from '../redux/redux-utils.js';
import type { ChatMessageItemWithHeight } from '../types/chat-types.js';
export type MessageResultsScreenParams = {
+threadInfo: ThreadInfo,
};
type MessageResultsScreenProps = {
+navigation: ChatNavigationProp<'MessageResultsScreen'>,
+route: NavigationRoute<'MessageResultsScreen'>,
};
function MessageResultsScreen(props: MessageResultsScreenProps): React.Node {
const { navigation, route } = props;
const { threadInfo } = route.params;
const { id: threadID } = threadInfo;
const [rawMessageResults, setRawMessageResults] = React.useState([]);
const measureMessages = useHeightMeasurer();
const [measuredMessages, setMeasuredMessages] = React.useState([]);
const [messageVerticalBounds, setMessageVerticalBounds] = React.useState();
const scrollViewContainerRef = React.useRef();
const callFetchPinnedMessages = useServerCall(fetchPinnedMessages);
const userInfos = useSelector(state => state.userStore.userInfos);
React.useEffect(() => {
(async () => {
const result = await callFetchPinnedMessages({ threadID });
setRawMessageResults(result.pinnedMessages);
})();
}, [callFetchPinnedMessages, threadID]);
const translatedMessageResults = React.useMemo(() => {
const threadInfos = { [threadID]: threadInfo };
return rawMessageResults
.map(messageInfo =>
createMessageInfo(messageInfo, null, userInfos, threadInfos),
)
.filter(Boolean);
}, [rawMessageResults, userInfos, threadID, threadInfo]);
const chatMessageInfos = useSelector(
messageListData(threadInfo.id, translatedMessageResults),
);
const sortedUniqueChatMessageInfoItems = React.useMemo(() => {
if (!chatMessageInfos) {
return [];
}
const chatMessageInfoItems = chatMessageInfos.filter(
item => item.itemType === 'message' && item.isPinned,
);
// By the nature of using messageListData and passing in
// the desired translatedMessageResults as additional
// messages, we will have duplicate ChatMessageInfoItems.
const uniqueChatMessageInfoItemsMap = new Map();
chatMessageInfoItems.forEach(
item =>
item.messageInfo &&
item.messageInfo.id &&
uniqueChatMessageInfoItemsMap.set(item.messageInfo.id, item),
);
// Push the items in the order they appear in the rawMessageResults
// since the messages fetched from the server are already sorted
// in the order of pin_time (newest first).
const sortedChatMessageInfoItems = [];
for (let i = 0; i < rawMessageResults.length; i++) {
sortedChatMessageInfoItems.push(
uniqueChatMessageInfoItemsMap.get(rawMessageResults[i].id),
);
}
return sortedChatMessageInfoItems.filter(Boolean);
}, [chatMessageInfos, rawMessageResults]);
const measureCallback = React.useCallback(
(listDataWithHeights: $ReadOnlyArray) => {
setMeasuredMessages(listDataWithHeights);
},
[],
);
React.useEffect(() => {
measureMessages(
sortedUniqueChatMessageInfoItems,
threadInfo,
measureCallback,
);
}, [
measureCallback,
measureMessages,
sortedUniqueChatMessageInfoItems,
threadInfo,
]);
- const modifiedItems = React.useMemo(
- () =>
- measuredMessages.map(item => {
- invariant(item.itemType !== 'loader', 'should not be loader');
- invariant(
- item.messageShapeType !== 'robotext',
- 'should not be robotext',
- );
-
- if (item.messageShapeType === 'multimedia') {
- return {
- ...item,
- startsConversation: false,
- startsCluster: true,
- endsCluster: true,
- messageInfo: {
- ...item.messageInfo,
- creator: {
- ...item.messageInfo.creator,
- isViewer: false,
- },
- },
- };
- }
-
- return {
- ...item,
- startsConversation: false,
- startsCluster: true,
- endsCluster: true,
- messageInfo: {
- ...item.messageInfo,
- creator: {
- ...item.messageInfo.creator,
- isViewer: false,
- },
- },
- };
- }),
- [measuredMessages],
- );
-
const onLayout = React.useCallback(() => {
scrollViewContainerRef.current?.measure(
(x, y, width, height, pageX, pageY) => {
if (
height === null ||
height === undefined ||
pageY === null ||
pageY === undefined
) {
return;
}
setMessageVerticalBounds({ height, y: pageY });
},
);
}, []);
const messageResultsToDisplay = React.useMemo(
() =>
- modifiedItems.map(item => (
-
- )),
- [modifiedItems, threadInfo, navigation, route, messageVerticalBounds],
+ measuredMessages.map(item => {
+ invariant(item.itemType !== 'loader', 'should not be loader');
+
+ return (
+
+ );
+ }),
+ [measuredMessages, threadInfo, navigation, route, messageVerticalBounds],
);
return (
{messageResultsToDisplay}
);
}
export default MessageResultsScreen;
diff --git a/native/chat/toggle-pin-modal.react.js b/native/chat/toggle-pin-modal.react.js
index de85a06c9..dd6908e80 100644
--- a/native/chat/toggle-pin-modal.react.js
+++ b/native/chat/toggle-pin-modal.react.js
@@ -1,204 +1,165 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import { Text, View } from 'react-native';
import {
toggleMessagePin,
toggleMessagePinActionTypes,
} from 'lib/actions/thread-actions.js';
import { type ThreadInfo } from 'lib/types/thread-types.js';
import {
useServerCall,
useDispatchActionPromise,
} from 'lib/utils/action-utils.js';
import MessageResult from './message-result.react.js';
import Button from '../components/button.react.js';
import Modal from '../components/modal.react.js';
import type { AppNavigationProp } from '../navigation/app-navigator.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
import { useStyles } from '../themes/colors.js';
import type { ChatMessageInfoItemWithHeight } from '../types/chat-types';
export type TogglePinModalParams = {
+item: ChatMessageInfoItemWithHeight,
+threadInfo: ThreadInfo,
};
type TogglePinModalProps = {
+navigation: AppNavigationProp<'TogglePinModal'>,
+route: NavigationRoute<'TogglePinModal'>,
};
function TogglePinModal(props: TogglePinModalProps): React.Node {
const { navigation, route } = props;
const { item, threadInfo } = route.params;
const { messageInfo, isPinned } = item;
const styles = useStyles(unboundStyles);
const callToggleMessagePin = useServerCall(toggleMessagePin);
const dispatchActionPromise = useDispatchActionPromise();
const modalInfo = React.useMemo(() => {
if (isPinned) {
return {
name: 'Remove Pinned Message',
action: 'unpin',
confirmationText:
'Are you sure you want to remove this pinned message?',
buttonText: 'Remove Pinned Message',
buttonStyle: styles.removePinButton,
};
}
return {
name: 'Pin Message',
action: 'pin',
confirmationText:
'You may pin this message to the channel you are ' +
'currently viewing. To unpin a message, select the pinned messages ' +
'icon in the channel.',
buttonText: 'Pin Message',
buttonStyle: styles.pinButton,
};
}, [isPinned, styles.pinButton, styles.removePinButton]);
- const modifiedItem = React.useMemo(() => {
- // The if / else if / else conditional is for Flow
- if (item.messageShapeType === 'robotext') {
- return item;
- } else if (item.messageShapeType === 'multimedia') {
- return {
- ...item,
- threadCreatedFromMessage: undefined,
- reactions: {},
- startsConversation: false,
- startsCluster: true,
- endsCluster: true,
- messageInfo: {
- ...item.messageInfo,
- creator: {
- ...item.messageInfo.creator,
- isViewer: false,
- },
- },
- };
- } else {
- return {
- ...item,
- threadCreatedFromMessage: undefined,
- reactions: {},
- startsConversation: false,
- startsCluster: true,
- endsCluster: true,
- messageInfo: {
- ...item.messageInfo,
- creator: {
- ...item.messageInfo.creator,
- isViewer: false,
- },
- },
- };
- }
- }, [item]);
-
const createToggleMessagePinPromise = React.useCallback(async () => {
invariant(messageInfo.id, 'messageInfo.id should be defined');
const result = await callToggleMessagePin({
messageID: messageInfo.id,
action: modalInfo.action,
});
return {
newMessageInfos: result.newMessageInfos,
threadID: result.threadID,
};
}, [callToggleMessagePin, messageInfo.id, modalInfo.action]);
const onPress = React.useCallback(() => {
dispatchActionPromise(
toggleMessagePinActionTypes,
createToggleMessagePinPromise(),
);
navigation.goBack();
}, [createToggleMessagePinPromise, dispatchActionPromise, navigation]);
const onCancel = React.useCallback(() => {
navigation.goBack();
}, [navigation]);
return (
{modalInfo.name}
{modalInfo.confirmationText}
);
}
const unboundStyles = {
modal: {
backgroundColor: 'modalForeground',
borderColor: 'modalForegroundBorder',
},
modalHeader: {
fontSize: 18,
color: 'modalForegroundLabel',
},
modalConfirmationText: {
fontSize: 12,
color: 'modalBackgroundLabel',
marginTop: 4,
},
buttonsContainer: {
flexDirection: 'column',
flex: 1,
justifyContent: 'flex-end',
marginBottom: 0,
height: 72,
paddingHorizontal: 16,
},
removePinButton: {
borderRadius: 5,
height: 48,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'vibrantRedButton',
},
pinButton: {
borderRadius: 5,
height: 48,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'purpleButton',
},
cancelButton: {
borderRadius: 5,
height: 48,
justifyContent: 'center',
alignItems: 'center',
},
textColor: {
color: 'modalButtonLabel',
},
};
export default TogglePinModal;
diff --git a/native/chat/utils.js b/native/chat/utils.js
index 23e6cb074..a19d6a72d 100644
--- a/native/chat/utils.js
+++ b/native/chat/utils.js
@@ -1,424 +1,463 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import Animated from 'react-native-reanimated';
import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js';
import { colorIsDark } from 'lib/shared/color-utils.js';
import { getMessageLabel } from 'lib/shared/edit-messages-utils.js';
import { messageKey } from 'lib/shared/message-utils.js';
import { viewerIsMember } from 'lib/shared/thread-utils.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import {
inlineEngagementLabelStyle,
clusterEndHeight,
inlineEngagementStyle,
} from './chat-constants.js';
import { ChatContext, useHeightMeasurer } from './chat-context.js';
import { failedSendHeight } from './failed-send.react.js';
import {
useNativeMessageListData,
type NativeChatMessageItem,
} from './message-data.react.js';
import { authorNameHeight } from './message-header.react.js';
import { multimediaMessageItemHeight } from './multimedia-message-utils.js';
import { getUnresolvedSidebarThreadInfo } from './sidebar-navigation.js';
import textMessageSendFailed from './text-message-send-failed.js';
import { timestampHeight } from './timestamp.react.js';
import { KeyboardContext } from '../keyboard/keyboard-state.js';
import { OverlayContext } from '../navigation/overlay-context.js';
import {
MultimediaMessageTooltipModalRouteName,
RobotextMessageTooltipModalRouteName,
TextMessageTooltipModalRouteName,
} from '../navigation/route-names.js';
import type {
ChatMessageInfoItemWithHeight,
ChatMessageItemWithHeight,
ChatRobotextMessageInfoItemWithHeight,
ChatTextMessageInfoItemWithHeight,
} from '../types/chat-types.js';
import type {
LayoutCoordinates,
VerticalBounds,
} from '../types/layout-types.js';
import type { AnimatedViewStyle } from '../types/styles.js';
/* eslint-disable import/no-named-as-default-member */
const {
Node,
Extrapolate,
interpolateNode,
interpolateColors,
block,
call,
eq,
cond,
sub,
} = Animated;
/* eslint-enable import/no-named-as-default-member */
function textMessageItemHeight(
item: ChatTextMessageInfoItemWithHeight,
): number {
const { messageInfo, contentHeight, startsCluster, endsCluster, threadInfo } =
item;
const { isViewer } = messageInfo.creator;
let height = 5 + contentHeight; // 5 from marginBottom in ComposedMessage
if (!isViewer && startsCluster) {
height += authorNameHeight;
}
if (endsCluster) {
height += clusterEndHeight;
}
if (textMessageSendFailed(item)) {
height += failedSendHeight;
}
const label = getMessageLabel(item.hasBeenEdited, threadInfo);
if (item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0) {
height +=
inlineEngagementStyle.height +
inlineEngagementStyle.marginTop +
inlineEngagementStyle.marginBottom;
} else if (label) {
height +=
inlineEngagementLabelStyle.height +
inlineEngagementStyle.marginTop +
inlineEngagementStyle.marginBottom;
}
return height;
}
function robotextMessageItemHeight(
item: ChatRobotextMessageInfoItemWithHeight,
): number {
if (item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0) {
return item.contentHeight + inlineEngagementStyle.height;
}
return item.contentHeight;
}
function messageItemHeight(item: ChatMessageInfoItemWithHeight): number {
let height = 0;
if (item.messageShapeType === 'text') {
height += textMessageItemHeight(item);
} else if (item.messageShapeType === 'multimedia') {
height += multimediaMessageItemHeight(item);
} else {
height += robotextMessageItemHeight(item);
}
if (item.startsConversation) {
height += timestampHeight;
}
return height;
}
function chatMessageItemHeight(item: ChatMessageItemWithHeight): number {
if (item.itemType === 'loader') {
return 56;
}
return messageItemHeight(item);
}
function useMessageTargetParameters(
sourceMessage: ChatMessageInfoItemWithHeight,
initialCoordinates: LayoutCoordinates,
messageListVerticalBounds: VerticalBounds,
currentInputBarHeight: number,
targetInputBarHeight: number,
sidebarThreadInfo: ?ThreadInfo,
): {
+position: number,
+color: string,
} {
const messageListData = useNativeMessageListData({
searching: false,
userInfoInputArray: [],
threadInfo: sidebarThreadInfo,
});
const [messagesWithHeight, setMessagesWithHeight] =
React.useState$ReadOnlyArray>(null);
const measureMessages = useHeightMeasurer();
React.useEffect(() => {
if (messageListData) {
measureMessages(
messageListData,
sidebarThreadInfo,
setMessagesWithHeight,
);
}
}, [measureMessages, messageListData, sidebarThreadInfo]);
const sourceMessageID = sourceMessage.messageInfo?.id;
const targetDistanceFromBottom = React.useMemo(() => {
if (!messagesWithHeight) {
return 0;
}
let offset = 0;
for (const message of messagesWithHeight) {
offset += chatMessageItemHeight(message);
if (message.messageInfo && message.messageInfo.id === sourceMessageID) {
return offset;
}
}
return (
messageListVerticalBounds.height + chatMessageItemHeight(sourceMessage)
);
}, [
messageListVerticalBounds.height,
messagesWithHeight,
sourceMessage,
sourceMessageID,
]);
if (!sidebarThreadInfo) {
return {
position: 0,
color: sourceMessage.threadInfo.color,
};
}
const authorNameComponentHeight = sourceMessage.messageInfo.creator.isViewer
? 0
: authorNameHeight;
const currentDistanceFromBottom =
messageListVerticalBounds.height +
messageListVerticalBounds.y -
initialCoordinates.y +
timestampHeight +
authorNameComponentHeight +
currentInputBarHeight;
return {
position:
targetDistanceFromBottom +
targetInputBarHeight -
currentDistanceFromBottom,
color: sidebarThreadInfo.color,
};
}
type AnimatedMessageArgs = {
+sourceMessage: ChatMessageInfoItemWithHeight,
+initialCoordinates: LayoutCoordinates,
+messageListVerticalBounds: VerticalBounds,
+progress: Node,
+targetInputBarHeight: ?number,
};
function useAnimatedMessageTooltipButton({
sourceMessage,
initialCoordinates,
messageListVerticalBounds,
progress,
targetInputBarHeight,
}: AnimatedMessageArgs): {
+style: AnimatedViewStyle,
+threadColorOverride: ?Node,
+isThreadColorDarkOverride: ?boolean,
} {
const chatContext = React.useContext(ChatContext);
invariant(chatContext, 'chatContext should be set');
const {
currentTransitionSidebarSourceID,
setCurrentTransitionSidebarSourceID,
chatInputBarHeights,
sidebarAnimationType,
setSidebarAnimationType,
} = chatContext;
const loggedInUserInfo = useLoggedInUserInfo();
const sidebarThreadInfo = React.useMemo(
() => getUnresolvedSidebarThreadInfo({ sourceMessage, loggedInUserInfo }),
[sourceMessage, loggedInUserInfo],
);
const currentInputBarHeight =
chatInputBarHeights.get(sourceMessage.threadInfo.id) ?? 0;
const keyboardState = React.useContext(KeyboardContext);
const newSidebarAnimationType =
!currentInputBarHeight ||
!targetInputBarHeight ||
keyboardState?.keyboardShowing ||
!viewerIsMember(sidebarThreadInfo)
? 'fade_source_message'
: 'move_source_message';
React.useEffect(() => {
setSidebarAnimationType(newSidebarAnimationType);
}, [setSidebarAnimationType, newSidebarAnimationType]);
const { position: targetPosition, color: targetColor } =
useMessageTargetParameters(
sourceMessage,
initialCoordinates,
messageListVerticalBounds,
currentInputBarHeight,
targetInputBarHeight ?? currentInputBarHeight,
sidebarThreadInfo,
);
React.useEffect(() => {
return () => setCurrentTransitionSidebarSourceID(null);
}, [setCurrentTransitionSidebarSourceID]);
const bottom = React.useMemo(
() =>
interpolateNode(progress, {
inputRange: [0.3, 1],
outputRange: [targetPosition, 0],
extrapolate: Extrapolate.CLAMP,
}),
[progress, targetPosition],
);
const [isThreadColorDarkOverride, setThreadColorDarkOverride] =
React.useState(null);
const setThreadColorBrightness = React.useCallback(() => {
const isSourceThreadDark = colorIsDark(sourceMessage.threadInfo.color);
const isTargetThreadDark = colorIsDark(targetColor);
if (isSourceThreadDark !== isTargetThreadDark) {
setThreadColorDarkOverride(isTargetThreadDark);
}
}, [sourceMessage.threadInfo.color, targetColor]);
const threadColorOverride = React.useMemo(() => {
if (
sourceMessage.messageShapeType !== 'text' ||
!currentTransitionSidebarSourceID
) {
return null;
}
return block([
cond(eq(progress, 1), call([], setThreadColorBrightness)),
interpolateColors(progress, {
inputRange: [0, 1],
outputColorRange: [
`#${targetColor}`,
`#${sourceMessage.threadInfo.color}`,
],
}),
]);
}, [
currentTransitionSidebarSourceID,
progress,
setThreadColorBrightness,
sourceMessage.messageShapeType,
sourceMessage.threadInfo.color,
targetColor,
]);
const messageContainerStyle = React.useMemo(() => {
return {
bottom: currentTransitionSidebarSourceID ? bottom : 0,
opacity:
currentTransitionSidebarSourceID &&
sidebarAnimationType === 'fade_source_message'
? 0
: 1,
};
}, [bottom, currentTransitionSidebarSourceID, sidebarAnimationType]);
return {
style: messageContainerStyle,
threadColorOverride,
isThreadColorDarkOverride,
};
}
function getMessageTooltipKey(item: ChatMessageInfoItemWithHeight): string {
return `tooltip|${messageKey(item.messageInfo)}`;
}
function isMessageTooltipKey(key: string): boolean {
return key.startsWith('tooltip|');
}
function useOverlayPosition(item: ChatMessageInfoItemWithHeight) {
const overlayContext = React.useContext(OverlayContext);
invariant(overlayContext, 'should be set');
for (const overlay of overlayContext.visibleOverlays) {
if (
(overlay.routeName === MultimediaMessageTooltipModalRouteName ||
overlay.routeName === TextMessageTooltipModalRouteName ||
overlay.routeName === RobotextMessageTooltipModalRouteName) &&
overlay.routeKey === getMessageTooltipKey(item)
) {
return overlay.position;
}
}
return undefined;
}
function useContentAndHeaderOpacity(
item: ChatMessageInfoItemWithHeight,
): number | Node {
const overlayPosition = useOverlayPosition(item);
const chatContext = React.useContext(ChatContext);
return React.useMemo(
() =>
overlayPosition &&
chatContext?.sidebarAnimationType === 'move_source_message'
? sub(
1,
interpolateNode(overlayPosition, {
inputRange: [0.05, 0.06],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP,
}),
)
: 1,
[chatContext?.sidebarAnimationType, overlayPosition],
);
}
function useDeliveryIconOpacity(
item: ChatMessageInfoItemWithHeight,
): number | Node {
const overlayPosition = useOverlayPosition(item);
const chatContext = React.useContext(ChatContext);
return React.useMemo(() => {
if (
!overlayPosition ||
!chatContext?.currentTransitionSidebarSourceID ||
chatContext?.sidebarAnimationType === 'fade_source_message'
) {
return 1;
}
return interpolateNode(overlayPosition, {
inputRange: [0.05, 0.06, 1],
outputRange: [1, 0, 0],
extrapolate: Extrapolate.CLAMP,
});
}, [
chatContext?.currentTransitionSidebarSourceID,
chatContext?.sidebarAnimationType,
overlayPosition,
]);
}
function chatMessageItemKey(
item: ChatMessageItemWithHeight | NativeChatMessageItem,
): string {
if (item.itemType === 'loader') {
return 'loader';
}
return messageKey(item.messageInfo);
}
+function modifyItemForResultScreen(
+ item: ChatMessageInfoItemWithHeight,
+): ChatMessageInfoItemWithHeight {
+ if (item.messageShapeType === 'robotext') {
+ return item;
+ }
+
+ if (item.messageShapeType === 'multimedia') {
+ return {
+ ...item,
+ startsConversation: false,
+ startsCluster: true,
+ endsCluster: true,
+ messageInfo: {
+ ...item.messageInfo,
+ creator: {
+ ...item.messageInfo.creator,
+ isViewer: false,
+ },
+ },
+ };
+ }
+
+ return {
+ ...item,
+ startsConversation: false,
+ startsCluster: true,
+ endsCluster: true,
+ messageInfo: {
+ ...item.messageInfo,
+ creator: {
+ ...item.messageInfo.creator,
+ isViewer: false,
+ },
+ },
+ };
+}
+
export {
chatMessageItemKey,
chatMessageItemHeight,
useAnimatedMessageTooltipButton,
messageItemHeight,
getMessageTooltipKey,
isMessageTooltipKey,
useContentAndHeaderOpacity,
useDeliveryIconOpacity,
+ modifyItemForResultScreen,
};