Page MenuHomePhabricator

D13916.id45801.diff
No OneTemporary

D13916.id45801.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
@@ -1,9 +1,6 @@
// @flow
import invariant from 'invariant';
-import _filter from 'lodash/fp/filter.js';
-import _flow from 'lodash/fp/flow.js';
-import _map from 'lodash/fp/map.js';
import _orderBy from 'lodash/fp/orderBy.js';
import * as React from 'react';
import { createSelector } from 'reselect';
@@ -26,6 +23,7 @@
import {
getSidebarItems,
getAllInitialSidebarItems,
+ getAllFinalSidebarItems,
type SidebarItem,
} from '../shared/sidebar-item-utils.js';
import { threadInChatList, threadIsPending } from '../shared/thread-utils.js';
@@ -44,6 +42,7 @@
} from '../types/minimally-encoded-thread-permissions-types.js';
import type { BaseAppState } from '../types/redux-types.js';
import { threadTypeIsSidebar } from '../types/thread-types-enum.js';
+import type { SidebarInfo, LastUpdatedTimes } from '../types/thread-types.js';
import type {
AccountUserInfo,
RelativeUserInfo,
@@ -53,15 +52,18 @@
import memoize2 from '../utils/memoize.js';
import { useSelector } from '../utils/redux-utils.js';
-export type ChatThreadItem = {
+type ChatThreadItemBase = {
+type: 'chatThreadItem',
+threadInfo: ThreadInfo,
+mostRecentNonLocalMessage: ?string,
- +lastUpdatedTime: number,
- +lastUpdatedTimeIncludingSidebars: number,
+sidebars: $ReadOnlyArray<SidebarItem>,
+pendingPersonalThreadUserInfo?: UserInfo,
};
+export type ChatThreadItem = $ReadOnly<{
+ ...ChatThreadItemBase,
+ +lastUpdatedTime: number,
+ +lastUpdatedTimeIncludingSidebars: number,
+}>;
const messageInfoSelector: (state: BaseAppState<>) => {
+[id: string]: ?MessageInfo,
@@ -81,7 +83,14 @@
);
}
-function useCreateChatThreadItem(): ThreadInfo => ChatThreadItem {
+type CreatedChatThreadItem = $ReadOnly<{
+ ...ChatThreadItemBase,
+ ...LastUpdatedTimes,
+ +lastUpdatedTimeIncludingSidebars: Promise<number>,
+ +lastUpdatedAtLeastTimeIncludingSidebars: number,
+ +allSidebarInfos: $ReadOnlyArray<SidebarInfo>,
+}>;
+function useCreateChatThreadItem(): ThreadInfo => CreatedChatThreadItem {
const messageInfos = useSelector(messageInfoSelector);
const sidebarInfos = useSidebarInfos();
const messageStore = useSelector(state => state.messageStore);
@@ -93,7 +102,7 @@
messageStore,
);
- const { lastUpdatedAtLeastTime } = getLastUpdatedTimes(
+ const { lastUpdatedTime, lastUpdatedAtLeastTime } = getLastUpdatedTimes(
threadInfo,
messageStore,
messageInfos,
@@ -105,6 +114,15 @@
? Math.max(lastUpdatedAtLeastTime, sidebars[0].lastUpdatedAtLeastTime)
: lastUpdatedAtLeastTime;
+ const lastUpdatedTimeIncludingSidebars = (async () => {
+ if (sidebars.length === 0) {
+ return await lastUpdatedTime;
+ }
+ const [lastUpdatedTimeResult, sidebarLastUpdatedTimeResult] =
+ await Promise.all([lastUpdatedTime, sidebars[0].lastUpdatedTime]);
+ return Math.max(lastUpdatedTimeResult, sidebarLastUpdatedTimeResult);
+ })();
+
const allInitialSidebarItems = getAllInitialSidebarItems(sidebars);
const sidebarItems = getSidebarItems(allInitialSidebarItems);
@@ -112,10 +130,12 @@
type: 'chatThreadItem',
threadInfo,
mostRecentNonLocalMessage,
- lastUpdatedTime: lastUpdatedAtLeastTime,
- lastUpdatedTimeIncludingSidebars:
- lastUpdatedAtLeastTimeIncludingSidebars,
+ lastUpdatedTime,
+ lastUpdatedAtLeastTime,
+ lastUpdatedTimeIncludingSidebars,
+ lastUpdatedAtLeastTimeIncludingSidebars,
sidebars: sidebarItems,
+ allSidebarInfos: sidebars,
};
},
[messageInfos, messageStore, sidebarInfos, getLastUpdatedTimes],
@@ -130,16 +150,158 @@
filterFunction: (threadInfo: ?(ThreadInfo | RawThreadInfo)) => boolean,
): $ReadOnlyArray<ChatThreadItem> {
const threadInfos = useSelector(threadInfoSelector);
+ const filteredThreadInfos = React.useMemo(
+ () => Object.values(threadInfos).filter(filterFunction),
+ [threadInfos, filterFunction],
+ );
+ return useChatThreadItems(filteredThreadInfos);
+}
+
+const sortFunc = _orderBy('lastUpdatedTimeIncludingSidebars')('desc');
+function useChatThreadItems(
+ threadInfos: $ReadOnlyArray<ThreadInfo>,
+): $ReadOnlyArray<ChatThreadItem> {
const getChatThreadItem = useCreateChatThreadItem();
- return React.useMemo(
+ const createdChatThreadItems = React.useMemo(
+ () => threadInfos.map(getChatThreadItem),
+ [threadInfos, getChatThreadItem],
+ );
+
+ const initialChatThreadItems = React.useMemo(
() =>
- _flow(
- _filter(filterFunction),
- _map(getChatThreadItem),
- _orderBy('lastUpdatedTimeIncludingSidebars')('desc'),
- )(threadInfos),
- [getChatThreadItem, filterFunction, threadInfos],
+ createdChatThreadItems.map(createdChatThreadItem => {
+ const {
+ allSidebarInfos,
+ lastUpdatedTime,
+ lastUpdatedAtLeastTime,
+ lastUpdatedTimeIncludingSidebars,
+ lastUpdatedAtLeastTimeIncludingSidebars,
+ ...rest
+ } = createdChatThreadItem;
+ return {
+ ...rest,
+ lastUpdatedTime: lastUpdatedAtLeastTime,
+ lastUpdatedTimeIncludingSidebars:
+ lastUpdatedAtLeastTimeIncludingSidebars,
+ };
+ }),
+ [createdChatThreadItems],
);
+
+ const [chatThreadItems, setChatThreadItems] = React.useState<
+ $ReadOnlyArray<ChatThreadItem>,
+ >(initialChatThreadItems);
+
+ const prevCreatedChatThreadItemsRef = React.useRef(createdChatThreadItems);
+ React.useEffect(() => {
+ if (createdChatThreadItems === prevCreatedChatThreadItemsRef.current) {
+ return;
+ }
+ prevCreatedChatThreadItemsRef.current = createdChatThreadItems;
+
+ setChatThreadItems(initialChatThreadItems);
+
+ void (async () => {
+ const finalChatThreadItems = await Promise.all(
+ createdChatThreadItems.map(async createdChatThreadItem => {
+ const {
+ allSidebarInfos,
+ lastUpdatedTime: lastUpdatedTimePromise,
+ lastUpdatedAtLeastTime,
+ lastUpdatedTimeIncludingSidebars:
+ lastUpdatedTimeIncludingSidebarsPromise,
+ lastUpdatedAtLeastTimeIncludingSidebars,
+ ...rest
+ } = createdChatThreadItem;
+ const [
+ lastUpdatedTime,
+ lastUpdatedTimeIncludingSidebars,
+ allSidebarItems,
+ ] = await Promise.all([
+ lastUpdatedTimePromise,
+ lastUpdatedTimeIncludingSidebarsPromise,
+ getAllFinalSidebarItems(allSidebarInfos),
+ ]);
+ const sidebars = getSidebarItems(allSidebarItems);
+ return {
+ ...rest,
+ lastUpdatedTime,
+ lastUpdatedTimeIncludingSidebars,
+ sidebars,
+ };
+ }),
+ );
+ if (createdChatThreadItems !== prevCreatedChatThreadItemsRef.current) {
+ // If these aren't equal, it indicates that the effect has fired again.
+ // We should discard this result as it is now outdated.
+ return;
+ }
+ // The callback below is basically
+ // setChatThreadItems(finalChatThreadItems), but it has extra logic to
+ // preserve objects if they are unchanged.
+ setChatThreadItems(prevChatThreadItems => {
+ if (prevChatThreadItems.length !== finalChatThreadItems.length) {
+ console.log(
+ 'unexpected: prevChatThreadItems.length !== ' +
+ 'finalChatThreadItems.length',
+ );
+ return finalChatThreadItems;
+ }
+ let somethingChanged = false;
+ const result: Array<ChatThreadItem> = [];
+ for (let i = 0; i < prevChatThreadItems.length; i++) {
+ const prevChatThreadItem = prevChatThreadItems[i];
+ const newChatThreadItem = finalChatThreadItems[i];
+ if (
+ prevChatThreadItem.threadInfo.id !== newChatThreadItem.threadInfo.id
+ ) {
+ console.log(
+ 'unexpected: prevChatThreadItem.threadInfo.id !== ' +
+ 'newChatThreadItem.threadInfo.id',
+ );
+ return finalChatThreadItems;
+ }
+ if (
+ prevChatThreadItem.lastUpdatedTime !==
+ newChatThreadItem.lastUpdatedTime ||
+ prevChatThreadItem.lastUpdatedTimeIncludingSidebars !==
+ newChatThreadItem.lastUpdatedTimeIncludingSidebars ||
+ prevChatThreadItem.sidebars.length !==
+ newChatThreadItem.sidebars.length
+ ) {
+ somethingChanged = true;
+ result[i] = newChatThreadItem;
+ continue;
+ }
+ const sidebarsMatching = prevChatThreadItem.sidebars.every(
+ (prevSidebar, j) => {
+ const newSidebar = newChatThreadItem.sidebars[j];
+ if (
+ newSidebar.type !== 'sidebar' ||
+ prevSidebar.type !== 'sidebar'
+ ) {
+ return newSidebar.type === prevSidebar.type;
+ }
+ return newSidebar.threadInfo.id === prevSidebar.threadInfo.id;
+ },
+ );
+ if (!sidebarsMatching) {
+ somethingChanged = true;
+ result[i] = newChatThreadItem;
+ continue;
+ }
+ result[i] = prevChatThreadItem;
+ }
+ if (somethingChanged) {
+ return result;
+ } else {
+ return prevChatThreadItems;
+ }
+ });
+ })();
+ }, [createdChatThreadItems, initialChatThreadItems]);
+
+ return React.useMemo(() => sortFunc(chatThreadItems), [chatThreadItems]);
}
export type RobotextChatMessageInfoItem = {
@@ -604,10 +766,10 @@
export {
messageInfoSelector,
- useCreateChatThreadItem,
createChatMessageItems,
messageListData,
useFlattenedChatListData,
useFilteredChatListData,
+ useChatThreadItems,
useMessageListData,
};
diff --git a/web/selectors/chat-selectors.js b/web/selectors/chat-selectors.js
--- a/web/selectors/chat-selectors.js
+++ b/web/selectors/chat-selectors.js
@@ -4,7 +4,7 @@
import {
type ChatThreadItem,
- useCreateChatThreadItem,
+ useChatThreadItems,
} from 'lib/selectors/chat-selectors.js';
import { threadInfoSelector } from 'lib/selectors/thread-selectors.js';
import { threadIsPending } from 'lib/shared/thread-utils.js';
@@ -13,13 +13,12 @@
import { useSelector } from '../redux/redux-utils.js';
function useChatThreadItem(threadInfo: ?ThreadInfo): ?ChatThreadItem {
- const createChatThreadItem = useCreateChatThreadItem();
- return React.useMemo(() => {
- if (!threadInfo) {
- return null;
- }
- return createChatThreadItem(threadInfo);
- }, [createChatThreadItem, threadInfo]);
+ const threadInfos = React.useMemo(
+ () => [threadInfo].filter(Boolean),
+ [threadInfo],
+ );
+ const [item] = useChatThreadItems(threadInfos);
+ return item;
}
function useActiveChatThreadItem(): ?ChatThreadItem {

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 23, 5:46 PM (2 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2571822
Default Alt Text
D13916.id45801.diff (10 KB)

Event Timeline