Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3328914
D13915.id45764.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D13915.id45764.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 21, 4:18 PM (10 h, 33 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2507104
Default Alt Text
D13915.id45764.diff (19 KB)
Attached To
Mode
D13915: [lib][native][web] Update useSidebarInfos to include lastUpdatedAtLeastTime
Attached
Detach File
Event Timeline
Log In to Comment