diff --git a/native/chat/settings/thread-settings.react.js b/native/chat/settings/thread-settings.react.js --- a/native/chat/settings/thread-settings.react.js +++ b/native/chat/settings/thread-settings.react.js @@ -19,6 +19,7 @@ removeUsersFromThreadActionTypes, } from 'lib/actions/thread-actions.js'; import { usePromoteSidebar } from 'lib/hooks/promote-sidebar.react.js'; +import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { childThreadInfos, @@ -40,7 +41,10 @@ } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import type { RelationshipButton } from 'lib/types/relationship-types.js'; import { threadPermissions } from 'lib/types/thread-permission-types.js'; -import { threadTypes } from 'lib/types/thread-types-enum.js'; +import { + threadTypes, + threadTypeIsCommunityRoot, +} from 'lib/types/thread-types-enum.js'; import type { UserInfos } from 'lib/types/user-types.js'; import { useResolvedOptionalThreadInfo, @@ -85,7 +89,12 @@ type OverlayContextType, } from '../../navigation/overlay-context.js'; import { + InviteLinkNavigatorRouteName, + ViewInviteLinksRouteName, + ManagePublicLinkRouteName, AddUsersModalRouteName, +} from '../../navigation/route-names.js'; +import { ComposeSubchannelModalRouteName, FullScreenThreadMediaGalleryRouteName, type NavigationRoute, @@ -284,6 +293,8 @@ +canAddMembers: boolean, +canLeaveThread: boolean, +canDeleteThread: boolean, + +canManageInviteLinks: boolean, + +inviteLinkExists: boolean, }; type State = { +numMembersShowing: number, @@ -1080,10 +1091,26 @@ }; onPressAddMember = () => { - this.props.navigation.navigate(AddUsersModalRouteName, { - presentedFrom: this.props.route.key, - threadInfo: this.props.threadInfo, - }); + if (this.props.inviteLinkExists) { + this.props.navigation.navigate(InviteLinkNavigatorRouteName, { + screen: ViewInviteLinksRouteName, + params: { + community: this.props.threadInfo, + }, + }); + } else if (this.props.canManageInviteLinks) { + this.props.navigation.navigate(InviteLinkNavigatorRouteName, { + screen: ManagePublicLinkRouteName, + params: { + community: this.props.threadInfo, + }, + }); + } else { + this.props.navigation.navigate(AddUsersModalRouteName, { + presentedFrom: this.props.route.key, + threadInfo: this.props.threadInfo, + }); + } }; onPressSeeMoreMembers = () => { @@ -1276,11 +1303,6 @@ threadPermissions.CREATE_SUBCHANNELS, ); - const canAddMembers = useThreadHasPermission( - threadInfo, - threadPermissions.ADD_MEMBERS, - ); - const canLeaveThread = useThreadHasPermission( threadInfo, threadPermissions.LEAVE_THREAD, @@ -1290,6 +1312,25 @@ threadInfo, threadPermissions.DELETE_THREAD, ); + + const isCommunityRoot = threadTypeIsCommunityRoot(threadInfo.type); + + const canManageLinks = useThreadHasPermission( + threadInfo, + threadPermissions.MANAGE_INVITE_LINKS, + ); + const inviteLink = useSelector(primaryInviteLinksSelector)[threadInfo.id]; + const canAddMembersViaInviteLink = + isCommunityRoot && (!!inviteLink || canManageLinks); + + const hasAddMembersPermission = useThreadHasPermission( + threadInfo, + threadPermissions.ADD_MEMBERS, + ); + const canAddMembersManually = hasAddMembersPermission && !isCommunityRoot; + + const canAddMembers = canAddMembersManually || canAddMembersViaInviteLink; + return ( ); }); diff --git a/native/invite-links/invite-links-navigator.react.js b/native/invite-links/invite-links-navigator.react.js --- a/native/invite-links/invite-links-navigator.react.js +++ b/native/invite-links/invite-links-navigator.react.js @@ -9,8 +9,10 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import ManagePublicLinkScreen from './manage-public-link-screen.react.js'; +import type { ManagePublicLinkScreenParams } from './manage-public-link-screen.react.js'; import ViewInviteLinksHeaderLeftButton from './view-invite-links-header-left-button.react.js'; import ViewInviteLinksScreen from './view-invite-links-screen.react.js'; +import type { ViewInviteLinksScreenParams } from './view-invite-links-screen.react.js'; import HeaderBackButton from '../navigation/header-back-button.react.js'; import { defaultStackScreenOptions } from '../navigation/options.js'; import type { RootNavigationProp } from '../navigation/root-navigator.react.js'; @@ -47,6 +49,16 @@ headerLeft: HeaderBackButton, }; +export type InviteLinksNavigatorParams = + | { + +screen: 'ViewInviteLinks', + +params: ViewInviteLinksScreenParams, + } + | { + +screen: 'ManagePublicLink', + +params: ManagePublicLinkScreenParams, + }; + type Props = { +navigation: RootNavigationProp<'InviteLinkNavigator'>, ... diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js --- a/native/navigation/route-names.js +++ b/native/navigation/route-names.js @@ -39,6 +39,7 @@ import type { CommunityCreationMembersScreenParams } from '../community-creation/community-creation-members.react.js'; import type { TagFarcasterChannelByNameParams } from '../community-settings/tag-farcaster-channel/tag-farcaster-channel-by-name.react.js'; import type { TagFarcasterChannelParams } from '../community-settings/tag-farcaster-channel/tag-farcaster-channel.react.js'; +import type { InviteLinksNavigatorParams } from '../invite-links/invite-links-navigator.react.js'; import type { ManagePublicLinkScreenParams } from '../invite-links/manage-public-link-screen.react.js'; import type { ViewInviteLinksScreenParams } from '../invite-links/view-invite-links-screen.react.js'; import type { ChatCameraModalParams } from '../media/chat-camera-modal.react.js'; @@ -180,7 +181,7 @@ +Registration: void, +CommunityCreation: void, +InviteLinkModal: InviteLinkModalParams, - +InviteLinkNavigator: void, + +InviteLinkNavigator: InviteLinksNavigatorParams, +RolesNavigator: void, +QRCodeSignInNavigator: void, +UserProfileBottomSheetNavigator: void,