Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3509419
D13913.id46431.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D13913.id46431.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 22, 4:37 AM (1 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690360
Default Alt Text
D13913.id46431.diff (9 KB)
Attached To
Mode
D13913: [lib] Introduce MessageSpec.getLastUpdatedTime
Attached
Detach File
Event Timeline
Log In to Comment