diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js
index d06e12fe1..ce59bccdc 100644
--- a/native/navigation/community-drawer-content.react.js
+++ b/native/navigation/community-drawer-content.react.js
@@ -1,169 +1,187 @@
// @flow
import * as React from 'react';
import { FlatList } 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 { useNavigateToThread } from '../chat/message-list-types';
import { useStyles } from '../themes/colors';
import type { TextStyle } from '../types/styles';
import CommunityDrawerItemCommunity from './community-drawer-item-cummunity.react';
const maxDepth = 2;
const safeAreaEdges = ['top'];
function CommunityDrawerContent(): React.Node {
const communities = useSelector(communityThreadSelector);
const communitiesSuffixed = React.useMemo(() => appendSuffix(communities), [
communities,
]);
const styles = useStyles(unboundStyles);
const [openCommunity, setOpenCommunity] = React.useState(
communitiesSuffixed.length === 1 ? communitiesSuffixed[0].id : null,
);
const navigateToThread = useNavigateToThread();
const childThreadInfosMap = useSelector(childThreadInfos);
const setOpenCommunnityOrClose = 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, setOpenCommunnityOrClose],
);
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,
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[] {
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: 'drawerBackgroud',
},
};
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 ecc0cd4f1..cb0b78b0e 100644
--- a/native/navigation/community-drawer-item.react.js
+++ b/native/navigation/community-drawer-item.react.js
@@ -1,133 +1,153 @@
// @flow
import * as React from 'react';
import { View, FlatList, TouchableOpacity } from 'react-native';
import type { ThreadInfo } from 'lib/types/thread-types';
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 },
+ 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]);
+ }, [
+ expanded,
+ itemChildren,
+ renderItem,
+ hasSubchannelsButton,
+ styles.subchannelsButton,
+ threadInfo,
+ ]);
const onExpandToggled = React.useCallback(() => {
toggleExpanded(threadInfo.id);
}, [toggleExpanded, threadInfo.id]);
const itemExpandButton = React.useMemo(() => {
- if (!itemChildren?.length) {
+ if (!itemChildren?.length && !hasSubchannelsButton) {
return ;
}
return ;
- }, [itemChildren?.length, expanded, onExpandToggled]);
+ }, [itemChildren?.length, hasSubchannelsButton, onExpandToggled, expanded]);
const onPress = React.useCallback(() => {
navigateToThread({ threadInfo });
}, [navigateToThread, threadInfo]);
return (
{itemExpandButton}
{threadInfo.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;