diff --git a/native/invite-links/invite-links-button.react.js b/native/invite-links/invite-links-button.react.js
new file mode 100644
index 000000000..bc96b0005
--- /dev/null
+++ b/native/invite-links/invite-links-button.react.js
@@ -0,0 +1,88 @@
+// @flow
+
+import { useActionSheet } from '@expo/react-native-action-sheet';
+import { useNavigation } from '@react-navigation/native';
+import * as React from 'react';
+import { TouchableOpacity } from 'react-native';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
+import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js';
+import type { ThreadInfo } from 'lib/types/thread-types.js';
+
+import SWMansionIcon from '../components/swmansion-icon.react.js';
+import {
+ InviteLinkNavigatorRouteName,
+ ViewInviteLinksRouteName,
+} from '../navigation/route-names.js';
+import { useSelector } from '../redux/redux-utils.js';
+import { useStyles } from '../themes/colors.js';
+
+type Props = {
+ +community: ThreadInfo,
+};
+
+function InviteLinksButton(props: Props): React.Node {
+ const { community } = props;
+ const inviteLink = useSelector(primaryInviteLinksSelector)[community.id];
+
+ const { navigate } = useNavigation();
+ const navigateToInviteLinksView = React.useCallback(() => {
+ if (!inviteLink || !community) {
+ return;
+ }
+ navigate<'InviteLinkNavigator'>(InviteLinkNavigatorRouteName, {
+ screen: ViewInviteLinksRouteName,
+ params: {
+ community,
+ },
+ });
+ }, [community, inviteLink, navigate]);
+
+ const insets = useSafeAreaInsets();
+ const activeTheme = useSelector(state => state.globalThemeInfo.activeTheme);
+ const styles = useStyles(unboundStyles);
+
+ const { showActionSheetWithOptions } = useActionSheet();
+ const options = React.useMemo(() => ['Invite Link', 'Cancel'], []);
+
+ const openActionSheet = React.useCallback(() => {
+ showActionSheetWithOptions(
+ {
+ options,
+ cancelButtonIndex: 1,
+ containerStyle: {
+ paddingBottom: insets.bottom,
+ },
+ userInterfaceStyle: activeTheme ?? 'dark',
+ },
+ selectedIndex => {
+ if (selectedIndex === 0) {
+ navigateToInviteLinksView();
+ }
+ },
+ );
+ }, [
+ activeTheme,
+ insets.bottom,
+ navigateToInviteLinksView,
+ options,
+ showActionSheetWithOptions,
+ ]);
+
+ if (!inviteLink) {
+ return null;
+ }
+ return (
+
+
+
+ );
+}
+
+const unboundStyles = {
+ button: {
+ color: 'drawerItemLabelLevel0',
+ },
+};
+
+export default InviteLinksButton;
diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js
index 7b79c6460..771d522c1 100644
--- a/native/navigation/community-drawer-content.react.js
+++ b/native/navigation/community-drawer-content.react.js
@@ -1,166 +1,190 @@
// @flow
+import { useDrawerStatus } from '@react-navigation/drawer';
import * as React from 'react';
import { FlatList, Platform, View, Text } 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 {
+ 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 { useNavigateToThread } from '../chat/message-list-types.js';
import SWMansionIcon from '../components/swmansion-icon.react.js';
import { useStyles } from '../themes/colors.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 labelStylesObj = useStyles(labelUnboundStyles);
const labelStyles = React.useMemo(
() => [
labelStylesObj.level0Label,
labelStylesObj.level1Label,
labelStylesObj.level2Label,
],
[labelStylesObj],
);
const drawerItemsData = React.useMemo(
() =>
createRecursiveDrawerItemsData(
childThreadInfosMap,
communitiesSuffixed,
labelStyles,
maxDepth,
),
[childThreadInfosMap, communitiesSuffixed, labelStyles],
);
const isCommunityCreationButtonEnabled = false;
let communityCreationButton;
if (isCommunityCreationButtonEnabled) {
communityCreationButton = (
Create community
);
}
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.react.js b/native/navigation/community-drawer-item.react.js
index 293b7d454..9a9bc4cfc 100644
--- a/native/navigation/community-drawer-item.react.js
+++ b/native/navigation/community-drawer-item.react.js
@@ -1,169 +1,171 @@
// @flow
import * as React from 'react';
import { View, FlatList, 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';
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);
const shouldRenderAvatars = useShouldRenderAvatars();
const avatar = React.useMemo(() => {
if (!shouldRenderAvatars) {
return null;
}
return (
);
}, [shouldRenderAvatars, styles.avatarContainer, threadInfo]);
return (
{itemExpandButton}
{avatar}
{uiName}
+
{children}
);
}
const unboundStyles = {
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;