diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js
index 843c7ece8..1d7e996d0 100644
--- a/native/navigation/community-drawer-content.react.js
+++ b/native/navigation/community-drawer-content.react.js
@@ -1,195 +1,236 @@
// @flow
import { useDrawerStatus } from '@react-navigation/drawer';
import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { FlatList, Platform, View, Text, TouchableOpacity } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useSelector } from 'react-redux';
import {
fetchPrimaryInviteLinkActionTypes,
fetchPrimaryInviteLinks,
} from 'lib/actions/link-actions.js';
import {
childThreadInfos,
communityThreadSelector,
} from 'lib/selectors/thread-selectors.js';
+import { threadTypeIsCommunityRoot } from 'lib/types/thread-types-enum.js';
import {
useDispatchActionPromise,
useServerCall,
} from 'lib/utils/action-utils.js';
import {
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 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({
ios: ['top'],
default: ['top', 'bottom'],
});
function CommunityDrawerContent(): React.Node {
const communities = useSelector(communityThreadSelector);
const resolvedCommunities = useResolvedThreadInfos(communities);
const communitiesSuffixed = React.useMemo(
() => appendSuffix(resolvedCommunities),
[resolvedCommunities],
);
const styles = useStyles(unboundStyles);
const callFetchPrimaryLinks = useServerCall(fetchPrimaryInviteLinks);
const dispatchActionPromise = useDispatchActionPromise();
const drawerStatus = useDrawerStatus();
React.useEffect(() => {
(async () => {
if (drawerStatus !== 'open') {
return;
}
await dispatchActionPromise(
fetchPrimaryInviteLinkActionTypes,
callFetchPrimaryLinks(),
);
})();
}, [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);
const labelStyles = React.useMemo(
() => [
labelStylesObj.level0Label,
labelStylesObj.level1Label,
labelStylesObj.level2Label,
],
[labelStylesObj],
);
+ const childThreadInfosMap = useSelector(childThreadInfos);
const drawerItemsData = React.useMemo(
() =>
createRecursiveDrawerItemsData(
childThreadInfosMap,
communitiesSuffixed,
labelStyles,
maxDepth,
),
[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);
}, [navigate]);
const communityCreationButton = (
Create community
);
+ const flattenedDrawerItemsData = React.useMemo(
+ () => flattenDrawerItemsData(drawerItemsData, [...expanded.values()]),
+ [drawerItemsData, expanded],
+ );
+
return (
-
+
{communityCreationButton}
);
}
const unboundStyles = {
drawerContent: {
flex: 1,
paddingRight: 8,
paddingTop: 8,
paddingBottom: 8,
},
communityCreationContainer: {
flexDirection: 'row',
padding: 24,
alignItems: 'center',
borderTopWidth: 1,
borderColor: 'panelSeparator',
},
communityCreationText: {
color: 'panelForegroundLabel',
fontSize: 16,
lineHeight: 24,
fontWeight: '500',
},
communityCreationIconContainer: {
height: 28,
width: 28,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 16,
marginRight: 12,
backgroundColor: 'panelSecondaryForeground',
},
communityCreationIcon: {
color: 'panelForegroundLabel',
},
};
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-community.react.js b/native/navigation/community-drawer-item-community.react.js
deleted file mode 100644
index 70f811e09..000000000
--- 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
index 9a9bc4cfc..b3bf0298d 100644
--- a/native/navigation/community-drawer-item.react.js
+++ b/native/navigation/community-drawer-item.react.js
@@ -1,171 +1,155 @@
// @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';
import SubchannelsButton from './subchannels-button.react.js';
import ThreadAvatar from '../avatars/thread-avatar.react.js';
import type { MessageListParams } from '../chat/message-list-types.js';
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 });
}, [navigateToThread, threadInfo]);
const { uiName } = useResolvedThreadInfo(threadInfo);
const shouldRenderAvatars = useShouldRenderAvatars();
const avatar = React.useMemo(() => {
if (!shouldRenderAvatars) {
return null;
}
return (
);
}, [shouldRenderAvatars, styles.avatarContainer, threadInfo]);
+ const containerStyle = React.useMemo(
+ () => [
+ styles.container,
+ {
+ paddingLeft: itemStyle.indentation,
+ },
+ styles[itemStyle.background],
+ ],
+ [itemStyle.indentation, itemStyle.background, styles],
+ );
+
return (
-
+
{itemExpandButton}
{avatar}
{uiName}
- {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,
},
textTouchableWrapper: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
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;