Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3345938
D13686.id45045.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
20 KB
Referenced Files
None
Subscribers
None
D13686.id45045.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D13686: [lib][native][web] Add support for rendering array of messages in RobotextChatMessageInfoItem
Attached
Detach File
Event Timeline
Log In to Comment