diff --git a/native/chat/chat-thread-list-utils.js b/native/chat/chat-thread-list-utils.js new file mode 100644 --- /dev/null +++ b/native/chat/chat-thread-list-utils.js @@ -0,0 +1,58 @@ +// @flow + +import _sum from 'lodash/fp/sum.js'; +import { Platform } from 'react-native'; + +import { + chatThreadListItemHeight, + spacerHeight, +} from './chat-thread-list-item.react.js'; +import type { Item } from './chat-thread-list.react.js'; +import { sidebarHeight } from './sidebar-item.react.js'; + +function keyExtractor(item: Item): string { + if (item.type === 'chatThreadItem') { + return item.threadInfo.id; + } else if (item.type === 'empty') { + return 'empty'; + } + return 'search'; +} + +function itemHeight(item: Item): number { + if (item.type === 'search') { + return Platform.OS === 'ios' ? 54.5 : 55; + } + + // itemHeight for emptyItem might be wrong because of line wrapping + // but we don't care because we'll only ever be rendering this item + // by itself and it should always be on-screen + if (item.type === 'empty') { + return 123; + } + + const baseHeight = chatThreadListItemHeight; + const sidebarsHeight = item.sidebars.length * sidebarHeight; + const spacerHeightAdjustment = item.sidebars.length > 0 ? spacerHeight : 0; + + return baseHeight + sidebarsHeight + spacerHeightAdjustment; +} + +function heightOfItems(data: $ReadOnlyArray): number { + return _sum(data.map(itemHeight)); +} + +function getItemLayout( + data: ?$ReadOnlyArray, + index: number, +): { length: number, offset: number, index: number } { + if (!data) { + return { length: 0, offset: 0, index }; + } + const offset = heightOfItems(data.filter((_, i): boolean => i < index)); + const item = data[index]; + const length = item ? itemHeight(item) : 0; + return { length, offset, index }; +} + +export { keyExtractor, itemHeight, heightOfItems, getItemLayout }; diff --git a/native/chat/chat-thread-list.react.js b/native/chat/chat-thread-list.react.js --- a/native/chat/chat-thread-list.react.js +++ b/native/chat/chat-thread-list.react.js @@ -2,7 +2,6 @@ import IonIcon from '@expo/vector-icons/Ionicons.js'; import invariant from 'invariant'; -import _sum from 'lodash/fp/sum.js'; import * as React from 'react'; import { View, @@ -39,11 +38,8 @@ } from 'lib/types/user-types.js'; import { useServerCall } from 'lib/utils/action-utils.js'; -import { - ChatThreadListItem, - chatThreadListItemHeight, - spacerHeight, -} from './chat-thread-list-item.react.js'; +import { ChatThreadListItem } from './chat-thread-list-item.react.js'; +import { getItemLayout, keyExtractor } from './chat-thread-list-utils.js'; import type { ChatTopTabsNavigationProp, ChatNavigationProp, @@ -52,7 +48,6 @@ type MessageListParams, useNavigateToThread, } from './message-list-types.js'; -import { sidebarHeight } from './sidebar-item.react.js'; import Button from '../components/button.react.js'; import Search from '../components/search.react.js'; import { @@ -85,7 +80,7 @@ const { Value, Node, interpolateNode, useValue } = Animated; /* eslint-enable import/no-named-as-default-member */ -type Item = +export type Item = | ChatThreadItem | { +type: 'search', +searchText: string } | { +type: 'empty', +emptyItem: React.ComponentType<{}> }; @@ -326,52 +321,6 @@ ); }; - static keyExtractor = (item: Item) => { - if (item.type === 'chatThreadItem') { - return item.threadInfo.id; - } else if (item.type === 'empty') { - return 'empty'; - } else { - return 'search'; - } - }; - - static getItemLayout = (data: ?$ReadOnlyArray, index: number) => { - if (!data) { - return { length: 0, offset: 0, index }; - } - const offset = ChatThreadList.heightOfItems( - data.filter((_, i) => i < index), - ); - const item = data[index]; - const length = item ? ChatThreadList.itemHeight(item) : 0; - return { length, offset, index }; - }; - - static itemHeight = (item: Item) => { - if (item.type === 'search') { - return Platform.OS === 'ios' ? 54.5 : 55; - } - - // itemHeight for emptyItem might be wrong because of line wrapping - // but we don't care because we'll only ever be rendering this item - // by itself and it should always be on-screen - if (item.type === 'empty') { - return 123; - } - - let height = chatThreadListItemHeight; - height += item.sidebars.length * sidebarHeight; - if (item.sidebars.length > 0) { - height += spacerHeight; - } - return height; - }; - - static heightOfItems(data: $ReadOnlyArray): number { - return _sum(data.map(ChatThreadList.itemHeight)); - } - listDataSelector = createSelector( (props: Props) => props.chatListData, (props: Props) => props.searchStatus, @@ -465,8 +414,8 @@