diff --git a/lib/utils/drawer-utils.react.js b/lib/utils/drawer-utils.react.js new file mode 100644 --- /dev/null +++ b/lib/utils/drawer-utils.react.js @@ -0,0 +1,84 @@ +// @flow + +import { threadIsChannel } from '../shared/thread-utils.js'; +import { + type ThreadInfo, + type ResolvedThreadInfo, + communitySubthreads, +} from '../types/thread-types.js'; + +export type CommunityDrawerItemData = { + +threadInfo: ThreadInfo, + +itemChildren?: $ReadOnlyArray>, + +hasSubchannelsButton: boolean, + +labelStyle: T, +}; + +function createRecursiveDrawerItemsData( + childThreadInfosMap: { +[id: string]: $ReadOnlyArray }, + communities: $ReadOnlyArray, + labelStyles: $ReadOnlyArray, + maxDepth: number, +): $ReadOnlyArray> { + const result = communities.map(community => ({ + threadInfo: community, + itemChildren: [], + labelStyle: labelStyles[0], + hasSubchannelsButton: false, + })); + let queue = result.map(item => [item, 0]); + + for (let i = 0; i < queue.length; i++) { + const [item, lvl] = queue[i]; + const itemChildThreadInfos = childThreadInfosMap[item.threadInfo.id] ?? []; + + if (lvl < maxDepth) { + item.itemChildren = itemChildThreadInfos + .filter(childItem => communitySubthreads.includes(childItem.type)) + .map(childItem => ({ + threadInfo: childItem, + itemChildren: [], + labelStyle: labelStyles[Math.min(lvl + 1, labelStyles.length - 1)], + hasSubchannelsButton: + lvl + 1 === maxDepth && + threadHasSubchannels(childItem, childThreadInfosMap), + })); + queue = queue.concat( + item.itemChildren.map(childItem => [childItem, lvl + 1]), + ); + } + } + return result; +} + +function threadHasSubchannels( + threadInfo: ThreadInfo, + childThreadInfosMap: { +[id: string]: $ReadOnlyArray }, +): boolean { + if (!childThreadInfosMap[threadInfo.id]?.length) { + return false; + } + return childThreadInfosMap[threadInfo.id].some(thread => + threadIsChannel(thread), + ); +} + +function appendSuffix( + chats: $ReadOnlyArray, +): ResolvedThreadInfo[] { + const result = []; + const names = new Map(); + + for (const chat of chats) { + let name = chat.uiName; + const numberOfOccurrences = names.get(name); + names.set(name, (numberOfOccurrences ?? 0) + 1); + if (numberOfOccurrences) { + name = `${name} (${numberOfOccurrences.toString()})`; + } + result.push({ ...chat, uiName: name }); + } + return result; +} + +export { createRecursiveDrawerItemsData, appendSuffix }; diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js --- a/native/navigation/community-drawer-content.react.js +++ b/native/navigation/community-drawer-content.react.js @@ -9,18 +9,15 @@ childThreadInfos, communityThreadSelector, } from 'lib/selectors/thread-selectors.js'; -import { threadIsChannel } from 'lib/shared/thread-utils.js'; import { - type ThreadInfo, - type ResolvedThreadInfo, - communitySubthreads, -} from 'lib/types/thread-types.js'; + createRecursiveDrawerItemsData, + appendSuffix, +} from 'lib/utils/drawer-utils.react.js'; import { useResolvedThreadInfos } from 'lib/utils/entity-helpers.js'; import CommunityDrawerItemCommunity from './community-drawer-item-community.react.js'; import { useNavigateToThread } from '../chat/message-list-types.js'; import { useStyles } from '../themes/colors.js'; -import type { TextStyle } from '../types/styles.js'; const maxDepth = 2; const safeAreaEdges = Platform.select({ @@ -49,23 +46,15 @@ }, []); const renderItem = React.useCallback( - ({ item }) => { - const itemData = { - threadInfo: item.threadInfo, - itemChildren: item.itemChildren, - labelStyle: item.labelStyle, - hasSubchannelsButton: item.subchannelsButton, - }; - return ( - - ); - }, + ({ item }) => ( + + ), [navigateToThread, openCommunity, setOpenCommunityOrClose], ); @@ -85,6 +74,7 @@ childThreadInfosMap, communitiesSuffixed, labelStyles, + maxDepth, ), [childThreadInfosMap, communitiesSuffixed, labelStyles], ); @@ -96,73 +86,6 @@ ); } -function createRecursiveDrawerItemsData( - childThreadInfosMap: { +[id: string]: $ReadOnlyArray }, - communities: $ReadOnlyArray, - labelStyles: $ReadOnlyArray, -) { - const result = communities.map(community => ({ - key: community.id, - threadInfo: community, - itemChildren: [], - labelStyle: labelStyles[0], - subchannelsButton: false, - })); - let queue = result.map(item => [item, 0]); - - for (let i = 0; i < queue.length; i++) { - const [item, lvl] = queue[i]; - const itemChildThreadInfos = childThreadInfosMap[item.threadInfo.id] ?? []; - - if (lvl < maxDepth) { - item.itemChildren = itemChildThreadInfos - .filter(childItem => communitySubthreads.includes(childItem.type)) - .map(childItem => ({ - threadInfo: childItem, - itemChildren: [], - labelStyle: labelStyles[Math.min(lvl + 1, labelStyles.length - 1)], - hasSubchannelsButton: - lvl + 1 === maxDepth && - threadHasSubchannels(childItem, childThreadInfosMap), - })); - queue = queue.concat( - item.itemChildren.map(childItem => [childItem, lvl + 1]), - ); - } - } - return result; -} - -function threadHasSubchannels( - threadInfo: ThreadInfo, - childThreadInfosMap: { +[id: string]: $ReadOnlyArray }, -) { - if (!childThreadInfosMap[threadInfo.id]?.length) { - return false; - } - return childThreadInfosMap[threadInfo.id].some(thread => - threadIsChannel(thread), - ); -} - -function appendSuffix( - chats: $ReadOnlyArray, -): ResolvedThreadInfo[] { - const result = []; - const names = new Map(); - - for (const chat of chats) { - let name = chat.uiName; - const numberOfOccurrences = names.get(name); - names.set(name, (numberOfOccurrences ?? 0) + 1); - if (numberOfOccurrences) { - name = `${name} (${numberOfOccurrences.toString()})`; - } - result.push({ ...chat, uiName: name }); - } - return result; -} - const unboundStyles = { drawerContent: { flex: 1, diff --git a/native/navigation/community-drawer-item.react.js b/native/navigation/community-drawer-item.react.js --- a/native/navigation/community-drawer-item.react.js +++ b/native/navigation/community-drawer-item.react.js @@ -3,7 +3,7 @@ import * as React from 'react'; import { View, FlatList, TouchableOpacity } from 'react-native'; -import type { ThreadInfo } from 'lib/types/thread-types.js'; +import type { CommunityDrawerItemData } from 'lib/utils/drawer-utils.react.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import { ExpandButton, ExpandButtonDisabled } from './expand-buttons.react.js'; @@ -13,15 +13,8 @@ import { useStyles } from '../themes/colors.js'; import type { TextStyle } from '../types/styles.js'; -export type CommunityDrawerItemData = { - +threadInfo: ThreadInfo, - +itemChildren?: $ReadOnlyArray, - +labelStyle: TextStyle, - +hasSubchannelsButton: boolean, -}; - export type DrawerItemProps = { - +itemData: CommunityDrawerItemData, + +itemData: CommunityDrawerItemData, +toggleExpanded: (threadID: string) => void, +expanded: boolean, +navigateToThread: (params: MessageListParams) => void, @@ -120,7 +113,7 @@ }; export type CommunityDrawerItemChatProps = { - +itemData: CommunityDrawerItemData, + +itemData: CommunityDrawerItemData, +navigateToThread: (params: MessageListParams) => void, };