Page MenuHomePhabricator

D13915.diff
No OneTemporary

D13915.diff

diff --git a/lib/hooks/search-threads.js b/lib/hooks/search-threads.js
--- a/lib/hooks/search-threads.js
+++ b/lib/hooks/search-threads.js
@@ -8,6 +8,11 @@
useFilteredChatListData,
} from '../selectors/chat-selectors.js';
import { useThreadSearchIndex } from '../selectors/nav-selectors.js';
+import {
+ type SidebarThreadItem,
+ getAllInitialSidebarItems,
+ getAllFinalSidebarItems,
+} from '../shared/sidebar-item-utils.js';
import { threadIsChannel } from '../shared/thread-utils.js';
import type { SetState } from '../types/hook-types.js';
import type {
@@ -29,7 +34,11 @@
+clearQuery: (event: SyntheticEvent<HTMLAnchorElement>) => void,
};
-function useSearchThreads<U: SidebarInfo | ChatThreadItem>(
+type ChildThreadInfos = {
+ +threadInfo: RawThreadInfo | ThreadInfo,
+ ...
+};
+function useSearchThreads<U: ChildThreadInfos>(
threadInfo: ThreadInfo,
childThreadInfos: $ReadOnlyArray<U>,
): SearchThreadsResult<U> {
@@ -93,10 +102,72 @@
function useSearchSidebars(
threadInfo: ThreadInfo,
-): SearchThreadsResult<SidebarInfo> {
+): SearchThreadsResult<SidebarThreadItem> {
const sidebarsByParentID = useSidebarInfos();
const childThreadInfos = sidebarsByParentID[threadInfo.id] ?? emptyArray;
- return useSearchThreads(threadInfo, childThreadInfos);
+ const initialSidebarItems = React.useMemo(
+ () => getAllInitialSidebarItems(childThreadInfos),
+ [childThreadInfos],
+ );
+ const [sidebarItems, setSidebarItems] =
+ React.useState<$ReadOnlyArray<SidebarThreadItem>>(initialSidebarItems);
+
+ const prevChildThreadInfosRef = React.useRef(childThreadInfos);
+ React.useEffect(() => {
+ if (childThreadInfos === prevChildThreadInfosRef.current) {
+ return;
+ }
+ prevChildThreadInfosRef.current = childThreadInfos;
+
+ setSidebarItems(initialSidebarItems);
+
+ void (async () => {
+ const finalSidebarItems = await getAllFinalSidebarItems(childThreadInfos);
+ if (childThreadInfos !== prevChildThreadInfosRef.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 setSidebarItems(finalSidebarItems), but
+ // it has extra logic to preserve objects if they are unchanged.
+ setSidebarItems(prevSidebarItems => {
+ if (prevSidebarItems.length !== finalSidebarItems.length) {
+ console.log(
+ 'unexpected: prevSidebarItems.length !== finalSidebarItems.length',
+ );
+ return finalSidebarItems;
+ }
+ let somethingChanged = false;
+ const result = [];
+ for (let i = 0; i < prevSidebarItems.length; i++) {
+ const prevSidebarItem = prevSidebarItems[i];
+ const newSidebarItem = finalSidebarItems[i];
+ if (prevSidebarItem.threadInfo.id !== newSidebarItem.threadInfo.id) {
+ console.log(
+ 'unexpected: prevSidebarItem.threadInfo.id !== ' +
+ 'newSidebarItem.threadInfo.id',
+ );
+ return finalSidebarItems;
+ }
+ if (
+ prevSidebarItem.lastUpdatedTime !== newSidebarItem.lastUpdatedTime
+ ) {
+ somethingChanged = true;
+ result[i] = newSidebarItem;
+ } else {
+ result[i] = prevSidebarItem;
+ }
+ }
+ if (somethingChanged) {
+ return result;
+ } else {
+ return prevSidebarItems;
+ }
+ });
+ })();
+ }, [childThreadInfos, initialSidebarItems]);
+
+ return useSearchThreads(threadInfo, sidebarItems);
}
function useSearchSubchannels(
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
@@ -28,7 +28,7 @@
) {
continue;
}
- const { lastUpdatedAtLeastTime: lastUpdatedTime } = getLastUpdatedTimes(
+ const { lastUpdatedTime, lastUpdatedAtLeastTime } = getLastUpdatedTimes(
childThreadInfo,
messageStore,
messageStore.messages,
@@ -40,6 +40,7 @@
sidebarInfos.push({
threadInfo: childThreadInfo,
lastUpdatedTime,
+ lastUpdatedAtLeastTime,
mostRecentNonLocalMessage,
});
}
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
@@ -25,6 +25,7 @@
import { messageSpecs } from '../shared/messages/message-specs.js';
import {
getSidebarItems,
+ getAllInitialSidebarItems,
type SidebarItem,
} from '../shared/sidebar-item-utils.js';
import { threadInChatList, threadIsPending } from '../shared/thread-utils.js';
@@ -92,30 +93,28 @@
messageStore,
);
- const { lastUpdatedAtLeastTime: lastUpdatedTime } = getLastUpdatedTimes(
+ const { lastUpdatedAtLeastTime } = getLastUpdatedTimes(
threadInfo,
messageStore,
messageInfos,
);
const sidebars = sidebarInfos[threadInfo.id] ?? [];
- const allSidebarItems = sidebars.map(sidebarInfo => ({
- type: 'sidebar',
- ...sidebarInfo,
- }));
- const lastUpdatedTimeIncludingSidebars =
- allSidebarItems.length > 0
- ? Math.max(lastUpdatedTime, allSidebarItems[0].lastUpdatedTime)
- : lastUpdatedTime;
+ const lastUpdatedAtLeastTimeIncludingSidebars =
+ sidebars.length > 0
+ ? Math.max(lastUpdatedAtLeastTime, sidebars[0].lastUpdatedAtLeastTime)
+ : lastUpdatedAtLeastTime;
- const sidebarItems = getSidebarItems(allSidebarItems);
+ const allInitialSidebarItems = getAllInitialSidebarItems(sidebars);
+ const sidebarItems = getSidebarItems(allInitialSidebarItems);
return {
type: 'chatThreadItem',
threadInfo,
mostRecentNonLocalMessage,
- lastUpdatedTime,
- lastUpdatedTimeIncludingSidebars,
+ lastUpdatedTime: lastUpdatedAtLeastTime,
+ lastUpdatedTimeIncludingSidebars:
+ lastUpdatedAtLeastTimeIncludingSidebars,
sidebars: sidebarItems,
};
},
diff --git a/lib/shared/sidebar-item-utils.js b/lib/shared/sidebar-item-utils.js
--- a/lib/shared/sidebar-item-utils.js
+++ b/lib/shared/sidebar-item-utils.js
@@ -1,10 +1,14 @@
// @flow
import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
-import { maxReadSidebars, maxUnreadSidebars } from '../types/thread-types.js';
+import {
+ maxReadSidebars,
+ maxUnreadSidebars,
+ type SidebarInfo,
+} from '../types/thread-types.js';
import { threeDays } from '../utils/date-utils.js';
-type SidebarThreadItem = {
+export type SidebarThreadItem = {
+type: 'sidebar',
+threadInfo: ThreadInfo,
+mostRecentNonLocalMessage: ?string,
@@ -65,4 +69,32 @@
return sidebarItems;
}
-export { getSidebarItems };
+function getAllInitialSidebarItems(
+ sidebarInfos: $ReadOnlyArray<SidebarInfo>,
+): SidebarThreadItem[] {
+ return sidebarInfos.map(sidebarItem => {
+ const { lastUpdatedTime, lastUpdatedAtLeastTime, ...rest } = sidebarItem;
+ return {
+ ...rest,
+ type: 'sidebar',
+ lastUpdatedTime: lastUpdatedAtLeastTime,
+ };
+ });
+}
+
+async function getAllFinalSidebarItems(
+ sidebarInfos: $ReadOnlyArray<SidebarInfo>,
+): Promise<$ReadOnlyArray<SidebarThreadItem>> {
+ const allSidebarItemPromises = sidebarInfos.map(async sidebarItem => {
+ const { lastUpdatedTime, lastUpdatedAtLeastTime, ...rest } = sidebarItem;
+ const finalLastUpdatedTime = await lastUpdatedTime;
+ return {
+ ...rest,
+ type: 'sidebar',
+ lastUpdatedTime: finalLastUpdatedTime,
+ };
+ });
+ return await Promise.all(allSidebarItemPromises);
+}
+
+export { getSidebarItems, getAllInitialSidebarItems, getAllFinalSidebarItems };
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
@@ -443,11 +443,11 @@
+lastUpdatedTime: Promise<number>,
};
-export type SidebarInfo = {
+export type SidebarInfo = $ReadOnly<{
+ ...LastUpdatedTimes,
+threadInfo: ThreadInfo,
- +lastUpdatedTime: number,
+mostRecentNonLocalMessage: ?string,
-};
+}>;
export type ToggleMessagePinRequest = {
+messageID: string,
diff --git a/native/chat/chat-thread-list-item.react.js b/native/chat/chat-thread-list-item.react.js
--- a/native/chat/chat-thread-list-item.react.js
+++ b/native/chat/chat-thread-list-item.react.js
@@ -50,10 +50,9 @@
() =>
data.sidebars.map((sidebarItem, index) => {
if (sidebarItem.type === 'sidebar') {
- const { type, ...sidebarInfo } = sidebarItem;
return (
<ChatThreadListSidebar
- sidebarInfo={sidebarInfo}
+ sidebarItem={sidebarItem}
onPressItem={onPressItem}
onSwipeableWillOpen={onSwipeableWillOpen}
currentlyOpenedSwipeableId={currentlyOpenedSwipeableId}
diff --git a/native/chat/chat-thread-list-sidebar.react.js b/native/chat/chat-thread-list-sidebar.react.js
--- a/native/chat/chat-thread-list-sidebar.react.js
+++ b/native/chat/chat-thread-list-sidebar.react.js
@@ -3,8 +3,8 @@
import * as React from 'react';
import { View } from 'react-native';
+import type { SidebarThreadItem } from 'lib/shared/sidebar-item-utils.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
-import type { SidebarInfo } from 'lib/types/thread-types.js';
import { sidebarHeight, SidebarItem } from './sidebar-item.react.js';
import SwipeableThread from './swipeable-thread.react.js';
@@ -15,7 +15,7 @@
import Arrow from '../vectors/arrow.react.js';
type Props = {
- +sidebarInfo: SidebarInfo,
+ +sidebarItem: SidebarThreadItem,
+onPressItem: (threadInfo: ThreadInfo) => void,
+onSwipeableWillOpen: (threadInfo: ThreadInfo) => void,
+currentlyOpenedSwipeableId: string,
@@ -26,14 +26,14 @@
const styles = useStyles(unboundStyles);
const {
- sidebarInfo,
+ sidebarItem,
onSwipeableWillOpen,
currentlyOpenedSwipeableId,
onPressItem,
extendArrow = false,
} = props;
- const { threadInfo } = sidebarInfo;
+ const { threadInfo } = sidebarItem;
const onPress = React.useCallback(
() => onPressItem(threadInfo),
@@ -58,40 +58,37 @@
const unreadIndicator = React.useMemo(
() => (
<View style={styles.unreadIndicatorContainer}>
- <UnreadDot unread={sidebarInfo.threadInfo.currentUser.unread} />
+ <UnreadDot unread={threadInfo.currentUser.unread} />
</View>
),
- [
- sidebarInfo.threadInfo.currentUser.unread,
- styles.unreadIndicatorContainer,
- ],
+ [threadInfo.currentUser.unread, styles.unreadIndicatorContainer],
);
- const sidebarItem = React.useMemo(
- () => <SidebarItem sidebarInfo={sidebarInfo} />,
- [sidebarInfo],
+ const sidebarItemElement = React.useMemo(
+ () => <SidebarItem sidebarItem={sidebarItem} />,
+ [sidebarItem],
);
const swipeableThread = React.useMemo(
() => (
<View style={styles.swipeableThreadContainer}>
<SwipeableThread
- threadInfo={sidebarInfo.threadInfo}
- mostRecentNonLocalMessage={sidebarInfo.mostRecentNonLocalMessage}
+ threadInfo={threadInfo}
+ mostRecentNonLocalMessage={sidebarItem.mostRecentNonLocalMessage}
onSwipeableWillOpen={onSwipeableWillOpen}
currentlyOpenedSwipeableId={currentlyOpenedSwipeableId}
iconSize={16}
>
- {sidebarItem}
+ {sidebarItemElement}
</SwipeableThread>
</View>
),
[
currentlyOpenedSwipeableId,
onSwipeableWillOpen,
- sidebarInfo.mostRecentNonLocalMessage,
- sidebarInfo.threadInfo,
- sidebarItem,
+ sidebarItem.mostRecentNonLocalMessage,
+ threadInfo,
+ sidebarItemElement,
styles.swipeableThreadContainer,
],
);
diff --git a/native/chat/sidebar-item.react.js b/native/chat/sidebar-item.react.js
--- a/native/chat/sidebar-item.react.js
+++ b/native/chat/sidebar-item.react.js
@@ -3,7 +3,7 @@
import * as React from 'react';
import { Text, View } from 'react-native';
-import type { SidebarInfo } from 'lib/types/thread-types.js';
+import type { SidebarThreadItem } from 'lib/shared/sidebar-item-utils.js';
import { shortAbsoluteDate } from 'lib/utils/date-utils.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
@@ -11,17 +11,17 @@
import { useStyles } from '../themes/colors.js';
type Props = {
- +sidebarInfo: SidebarInfo,
+ +sidebarItem: SidebarThreadItem,
};
function SidebarItem(props: Props): React.Node {
- const { lastUpdatedTime } = props.sidebarInfo;
+ const { lastUpdatedTime } = props.sidebarItem;
const lastActivity = React.useMemo(
() => shortAbsoluteDate(lastUpdatedTime),
[lastUpdatedTime],
);
- const { threadInfo } = props.sidebarInfo;
+ const { threadInfo } = props.sidebarItem;
const { uiName } = useResolvedThreadInfo(threadInfo);
const styles = useStyles(unboundStyles);
const unreadStyle = threadInfo.currentUser.unread ? styles.unread : null;
diff --git a/native/chat/sidebar-list-modal.react.js b/native/chat/sidebar-list-modal.react.js
--- a/native/chat/sidebar-list-modal.react.js
+++ b/native/chat/sidebar-list-modal.react.js
@@ -4,8 +4,8 @@
import { View } from 'react-native';
import { useSearchSidebars } from 'lib/hooks/search-threads.js';
+import type { SidebarThreadItem } from 'lib/shared/sidebar-item-utils.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
-import type { SidebarInfo } from 'lib/types/thread-types.js';
import { SidebarItem } from './sidebar-item.react.js';
import ThreadListModal from './thread-list-modal.react.js';
@@ -33,7 +33,7 @@
const createRenderItem = React.useCallback(
(onPressItem: (threadInfo: ThreadInfo) => void) =>
// eslint-disable-next-line react/display-name
- (row: { +item: SidebarInfo, +index: number, ... }) => {
+ (row: { +item: SidebarThreadItem, +index: number, ... }) => {
let extendArrow: boolean = false;
if (row.index < numOfSidebarsWithExtendedArrow) {
extendArrow = true;
@@ -64,7 +64,7 @@
}
function Item(props: {
- item: SidebarInfo,
+ item: SidebarThreadItem,
onPressItem: (threadInfo: ThreadInfo) => void,
extendArrow: boolean,
}): React.Node {
@@ -106,7 +106,7 @@
{arrow}
<View style={styles.spacer} />
<View style={styles.sidebarItemContainer}>
- <SidebarItem sidebarInfo={item} />
+ <SidebarItem sidebarItem={item} />
</View>
</View>
</Button>
diff --git a/native/chat/thread-list-modal.react.js b/native/chat/thread-list-modal.react.js
--- a/native/chat/thread-list-modal.react.js
+++ b/native/chat/thread-list-modal.react.js
@@ -11,10 +11,11 @@
} from 'react-native';
import type { ThreadSearchState } from 'lib/hooks/search-threads.js';
-import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js';
import type { SetState } from 'lib/types/hook-types.js';
-import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
-import type { SidebarInfo } from 'lib/types/thread-types.js';
+import type {
+ ThreadInfo,
+ RawThreadInfo,
+} from 'lib/types/minimally-encoded-thread-permissions-types.js';
import { useNavigateToThread } from './message-list-types.js';
import Modal from '../components/modal.react.js';
@@ -24,13 +25,14 @@
import { useIndicatorStyle, useStyles } from '../themes/colors.js';
import { waitForModalInputFocus } from '../utils/timers.js';
-function keyExtractor(sidebarInfo: SidebarInfo | ChatThreadItem) {
+type ChatItem = {
+ +threadInfo: RawThreadInfo | ThreadInfo,
+ ...
+};
+function keyExtractor(sidebarInfo: ChatItem) {
return sidebarInfo.threadInfo.id;
}
-function getItemLayout(
- data: ?$ReadOnlyArray<SidebarInfo | ChatThreadItem>,
- index: number,
-) {
+function getItemLayout(data: ?$ReadOnlyArray<ChatItem>, index: number) {
return { length: 24, offset: 24 * index, index };
}
@@ -46,9 +48,7 @@
+searchPlaceholder?: string,
+modalTitle: string,
};
-function ThreadListModal<U: SidebarInfo | ChatThreadItem>(
- props: Props<U>,
-): React.Node {
+function ThreadListModal<U: ChatItem>(props: Props<U>): React.Node {
const {
threadInfo: parentThreadInfo,
searchState,
diff --git a/web/chat/chat-thread-list-item.react.js b/web/chat/chat-thread-list-item.react.js
--- a/web/chat/chat-thread-list-item.react.js
+++ b/web/chat/chat-thread-list-item.react.js
@@ -90,12 +90,11 @@
const sidebars = item.sidebars.map((sidebarItem, index) => {
if (sidebarItem.type === 'sidebar') {
- const { type, ...sidebarInfo } = sidebarItem;
return (
<ChatThreadListSidebar
- sidebarInfo={sidebarInfo}
+ sidebarItem={sidebarItem}
isSubsequentItem={index > 0}
- key={sidebarInfo.threadInfo.id}
+ key={sidebarItem.threadInfo.id}
/>
);
} else if (sidebarItem.type === 'seeMore') {
diff --git a/web/chat/chat-thread-list-sidebar.react.js b/web/chat/chat-thread-list-sidebar.react.js
--- a/web/chat/chat-thread-list-sidebar.react.js
+++ b/web/chat/chat-thread-list-sidebar.react.js
@@ -3,7 +3,7 @@
import classNames from 'classnames';
import * as React from 'react';
-import type { SidebarInfo } from 'lib/types/thread-types.js';
+import type { SidebarThreadItem } from 'lib/shared/sidebar-item-utils.js';
import ChatThreadListItemMenu from './chat-thread-list-item-menu.react.js';
import css from './chat-thread-list.css';
@@ -11,12 +11,12 @@
import { useThreadIsActive } from '../selectors/thread-selectors.js';
type Props = {
- +sidebarInfo: SidebarInfo,
+ +sidebarItem: SidebarThreadItem,
+isSubsequentItem: boolean,
};
function ChatThreadListSidebar(props: Props): React.Node {
- const { sidebarInfo, isSubsequentItem } = props;
- const { threadInfo, mostRecentNonLocalMessage } = sidebarInfo;
+ const { sidebarItem, isSubsequentItem } = props;
+ const { threadInfo, mostRecentNonLocalMessage } = sidebarItem;
const {
currentUser: { unread },
id: threadID,
@@ -35,7 +35,7 @@
})}
>
<div className={css.dotContainer}>{unreadDot}</div>
- <SidebarItem sidebarInfo={sidebarInfo} extendArrow={isSubsequentItem} />
+ <SidebarItem sidebarItem={sidebarItem} extendArrow={isSubsequentItem} />
<ChatThreadListItemMenu
threadInfo={threadInfo}
mostRecentNonLocalMessage={mostRecentNonLocalMessage}
diff --git a/web/chat/sidebar-item.react.js b/web/chat/sidebar-item.react.js
--- a/web/chat/sidebar-item.react.js
+++ b/web/chat/sidebar-item.react.js
@@ -3,19 +3,19 @@
import classNames from 'classnames';
import * as React from 'react';
-import type { SidebarInfo } from 'lib/types/thread-types.js';
+import type { SidebarThreadItem } from 'lib/shared/sidebar-item-utils.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
import css from './chat-thread-list.css';
import { useOnClickThread } from '../selectors/thread-selectors.js';
type Props = {
- +sidebarInfo: SidebarInfo,
+ +sidebarItem: SidebarThreadItem,
+extendArrow?: boolean,
};
function SidebarItem(props: Props): React.Node {
const {
- sidebarInfo: { threadInfo },
+ sidebarItem: { threadInfo },
extendArrow = false,
} = props;
const {

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 24, 1:40 AM (19 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2573432
Default Alt Text
D13915.diff (19 KB)

Event Timeline