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 @@ -15,6 +15,7 @@ childThreadInfos, communityThreadSelector, } from 'lib/selectors/thread-selectors.js'; +import { threadTypeIsCommunityRoot } from 'lib/types/thread-types-enum.js'; import { useDispatchActionPromise, useServerCall, @@ -25,11 +26,15 @@ } 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 CommunityDrawerItem from './community-drawer-item.react.js'; import { CommunityCreationRouteName } from './route-names.js'; import { useNavigateToThread } from '../chat/message-list-types.js'; import SWMansionIcon from '../components/swmansion-icon.react.js'; import { useStyles } from '../themes/colors.js'; +import { + flattenDrawerItemsData, + filterOutThreadAndDescendantIDs, +} from '../utils/drawer-utils.react.js'; const maxDepth = 2; const safeAreaEdges = Platform.select({ @@ -61,28 +66,17 @@ })(); }, [callFetchPrimaryLinks, dispatchActionPromise, drawerStatus]); - 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 }) => ( - - ), - [navigateToThread, openCommunity, setOpenCommunityOrClose], + const [expanded, setExpanded] = React.useState(() => { + if (communitiesSuffixed.length === 1) { + return new Set([communitiesSuffixed[0].id]); + } + return new Set(); + }); + + const setOpenCommunityOrClose = React.useCallback( + (id: string) => + expanded.has(id) ? setExpanded(new Set()) : setExpanded(new Set([id])), + [expanded], ); const labelStylesObj = useStyles(labelUnboundStyles); @@ -94,6 +88,7 @@ ], [labelStylesObj], ); + const childThreadInfosMap = useSelector(childThreadInfos); const drawerItemsData = React.useMemo( () => @@ -106,6 +101,43 @@ [childThreadInfosMap, communitiesSuffixed, labelStyles], ); + const toggleExpanded = React.useCallback( + (id: string) => + setExpanded(expandedState => { + if (expanded.has(id)) { + return new Set( + filterOutThreadAndDescendantIDs( + [...expandedState.values()], + drawerItemsData, + id, + ), + ); + } + return new Set([...expanded.values(), id]); + }), + [drawerItemsData, expanded], + ); + + const navigateToThread = useNavigateToThread(); + + const renderItem = React.useCallback( + ({ item }) => { + const isCommunity = threadTypeIsCommunityRoot(item.threadInfo.type); + return ( + + ); + }, + [expanded, navigateToThread, setOpenCommunityOrClose, toggleExpanded], + ); + const { navigate } = useNavigation(); const onPressCommunityCreation = React.useCallback(() => { navigate(CommunityCreationRouteName); @@ -126,9 +158,18 @@ ); + const flattenedDrawerItemsData = React.useMemo( + () => flattenDrawerItemsData(drawerItemsData, [...expanded.values()]), + [drawerItemsData, expanded], + ); + return ( - + {communityCreationButton} ); diff --git a/native/navigation/community-drawer-item-community.react.js b/native/navigation/community-drawer-item-community.react.js deleted file mode 100644 --- a/native/navigation/community-drawer-item-community.react.js +++ /dev/null @@ -1,44 +0,0 @@ -// @flow - -import * as React from 'react'; -import { View } from 'react-native'; - -import CommunityDrawerItem from './community-drawer-item.react.js'; -import type { DrawerItemProps } from './community-drawer-item.react.js'; -import { useStyles } from '../themes/colors.js'; - -function CommunityDrawerItemCommunity(props: DrawerItemProps): React.Node { - const styles = useStyles(unboundStyles); - - const style = React.useMemo( - () => - props.expanded - ? [styles.communityExpanded, styles.communityBase] - : styles.communityBase, - [props.expanded, styles.communityBase, styles.communityExpanded], - ); - - return ( - - - - ); -} - -const unboundStyles = { - communityBase: { - paddingVertical: 2, - paddingRight: 24, - paddingLeft: 8, - overflow: 'hidden', - }, - communityExpanded: { - backgroundColor: 'drawerOpenCommunityBackground', - borderTopRightRadius: 8, - borderBottomRightRadius: 8, - }, -}; - -const MemoizedCommunityDrawerItemCommunity: React.ComponentType = - React.memo(CommunityDrawerItemCommunity); -export default MemoizedCommunityDrawerItemCommunity; 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 @@ -1,9 +1,8 @@ // @flow import * as React from 'react'; -import { View, FlatList, TouchableOpacity } from 'react-native'; +import { View, TouchableOpacity } from 'react-native'; -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,68 +12,53 @@ import { SingleLine } from '../components/single-line.react.js'; import InviteLinksButton from '../invite-links/invite-links-button.react.js'; import { useStyles } from '../themes/colors.js'; -import type { TextStyle } from '../types/styles.js'; import { useShouldRenderAvatars } from '../utils/avatar-utils.js'; +import type { CommunityDrawerItemDataFlattened } from '../utils/drawer-utils.react.js'; export type DrawerItemProps = { - +itemData: CommunityDrawerItemData, + +itemData: CommunityDrawerItemDataFlattened, +toggleExpanded: (threadID: string) => void, - +expanded: boolean, + +isExpanded: boolean, +navigateToThread: (params: MessageListParams) => void, }; function CommunityDrawerItem(props: DrawerItemProps): React.Node { const { - itemData: { threadInfo, itemChildren, labelStyle, hasSubchannelsButton }, + itemData: { + threadInfo, + labelStyle, + hasSubchannelsButton, + hasChildren, + itemStyle, + }, navigateToThread, - expanded, + isExpanded, toggleExpanded, } = props; const styles = useStyles(unboundStyles); - const renderItem = React.useCallback( - ({ item }) => ( - - ), - [navigateToThread], - ); - - const children = React.useMemo(() => { - if (!expanded) { - return null; - } - if (hasSubchannelsButton) { + const subchannelsButton = React.useMemo(() => { + if (isExpanded && hasSubchannelsButton) { return ( ); } - return ; - }, [ - expanded, - itemChildren, - renderItem, - hasSubchannelsButton, - styles.subchannelsButton, - threadInfo, - ]); + return null; + }, [isExpanded, hasSubchannelsButton, styles.subchannelsButton, threadInfo]); const onExpandToggled = React.useCallback(() => { toggleExpanded(threadInfo.id); }, [toggleExpanded, threadInfo.id]); const itemExpandButton = React.useMemo(() => { - if (!itemChildren?.length && !hasSubchannelsButton) { + if (!hasChildren && !hasSubchannelsButton) { return ; } - return ; - }, [itemChildren?.length, hasSubchannelsButton, onExpandToggled, expanded]); + return ; + }, [hasChildren, hasSubchannelsButton, onExpandToggled, isExpanded]); const onPress = React.useCallback(() => { navigateToThread({ threadInfo }); @@ -96,8 +80,19 @@ ); }, [shouldRenderAvatars, styles.avatarContainer, threadInfo]); + const containerStyle = React.useMemo( + () => [ + styles.container, + { + paddingLeft: itemStyle.indentation, + }, + styles[itemStyle.background], + ], + [itemStyle.indentation, itemStyle.background, styles], + ); + return ( - + {itemExpandButton} - {children} + {subchannelsButton} ); } const unboundStyles = { + container: { + paddingRight: 24, + }, + none: { + paddingVertical: 2, + }, + beginning: { + backgroundColor: 'drawerOpenCommunityBackground', + borderTopRightRadius: 8, + paddingTop: 2, + }, + middle: { + backgroundColor: 'drawerOpenCommunityBackground', + paddingRight: 24, + }, + end: { + backgroundColor: 'drawerOpenCommunityBackground', + borderBottomRightRadius: 8, + paddingBottom: 2, + }, avatarContainer: { marginRight: 8, }, - chatView: { - marginLeft: 16, - }, threadEntry: { flexDirection: 'row', marginVertical: 6, @@ -137,34 +149,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);