diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js index 824f5ea31..985d557f3 100644 --- a/native/navigation/community-drawer-content.react.js +++ b/native/navigation/community-drawer-content.react.js @@ -1,190 +1,199 @@ // @flow import * as React from 'react'; import { FlatList, Platform } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useSelector } from 'react-redux'; import { childThreadInfos, communityThreadSelector, } from 'lib/selectors/thread-selectors'; import { threadIsChannel } from 'lib/shared/thread-utils'; -import { type ThreadInfo, communitySubthreads } from 'lib/types/thread-types'; +import { + type ThreadInfo, + type ResolvedThreadInfo, + communitySubthreads, +} from 'lib/types/thread-types'; +import { useResolvedThreadInfos } from 'lib/utils/entity-helpers'; import { useNavigateToThread } from '../chat/message-list-types'; import { useStyles } from '../themes/colors'; import type { TextStyle } from '../types/styles'; import CommunityDrawerItemCommunity from './community-drawer-item-community.react'; const maxDepth = 2; const safeAreaEdges = Platform.select({ ios: ['top'], default: ['top', 'bottom'], }); function CommunityDrawerContent(): React.Node { const communities = useSelector(communityThreadSelector); - const communitiesSuffixed = React.useMemo(() => appendSuffix(communities), [ - communities, - ]); + const resolvedCommunities = useResolvedThreadInfos(communities); + const communitiesSuffixed = React.useMemo( + () => appendSuffix(resolvedCommunities), + [resolvedCommunities], + ); const styles = useStyles(unboundStyles); const [openCommunity, setOpenCommunity] = React.useState( communitiesSuffixed.length === 1 ? communitiesSuffixed[0].id : null, ); const navigateToThread = useNavigateToThread(); const childThreadInfosMap = useSelector(childThreadInfos); const setOpenCommunityOrClose = React.useCallback((index: string) => { setOpenCommunity(open => (open === index ? null : index)); }, []); const renderItem = React.useCallback( ({ item }) => { const itemData = { threadInfo: item.threadInfo, itemChildren: item.itemChildren, labelStyle: item.labelStyle, hasSubchannelsButton: item.subchannelsButton, }; return ( ); }, [navigateToThread, openCommunity, setOpenCommunityOrClose], ); const labelStylesObj = useStyles(labelUnboundStyles); const labelStyles = React.useMemo( () => [ labelStylesObj.level0Label, labelStylesObj.level1Label, labelStylesObj.level2Label, ], [labelStylesObj], ); const drawerItemsData = React.useMemo( () => createRecursiveDrawerItemsData( childThreadInfosMap, communitiesSuffixed, labelStyles, ), [childThreadInfosMap, communitiesSuffixed, labelStyles], ); return ( ); } function createRecursiveDrawerItemsData( childThreadInfosMap: { +[id: string]: $ReadOnlyArray }, - communities: $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): ThreadInfo[] { +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, paddingRight: 8, paddingTop: 8, backgroundColor: 'drawerBackground', }, }; const labelUnboundStyles = { level0Label: { color: 'drawerItemLabelLevel0', fontSize: 16, lineHeight: 24, fontWeight: '500', }, level1Label: { color: 'drawerItemLabelLevel1', fontSize: 14, lineHeight: 22, fontWeight: '500', }, level2Label: { color: 'drawerItemLabelLevel2', fontSize: 14, lineHeight: 22, fontWeight: '400', }, }; const MemoizedCommunityDrawerContent: React.ComponentType<{}> = React.memo( CommunityDrawerContent, ); export default MemoizedCommunityDrawerContent; diff --git a/native/navigation/community-drawer-item.react.js b/native/navigation/community-drawer-item.react.js index cb0b78b0e..7c5cebb6c 100644 --- a/native/navigation/community-drawer-item.react.js +++ b/native/navigation/community-drawer-item.react.js @@ -1,153 +1,155 @@ // @flow import * as React from 'react'; import { View, FlatList, TouchableOpacity } from 'react-native'; import type { ThreadInfo } from 'lib/types/thread-types'; +import { useResolvedThreadInfo } from 'lib/utils/entity-helpers'; import type { MessageListParams } from '../chat/message-list-types'; import { SingleLine } from '../components/single-line.react'; import { useStyles } from '../themes/colors'; import type { TextStyle } from '../types/styles'; import { ExpandButton, ExpandButtonDisabled } from './expand-buttons.react'; import SubchannelsButton from './subchannels-button.react'; export type CommunityDrawerItemData = { +threadInfo: ThreadInfo, +itemChildren?: $ReadOnlyArray, +labelStyle: TextStyle, +hasSubchannelsButton: boolean, }; export type DrawerItemProps = { +itemData: CommunityDrawerItemData, +toggleExpanded: (threadID: string) => void, +expanded: boolean, +navigateToThread: (params: MessageListParams) => void, }; function CommunityDrawerItem(props: DrawerItemProps): React.Node { const { itemData: { threadInfo, itemChildren, labelStyle, hasSubchannelsButton }, navigateToThread, expanded, toggleExpanded, } = props; const styles = useStyles(unboundStyles); const renderItem = React.useCallback( ({ item }) => ( ), [navigateToThread], ); const children = React.useMemo(() => { if (!expanded) { return null; } if (hasSubchannelsButton) { return ( ); } return ; }, [ expanded, itemChildren, renderItem, hasSubchannelsButton, styles.subchannelsButton, threadInfo, ]); const onExpandToggled = React.useCallback(() => { toggleExpanded(threadInfo.id); }, [toggleExpanded, threadInfo.id]); const itemExpandButton = React.useMemo(() => { if (!itemChildren?.length && !hasSubchannelsButton) { return ; } return ; }, [itemChildren?.length, hasSubchannelsButton, onExpandToggled, expanded]); const onPress = React.useCallback(() => { navigateToThread({ threadInfo }); }, [navigateToThread, threadInfo]); + const { uiName } = useResolvedThreadInfo(threadInfo); return ( {itemExpandButton} - {threadInfo.uiName} + {uiName} {children} ); } const unboundStyles = { chatView: { marginLeft: 16, }, threadEntry: { flexDirection: 'row', marginVertical: 6, }, textTouchableWrapper: { flex: 1, }, subchannelsButton: { marginLeft: 24, marginBottom: 6, }, }; export type CommunityDrawerItemChatProps = { +itemData: CommunityDrawerItemData, +navigateToThread: (params: MessageListParams) => void, }; function CommunityDrawerItemChat( props: CommunityDrawerItemChatProps, ): React.Node { const [expanded, setExpanded] = React.useState(false); const styles = useStyles(unboundStyles); const toggleExpanded = React.useCallback(() => { setExpanded(isExpanded => !isExpanded); }, []); return ( ); } const MemoizedCommunityDrawerItemChat: React.ComponentType = React.memo( CommunityDrawerItemChat, ); const MemoizedCommunityDrawerItem: React.ComponentType = React.memo( CommunityDrawerItem, ); export default MemoizedCommunityDrawerItem;