Skip to content

Commit

Permalink
[lib][native][web] Update useSidebarInfos to include lastUpdatedAtLea…
Browse files Browse the repository at this point in the history
…stTime

Summary:
Before this diff, `useSidebarInfos` would return `lastUpdatedAtLeastTime` as `lastUpdatedTime`, as a temporary hack to help split up my work into smaller diffs.

In an earlier diff we updated `lastUpdatedTime` to be a `Promise`, and this allows us to avoid refactoring everything to handle the `Promise`.

In this diff, we address this temporary hack for sidebars, but for not for the `ChatThreadItem` itself (which will be handled later).

`useSidebarInfos` is used in places outside of `chat-selectors.js`, so this required some additional refactoring.

Depends on D13914

Test Plan: Tested in combination with the rest of the stack. See video in D13918

Reviewers: tomek

Reviewed By: tomek

Differential Revision: https://phab.comm.dev/D13915
  • Loading branch information
Ashoat committed Nov 13, 2024
1 parent 4816903 commit ed9e9cf
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 72 deletions.
77 changes: 74 additions & 3 deletions lib/hooks/search-threads.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import {
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 {
Expand All @@ -29,7 +34,11 @@ type SearchThreadsResult<U> = {
+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> {
Expand Down Expand Up @@ -93,10 +102,72 @@ const emptyArray: $ReadOnlyArray<SidebarInfo> = [];

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(
Expand Down
3 changes: 2 additions & 1 deletion lib/hooks/sidebar-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function useSidebarInfos(): { +[id: string]: $ReadOnlyArray<SidebarInfo> } {
) {
continue;
}
const { lastUpdatedAtLeastTime: lastUpdatedTime } = getLastUpdatedTimes(
const { lastUpdatedTime, lastUpdatedAtLeastTime } = getLastUpdatedTimes(
childThreadInfo,
messageStore,
messageStore.messages,
Expand All @@ -40,6 +40,7 @@ function useSidebarInfos(): { +[id: string]: $ReadOnlyArray<SidebarInfo> } {
sidebarInfos.push({
threadInfo: childThreadInfo,
lastUpdatedTime,
lastUpdatedAtLeastTime,
mostRecentNonLocalMessage,
});
}
Expand Down
23 changes: 11 additions & 12 deletions lib/selectors/chat-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
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';
Expand Down Expand Up @@ -92,30 +93,28 @@ function useCreateChatThreadItem(): ThreadInfo => ChatThreadItem {
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,
};
},
Expand Down
38 changes: 35 additions & 3 deletions lib/shared/sidebar-item-utils.js
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -65,4 +69,32 @@ function getSidebarItems(
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 };
6 changes: 3 additions & 3 deletions lib/types/thread-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,11 @@ export type LastUpdatedTimes = {
+lastUpdatedTime: Promise<number>,
};

export type SidebarInfo = {
export type SidebarInfo = $ReadOnly<{
...LastUpdatedTimes,
+threadInfo: ThreadInfo,
+lastUpdatedTime: number,
+mostRecentNonLocalMessage: ?string,
};
}>;

export type ToggleMessagePinRequest = {
+messageID: string,
Expand Down
3 changes: 1 addition & 2 deletions native/chat/chat-thread-list-item.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,9 @@ function ChatThreadListItem({
() =>
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}
Expand Down
33 changes: 15 additions & 18 deletions native/chat/chat-thread-list-sidebar.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,7 +15,7 @@ import ExtendedArrow from '../vectors/arrow-extended.react.js';
import Arrow from '../vectors/arrow.react.js';

type Props = {
+sidebarInfo: SidebarInfo,
+sidebarItem: SidebarThreadItem,
+onPressItem: (threadInfo: ThreadInfo) => void,
+onSwipeableWillOpen: (threadInfo: ThreadInfo) => void,
+currentlyOpenedSwipeableId: string,
Expand All @@ -26,14 +26,14 @@ function ChatThreadListSidebar(props: Props): React.Node {
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),
Expand All @@ -58,40 +58,37 @@ function ChatThreadListSidebar(props: Props): React.Node {
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,
],
);
Expand Down
8 changes: 4 additions & 4 deletions native/chat/sidebar-item.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
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';

import SingleLine from '../components/single-line.react.js';
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;
Expand Down
8 changes: 4 additions & 4 deletions native/chat/sidebar-list-modal.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as React from 'react';
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';
Expand Down Expand Up @@ -33,7 +33,7 @@ function SidebarListModal(props: Props): React.Node {
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;
Expand Down Expand Up @@ -64,7 +64,7 @@ function SidebarListModal(props: Props): React.Node {
}

function Item(props: {
item: SidebarInfo,
item: SidebarThreadItem,
onPressItem: (threadInfo: ThreadInfo) => void,
extendArrow: boolean,
}): React.Node {
Expand Down Expand Up @@ -106,7 +106,7 @@ function Item(props: {
{arrow}
<View style={styles.spacer} />
<View style={styles.sidebarItemContainer}>
<SidebarItem sidebarInfo={item} />
<SidebarItem sidebarItem={item} />
</View>
</View>
</Button>
Expand Down
Loading

0 comments on commit ed9e9cf

Please sign in to comment.