Page MenuHomePhabricator

D13686.id45045.diff
No OneTemporary

D13686.id45045.diff

diff --git a/lib/selectors/chat-selectors.js b/lib/selectors/chat-selectors.js
--- a/lib/selectors/chat-selectors.js
+++ b/lib/selectors/chat-selectors.js
@@ -275,7 +275,7 @@
export type RobotextChatMessageInfoItem = {
+itemType: 'message',
+messageInfoType: 'robotext',
- +messageInfo: RobotextMessageInfo,
+ +messageInfos: $ReadOnlyArray<RobotextMessageInfo>,
+startsConversation: boolean,
+startsCluster: boolean,
endsCluster: boolean,
@@ -549,7 +549,7 @@
chatMessageItems.push({
itemType: 'message',
messageInfoType: 'robotext',
- messageInfo: originalMessageInfo,
+ messageInfos: [originalMessageInfo],
startsConversation,
startsCluster,
endsCluster: false,
diff --git a/lib/shared/chat-message-item-utils.js b/lib/shared/chat-message-item-utils.js
--- a/lib/shared/chat-message-item-utils.js
+++ b/lib/shared/chat-message-item-utils.js
@@ -14,12 +14,19 @@
// This complicated type matches both ChatMessageItem and
// ChatMessageItemWithHeight, and is a disjoint union of types
-type BaseChatMessageInfoItem = {
- +itemType: 'message',
- +messageInfo: ChatMessageItemMessageInfo,
- +messageInfos?: ?void,
- ...
-};
+type BaseChatMessageInfoItem =
+ | {
+ +itemType: 'message',
+ +messageInfo: ChatMessageItemMessageInfo,
+ +messageInfos?: ?void,
+ ...
+ }
+ | {
+ +itemType: 'message',
+ +messageInfos: $ReadOnlyArray<ChatMessageItemMessageInfo>,
+ +messageInfo?: ?void,
+ ...
+ };
type BaseChatMessageItem =
| BaseChatMessageInfoItem
| {
@@ -33,26 +40,49 @@
if (item.itemType === 'loader') {
return 'loader';
}
- return messageKey(item.messageInfo);
+ if (item.messageInfo) {
+ return messageKey(item.messageInfo);
+ }
+ return item.messageInfos.map(messageKey).join('|');
}
function chatMessageInfoItemTimestamp(item: BaseChatMessageInfoItem): string {
- return longAbsoluteDate(item.messageInfo.time);
+ // If there's an array of messageInfos, we expect at least one,
+ // and the most recent should be first
+ const messageInfo = item.messageInfo
+ ? item.messageInfo
+ : item.messageInfos[0];
+ return longAbsoluteDate(messageInfo.time);
}
+// If the ChatMessageInfoItem can be the target of operations like sidebar
+// creation, reaction, or pinning, then this function returns the RawMessageInfo
+// that would be the target. Currently, the only reason that a
+// ChatMessageInfoItem can't be such a target would be if it's a combined
+// RobotextChatMessageInfoItem that has multiple messageInfos.
function chatMessageInfoItemTargetableMessageInfo(
item: BaseChatMessageInfoItem,
-): ComposableMessageInfo | RobotextMessageInfo {
- return item.messageInfo;
+): ?ComposableMessageInfo | RobotextMessageInfo {
+ if (item.messageInfo) {
+ return item.messageInfo;
+ } else if (item.messageInfos && item.messageInfos.length === 1) {
+ return item.messageInfos[0];
+ }
+ return null;
}
function chatMessageItemHasNonViewerMessage(
item: BaseChatMessageItem,
viewerID: ?string,
): boolean {
- return (
- item.itemType === 'message' && item.messageInfo.creator.id !== viewerID
- );
+ if (item.messageInfo) {
+ return item.messageInfo.creator.id !== viewerID;
+ } else if (item.messageInfos) {
+ return item.messageInfos.some(
+ messageInfo => messageInfo.creator.id !== viewerID,
+ );
+ }
+ return false;
}
type BaseChatMessageItemForEngagementCheck = {
diff --git a/lib/shared/edit-messages-utils.js b/lib/shared/edit-messages-utils.js
--- a/lib/shared/edit-messages-utils.js
+++ b/lib/shared/edit-messages-utils.js
@@ -100,7 +100,7 @@
function useCanEditMessage(
threadInfo: ThreadInfo,
- targetMessageInfo: ComposableMessageInfo | RobotextMessageInfo,
+ targetMessageInfo: ?ComposableMessageInfo | RobotextMessageInfo,
): boolean {
const currentUserInfo = useSelector(state => state.currentUserInfo);
const currentUserCanEditMessage = useThreadHasPermission(
@@ -111,7 +111,11 @@
return false;
}
- if (!targetMessageInfo.id || targetMessageInfo.type !== messageTypes.TEXT) {
+ if (
+ !targetMessageInfo ||
+ !targetMessageInfo.id ||
+ targetMessageInfo.type !== messageTypes.TEXT
+ ) {
return false;
}
diff --git a/lib/shared/reaction-utils.js b/lib/shared/reaction-utils.js
--- a/lib/shared/reaction-utils.js
+++ b/lib/shared/reaction-utils.js
@@ -76,12 +76,11 @@
function useCanCreateReactionFromMessage(
threadInfo: ThreadInfo,
- targetMessageInfo: ComposableMessageInfo | RobotextMessageInfo,
+ targetMessageInfo: ?ComposableMessageInfo | RobotextMessageInfo,
): boolean {
- const targetMessageCreatorRelationship = useSelector(
- state =>
- state.userStore.userInfos[targetMessageInfo.creator.id]
- ?.relationshipStatus,
+ const creatorID = targetMessageInfo?.creator.id;
+ const targetMessageCreatorRelationship = useSelector(state =>
+ creatorID ? state.userStore.userInfos[creatorID]?.relationshipStatus : null,
);
const userHasReactionPermission = useThreadHasPermission(
@@ -93,6 +92,7 @@
}
if (
+ !targetMessageInfo ||
(!targetMessageInfo.id && !threadTypeIsThick(threadInfo.type)) ||
(threadInfo.sourceMessageID &&
threadInfo.sourceMessageID === targetMessageInfo.id)
diff --git a/lib/shared/sidebar-utils.js b/lib/shared/sidebar-utils.js
--- a/lib/shared/sidebar-utils.js
+++ b/lib/shared/sidebar-utils.js
@@ -197,10 +197,11 @@
function useCanCreateSidebarFromMessage(
threadInfo: ThreadInfo,
- messageInfo: ComposableMessageInfo | RobotextMessageInfo,
+ messageInfo: ?ComposableMessageInfo | RobotextMessageInfo,
): boolean {
- const messageCreatorUserInfo = useSelector(
- state => state.userStore.userInfos[messageInfo.creator.id],
+ const creatorID = messageInfo?.creator.id;
+ const messageCreatorUserInfo = useSelector(state =>
+ creatorID ? state.userStore.userInfos[creatorID] : null,
);
const hasCreateSidebarsPermission = useThreadHasPermission(
threadInfo,
@@ -211,6 +212,7 @@
}
if (
+ !messageInfo ||
(!messageInfo.id && !threadTypeIsThick(threadInfo.type)) ||
(threadInfo.sourceMessageID &&
threadInfo.sourceMessageID === messageInfo.id) ||
diff --git a/lib/utils/message-pinning-utils.js b/lib/utils/message-pinning-utils.js
--- a/lib/utils/message-pinning-utils.js
+++ b/lib/utils/message-pinning-utils.js
@@ -27,16 +27,18 @@
}
function useCanToggleMessagePin(
- messageInfo: MessageInfo,
+ messageInfo: ?MessageInfo,
threadInfo: ThreadInfo,
): boolean {
- const isValidMessage = !isInvalidPinSourceForThread(messageInfo, threadInfo);
const hasManagePinsPermission = useThreadHasPermission(
threadInfo,
threadPermissions.MANAGE_PINS,
);
-
- return isValidMessage && hasManagePinsPermission;
+ return (
+ !!messageInfo &&
+ hasManagePinsPermission &&
+ !isInvalidPinSourceForThread(messageInfo, threadInfo)
+ );
}
function pinnedMessageCountText(pinnedCount: number): string {
diff --git a/native/chat/chat-item-height-measurer.react.js b/native/chat/chat-item-height-measurer.react.js
--- a/native/chat/chat-item-height-measurer.react.js
+++ b/native/chat/chat-item-height-measurer.react.js
@@ -38,7 +38,7 @@
const { messageInfo, hasBeenEdited, threadCreatedFromMessage, reactions } =
item;
- if (messageInfo.type === messageTypes.TEXT) {
+ if (messageInfo && messageInfo.type === messageTypes.TEXT) {
return JSON.stringify({
text: messageInfo.text,
edited: getMessageLabel(hasBeenEdited, messageInfo.threadID),
@@ -46,7 +46,7 @@
reactions: reactionsToRawString(reactions),
});
} else if (item.robotext) {
- const { threadID } = item.messageInfo;
+ const { threadID } = item.messageInfos[0];
return JSON.stringify({
robotext: entityTextToRawString(item.robotext, { threadID }),
sidebar: getInlineEngagementSidebarText(threadCreatedFromMessage),
@@ -73,7 +73,7 @@
const { messageInfo, hasBeenEdited, threadCreatedFromMessage, reactions } =
item;
- if (messageInfo.type === messageTypes.TEXT) {
+ if (messageInfo && messageInfo.type === messageTypes.TEXT) {
const label = getMessageLabel(hasBeenEdited, messageInfo.threadID);
return dummyNodeForTextMessageHeightMeasurement(
messageInfo.text,
@@ -84,7 +84,7 @@
} else if (item.robotext) {
return dummyNodeForRobotextMessageHeightMeasurement(
item.robotext,
- messageInfo.threadID,
+ item.messageInfos[0].threadID,
threadCreatedFromMessage,
reactions,
);
@@ -116,6 +116,26 @@
return item;
}
+ if (item.messageInfoType !== 'composable') {
+ invariant(
+ height !== null && height !== undefined,
+ 'height should be set',
+ );
+ return {
+ itemType: 'message',
+ messageShapeType: 'robotext',
+ messageInfos: item.messageInfos,
+ threadInfo,
+ startsConversation: item.startsConversation,
+ startsCluster: item.startsCluster,
+ endsCluster: item.endsCluster,
+ threadCreatedFromMessage: item.threadCreatedFromMessage,
+ robotext: item.robotext,
+ contentHeight: height,
+ reactions: item.reactions,
+ };
+ }
+
const { messageInfo } = item;
const messageType: MessageType = messageInfo.type;
invariant(
@@ -181,29 +201,11 @@
isPinned: item.isPinned,
};
}
- invariant(
- item.messageInfoType !== 'composable',
+
+ throw new Error(
'ChatItemHeightMeasurer was handed a messageInfoType=composable, but ' +
`does not know how to handle MessageType ${messageInfo.type}`,
);
- invariant(
- item.messageInfoType === 'robotext',
- 'ChatItemHeightMeasurer was handed a messageInfoType that it does ' +
- `not recognize: ${item.messageInfoType}`,
- );
- return {
- itemType: 'message',
- messageShapeType: 'robotext',
- messageInfo,
- threadInfo,
- startsConversation: item.startsConversation,
- startsCluster: item.startsCluster,
- endsCluster: item.endsCluster,
- threadCreatedFromMessage: item.threadCreatedFromMessage,
- robotext: item.robotext,
- contentHeight: height,
- reactions: item.reactions,
- };
},
[composedMessageMaxWidth, inputStatePendingUploads, threadInfo],
);
diff --git a/native/chat/inner-robotext-message.react.js b/native/chat/inner-robotext-message.react.js
--- a/native/chat/inner-robotext-message.react.js
+++ b/native/chat/inner-robotext-message.react.js
@@ -54,8 +54,8 @@
const activeTheme = useSelector(state => state.globalThemeInfo.activeTheme);
const styles = useOverlayStyles(unboundStyles);
- const { messageInfo, robotext } = item;
- const { threadID } = messageInfo;
+ const { messageInfos, robotext } = item;
+ const { threadID } = messageInfos[0];
const resolvedRobotext = useResolvedEntityText(robotext);
invariant(
resolvedRobotext,
diff --git a/native/chat/robotext-message.react.js b/native/chat/robotext-message.react.js
--- a/native/chat/robotext-message.react.js
+++ b/native/chat/robotext-message.react.js
@@ -65,7 +65,10 @@
const styles = useStyles(unboundStyles);
const targetableMessageInfo = chatMessageInfoItemTargetableMessageInfo(item);
let inlineEngagement = null;
- if (chatMessageItemHasEngagement(item, item.threadInfo.id)) {
+ if (
+ targetableMessageInfo &&
+ chatMessageItemHasEngagement(item, item.threadInfo.id)
+ ) {
inlineEngagement = (
<View style={styles.sidebar}>
<InlineEngagement
diff --git a/native/chat/sidebar-navigation.js b/native/chat/sidebar-navigation.js
--- a/native/chat/sidebar-navigation.js
+++ b/native/chat/sidebar-navigation.js
@@ -41,6 +41,10 @@
}
const { messageInfo, threadInfo } = sourceMessage;
+ if (!messageInfo) {
+ return null;
+ }
+
return createUnresolvedPendingSidebar({
sourceMessageInfo: messageInfo,
parentThreadInfo: threadInfo,
@@ -75,6 +79,10 @@
}
const { messageInfo, threadInfo } = sourceMessage;
+ if (!messageInfo) {
+ return null;
+ }
+
return await createPendingSidebar({
sourceMessageInfo: messageInfo,
parentThreadInfo: threadInfo,
@@ -125,8 +133,11 @@
const chatContext = React.useContext(ChatContext);
const setSidebarSourceID = chatContext?.setCurrentTransitionSidebarSourceID;
const navigateToSidebar = useNavigateToSidebar(item);
- const messageID = item.messageInfo.id;
+ const messageID = item.messageInfo?.id;
return React.useCallback(() => {
+ if (!messageID) {
+ return;
+ }
setSidebarSourceID && setSidebarSourceID(messageID);
navigateToSidebar();
}, [setSidebarSourceID, messageID, navigateToSidebar]);
diff --git a/native/chat/utils.js b/native/chat/utils.js
--- a/native/chat/utils.js
+++ b/native/chat/utils.js
@@ -153,9 +153,10 @@
};
}
- const authorNameComponentHeight = sourceMessage.messageInfo.creator.isViewer
- ? 0
- : authorNameHeight;
+ const authorNameComponentHeight =
+ !sourceMessage.messageInfo || sourceMessage.messageInfo.creator.isViewer
+ ? 0
+ : authorNameHeight;
const currentDistanceFromBottom =
messageListVerticalBounds.height +
messageListVerticalBounds.y -
diff --git a/native/search/message-search.react.js b/native/search/message-search.react.js
--- a/native/search/message-search.react.js
+++ b/native/search/message-search.react.js
@@ -228,8 +228,21 @@
function oldestMessage(data: $ReadOnlyArray<ChatMessageItemWithHeight>) {
for (let i = data.length - 1; i >= 0; i--) {
- if (data[i].itemType === 'message' && data[i].messageInfo.id) {
- return data[i].messageInfo;
+ const item = data[i];
+ if (item.itemType !== 'message') {
+ continue;
+ }
+ if (item.messageShapeType !== 'robotext') {
+ if (item.messageInfo.id) {
+ return item.messageInfo;
+ }
+ continue;
+ }
+ for (let j = item.messageInfos.length - 1; j >= 0; j--) {
+ const messageInfo = item.messageInfos[j];
+ if (messageInfo.id) {
+ return messageInfo;
+ }
}
}
return undefined;
diff --git a/native/types/chat-types.js b/native/types/chat-types.js
--- a/native/types/chat-types.js
+++ b/native/types/chat-types.js
@@ -15,7 +15,7 @@
export type ChatRobotextMessageInfoItemWithHeight = {
+itemType: 'message',
+messageShapeType: 'robotext',
- +messageInfo: RobotextMessageInfo,
+ +messageInfos: $ReadOnlyArray<RobotextMessageInfo>,
+threadInfo: ThreadInfo,
+startsConversation: boolean,
+startsCluster: boolean,
diff --git a/web/chat/chat-message-list.react.js b/web/chat/chat-message-list.react.js
--- a/web/chat/chat-message-list.react.js
+++ b/web/chat/chat-message-list.react.js
@@ -135,6 +135,7 @@
(hasNewMessage &&
messageListData &&
messageListData[0].itemType === 'message' &&
+ messageListData[0].messageInfoType === 'composable' &&
messageListData[0].messageInfo.localID) ||
(hasNewMessage &&
snapshot &&
diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js
--- a/web/chat/robotext-message.react.js
+++ b/web/chat/robotext-message.react.js
@@ -41,9 +41,11 @@
let inlineEngagement;
const { item, threadInfo } = props;
const { threadCreatedFromMessage, reactions } = item;
- if (threadCreatedFromMessage || Object.keys(reactions).length > 0) {
- const targetableMessageInfo =
- chatMessageInfoItemTargetableMessageInfo(item);
+ const targetableMessageInfo = chatMessageInfoItemTargetableMessageInfo(item);
+ if (
+ targetableMessageInfo &&
+ (threadCreatedFromMessage || Object.keys(reactions).length > 0)
+ ) {
inlineEngagement = (
<div className={css.sidebarMarginTop}>
<InlineEngagement
@@ -57,8 +59,8 @@
);
}
- const { messageInfo, robotext } = item;
- const { threadID } = messageInfo;
+ const { messageInfos, robotext } = item;
+ const { threadID } = messageInfos[0];
const resolvedRobotext = useResolvedEntityText(robotext);
invariant(
resolvedRobotext,
diff --git a/web/components/message-result.react.js b/web/components/message-result.react.js
--- a/web/components/message-result.react.js
+++ b/web/components/message-result.react.js
@@ -37,7 +37,9 @@
const shouldShowUsername = !item.startsConversation && !item.startsCluster;
const username = useStringForUser(
- shouldShowUsername ? item.messageInfo.creator : null,
+ shouldShowUsername && item.messageInfoType === 'composable'
+ ? item.messageInfo.creator
+ : null,
);
const messageContainerClassNames = classNames({
diff --git a/web/selectors/thread-selectors.js b/web/selectors/thread-selectors.js
--- a/web/selectors/thread-selectors.js
+++ b/web/selectors/thread-selectors.js
@@ -64,7 +64,7 @@
}
function useOnClickPendingSidebar(
- messageInfo: ComposableMessageInfo | RobotextMessageInfo,
+ messageInfo: ?ComposableMessageInfo | RobotextMessageInfo,
threadInfo: ThreadInfo,
): (event: SyntheticEvent<HTMLElement>) => mixed {
const dispatch = useDispatch();
@@ -79,7 +79,7 @@
return React.useCallback(
async (event: SyntheticEvent<HTMLElement>) => {
event.preventDefault();
- if (!loggedInUserInfo) {
+ if (!loggedInUserInfo || !messageInfo) {
return;
}
const pendingSidebarInfo = await createPendingSidebar({
diff --git a/web/tooltips/tooltip-action-utils.js b/web/tooltips/tooltip-action-utils.js
--- a/web/tooltips/tooltip-action-utils.js
+++ b/web/tooltips/tooltip-action-utils.js
@@ -175,7 +175,6 @@
item: ChatMessageInfoItem,
threadInfo: ThreadInfo,
): ?MessageTooltipAction {
- const { messageInfo } = item;
const { popModal } = useModalContext();
const inputState = React.useContext(InputStateContext);
invariant(inputState, 'inputState is required');
@@ -184,8 +183,18 @@
threadInfo,
threadPermissions.VOICED,
);
+
+ let messageInfo;
+ if (
+ item.messageInfoType === 'composable' &&
+ item.messageInfo.type === messageTypes.TEXT &&
+ currentUserIsVoiced
+ ) {
+ messageInfo = item.messageInfo;
+ }
+
return React.useMemo(() => {
- if (item.messageInfo.type !== messageTypes.TEXT || !currentUserIsVoiced) {
+ if (!messageInfo) {
return null;
}
const buttonContent = <CommIcon icon="reply-filled" size={18} />;
@@ -202,34 +211,35 @@
onClick,
label: 'Reply',
};
- }, [
- popModal,
- addReply,
- item.messageInfo.type,
- messageInfo,
- currentUserIsVoiced,
- ]);
+ }, [popModal, addReply, messageInfo]);
}
const copiedMessageDurationMs = 2000;
function useMessageCopyAction(
item: ChatMessageInfoItem,
): ?MessageTooltipAction {
- const { messageInfo } = item;
-
const [successful, setSuccessful] = useResettingState(
false,
copiedMessageDurationMs,
);
+ let messageInfo;
+ if (
+ item.messageInfoType === 'composable' &&
+ item.messageInfo.type === messageTypes.TEXT
+ ) {
+ messageInfo = item.messageInfo;
+ }
+
+ const messageText = messageInfo?.text;
return React.useMemo(() => {
- if (messageInfo.type !== messageTypes.TEXT) {
+ if (!messageText) {
return null;
}
const buttonContent = <CommIcon icon="copy-filled" size={18} />;
const onClick = async () => {
try {
- await navigator.clipboard.writeText(messageInfo.text);
+ await navigator.clipboard.writeText(messageText);
setSuccessful(true);
} catch (e) {
setSuccessful(false);
@@ -240,7 +250,7 @@
onClick,
label: successful ? 'Copied!' : 'Copy',
};
- }, [messageInfo.text, messageInfo.type, setSuccessful, successful]);
+ }, [messageText, setSuccessful, successful]);
}
function useMessageReactAction(
@@ -336,6 +346,10 @@
item.messageInfoType === 'composable',
'canEditMessage should only be true for composable messages!',
);
+ invariant(
+ messageInfo && messageInfo.type === messageTypes.TEXT,
+ 'canEditMessage should only be true for text messages!',
+ );
const buttonContent = <CommIcon icon="edit-filled" size={18} />;
const onClickEdit = () => {
const callback = (maxHeight: number) =>

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 23, 7:36 AM (19 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2569651
Default Alt Text
D13686.id45045.diff (20 KB)

Event Timeline