Page MenuHomePhabricator

D13913.id45762.diff
No OneTemporary

D13913.id45762.diff

diff --git a/lib/hooks/sidebar-hooks.js b/lib/hooks/sidebar-hooks.js
--- a/lib/hooks/sidebar-hooks.js
+++ b/lib/hooks/sidebar-hooks.js
@@ -3,32 +3,18 @@
import _orderBy from 'lodash/fp/orderBy.js';
import * as React from 'react';
+import { useGetLastUpdatedTimes } from './thread-time.js';
import { childThreadInfos } from '../selectors/thread-selectors.js';
import { getMostRecentNonLocalMessageID } from '../shared/message-utils.js';
import { threadInChatList } from '../shared/thread-utils.js';
-import type { MessageStore, RawMessageInfo } from '../types/message-types.js';
-import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
import { threadTypeIsSidebar } from '../types/thread-types-enum.js';
import type { SidebarInfo } from '../types/thread-types.js';
import { useSelector } from '../utils/redux-utils.js';
-function getMostRecentRawMessageInfo(
- threadInfo: ThreadInfo,
- messageStore: MessageStore,
-): ?RawMessageInfo {
- const thread = messageStore.threads[threadInfo.id];
- if (!thread) {
- return null;
- }
- for (const messageID of thread.messageIDs) {
- return messageStore.messages[messageID];
- }
- return null;
-}
-
function useSidebarInfos(): { +[id: string]: $ReadOnlyArray<SidebarInfo> } {
const childThreadInfoByParentID = useSelector(childThreadInfos);
const messageStore = useSelector(state => state.messageStore);
+ const getLastUpdatedTimes = useGetLastUpdatedTimes();
return React.useMemo(() => {
const result: { [id: string]: $ReadOnlyArray<SidebarInfo> } = {};
@@ -42,12 +28,11 @@
) {
continue;
}
- const mostRecentRawMessageInfo = getMostRecentRawMessageInfo(
+ const { lastUpdatedAtLeastTime: lastUpdatedTime } = getLastUpdatedTimes(
childThreadInfo,
messageStore,
+ messageStore.messages,
);
- const lastUpdatedTime =
- mostRecentRawMessageInfo?.time ?? childThreadInfo.creationTime;
const mostRecentNonLocalMessage = getMostRecentNonLocalMessageID(
childThreadInfo.id,
messageStore,
@@ -61,7 +46,7 @@
result[parentID] = _orderBy('lastUpdatedTime')('desc')(sidebarInfos);
}
return result;
- }, [childThreadInfoByParentID, messageStore]);
+ }, [childThreadInfoByParentID, messageStore, getLastUpdatedTimes]);
}
export { useSidebarInfos };
diff --git a/lib/hooks/thread-time.js b/lib/hooks/thread-time.js
--- a/lib/hooks/thread-time.js
+++ b/lib/hooks/thread-time.js
@@ -1,25 +1,107 @@
// @flow
-import type { MessageInfo, MessageStore } from '../types/message-types.js';
+import * as React from 'react';
+
+import { useGetLatestMessageEdit } from './latest-message-edit.js';
+import { messageSpecs } from '../shared/messages/message-specs.js';
+import type {
+ MessageInfo,
+ RawMessageInfo,
+ MessageStore,
+} from '../types/message-types.js';
import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
+import type { LastUpdatedTimes } from '../types/thread-types.js';
+import { useSelector } from '../utils/redux-utils.js';
-function getLastUpdatedTime(
+function useGetLastUpdatedTimes(): (
threadInfo: ThreadInfo,
messageStore: MessageStore,
- messages: { +[id: string]: ?MessageInfo },
-): number {
- const thread = messageStore.threads[threadInfo.id];
- if (!thread) {
- return threadInfo.creationTime;
- }
- for (const messageID of thread.messageIDs) {
- const messageInfo = messages[messageID];
- if (!messageInfo) {
- continue;
- }
- return messageInfo.time;
- }
- return threadInfo.creationTime;
+ messages: { +[id: string]: ?MessageInfo | RawMessageInfo },
+) => LastUpdatedTimes {
+ const viewerID = useSelector(state => state.currentUserInfo?.id);
+ const fetchMessage = useGetLatestMessageEdit();
+ return React.useCallback(
+ (threadInfo, messageStore, messages) => {
+ // This callback returns two variables:
+ // - lastUpdatedTime: this is a Promise that resolves with the final value
+ // - lastUpdatedAtLeastTime: this is a number that represents what we
+ // should use while we're waiting for lastUpdatedTime to resolve. It's
+ // set based on the most recent message whose spec returns a non-Promise
+ // when getLastUpdatedTime is called
+ let lastUpdatedAtLeastTime = threadInfo.creationTime;
+
+ const thread = messageStore.threads[threadInfo.id];
+ if (!thread || !viewerID) {
+ return {
+ lastUpdatedAtLeastTime,
+ lastUpdatedTime: Promise.resolve(lastUpdatedAtLeastTime),
+ };
+ }
+
+ const getLastUpdatedTimeParams = {
+ threadInfo,
+ viewerID,
+ fetchMessage,
+ };
+
+ let lastUpdatedTime: ?Promise<?number>;
+ for (const messageID of thread.messageIDs) {
+ const messageInfo = messages[messageID];
+ if (!messageInfo) {
+ continue;
+ }
+
+ // We call getLastUpdatedTime on the message spec. It can return either
+ // ?number or Promise<?number>. If the message spec doesn't implement
+ // getLastUpdatedTime, then we default to messageInfo.time.
+ const { getLastUpdatedTime } = messageSpecs[messageInfo.type];
+ const lastUpdatedTimePromisable = getLastUpdatedTime
+ ? getLastUpdatedTime(messageInfo, getLastUpdatedTimeParams)
+ : messageInfo.time;
+
+ // We rely on the fact that thread.messageIDs is ordered chronologically
+ // (newest first) to chain together lastUpdatedTime. An older message's
+ // lastUpdatedTime is only considered if all of the newer messages
+ // return falsey.
+ lastUpdatedTime = (async () => {
+ if (lastUpdatedTime) {
+ const earlierChecks = await lastUpdatedTime;
+ if (earlierChecks) {
+ return earlierChecks;
+ }
+ }
+ return await lastUpdatedTimePromisable;
+ })();
+
+ if (typeof lastUpdatedTimePromisable === 'number') {
+ // We break from the loop the first time this condition is met.
+ // There's no need to consider any older messages, since both
+ // lastUpdated and lastUpdatedAtLeastTime will be this value (or
+ // higher, in the case of lastUpdated). That is also why this loop
+ // only sets lastUpdatedAtLeastTime once: once we get to this
+ // "baseline" case, there's no need to consider any more messages.
+ lastUpdatedAtLeastTime = lastUpdatedTimePromisable;
+ break;
+ }
+ }
+
+ const lastUpdatedWithFallback = (async () => {
+ if (lastUpdatedTime) {
+ const earlierChecks = await lastUpdatedTime;
+ if (earlierChecks) {
+ return earlierChecks;
+ }
+ }
+ return lastUpdatedAtLeastTime;
+ })();
+
+ return {
+ lastUpdatedAtLeastTime,
+ lastUpdatedTime: lastUpdatedWithFallback,
+ };
+ },
+ [viewerID, fetchMessage],
+ );
}
-export { getLastUpdatedTime };
+export { useGetLastUpdatedTimes };
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
@@ -14,7 +14,7 @@
threadInfoSelector,
} from './thread-selectors.js';
import { useSidebarInfos } from '../hooks/sidebar-hooks.js';
-import { getLastUpdatedTime } from '../hooks/thread-time.js';
+import { useGetLastUpdatedTimes } from '../hooks/thread-time.js';
import {
createMessageInfo,
getMostRecentNonLocalMessageID,
@@ -97,13 +97,15 @@
const messageInfos = useSelector(messageInfoSelector);
const sidebarInfos = useSidebarInfos();
const messageStore = useSelector(state => state.messageStore);
+ const getLastUpdatedTimes = useGetLastUpdatedTimes();
return React.useCallback(
threadInfo => {
const mostRecentNonLocalMessage = getMostRecentNonLocalMessageID(
threadInfo.id,
messageStore,
);
- const lastUpdatedTime = getLastUpdatedTime(
+
+ const { lastUpdatedAtLeastTime: lastUpdatedTime } = getLastUpdatedTimes(
threadInfo,
messageStore,
messageInfos,
@@ -170,7 +172,7 @@
sidebars: sidebarItems,
};
},
- [messageInfos, messageStore, sidebarInfos],
+ [messageInfos, messageStore, sidebarInfos, getLastUpdatedTimes],
);
}
diff --git a/lib/shared/messages/message-spec.js b/lib/shared/messages/message-spec.js
--- a/lib/shared/messages/message-spec.js
+++ b/lib/shared/messages/message-spec.js
@@ -141,4 +141,8 @@
messageInfo: Info,
params: ShowInMessagePreviewParams,
) => Promise<boolean>,
+ +getLastUpdatedTime?: (
+ messageInfoOrRawMessageInfo: Info | RawInfo,
+ params: ShowInMessagePreviewParams,
+ ) => ?number | Promise<?number>,
};
diff --git a/lib/shared/messages/multimedia-message-spec.js b/lib/shared/messages/multimedia-message-spec.js
--- a/lib/shared/messages/multimedia-message-spec.js
+++ b/lib/shared/messages/multimedia-message-spec.js
@@ -369,6 +369,14 @@
showInMessagePreview: (messageInfo: MediaMessageInfo | ImagesMessageInfo) =>
Promise.resolve(messageInfo.media.length > 0),
+
+ getLastUpdatedTime: (
+ messageInfo:
+ | MediaMessageInfo
+ | ImagesMessageInfo
+ | RawMediaMessageInfo
+ | RawImagesMessageInfo,
+ ) => (messageInfo.media.length > 0 ? messageInfo.time : null),
});
// Four photos were uploaded before dimensions were calculated server-side,
diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js
--- a/lib/types/thread-types.js
+++ b/lib/types/thread-types.js
@@ -436,6 +436,13 @@
+offset: number,
};
+export type LastUpdatedTimes = {
+ // The last updated time is at least this number, but possibly higher
+ // We won't know for sure until the below Promise resolves
+ +lastUpdatedAtLeastTime: number,
+ +lastUpdatedTime: Promise<number>,
+};
+
export type SidebarInfo = {
+threadInfo: ThreadInfo,
+lastUpdatedTime: number,

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 21, 9:23 AM (5 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2539416
Default Alt Text
D13913.id45762.diff (9 KB)

Event Timeline