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 @@ -271,6 +271,7 @@ export type UserProfileBottomSheetParamList = { +UserProfileBottomSheet: UserProfileBottomSheetParams, +UserProfileAvatarModal: UserProfileAvatarModalParams, + +UserRelationshipTooltipModal: UserRelationshipTooltipModalParams, }; export type ScreenParamList = { diff --git a/native/user-profile/user-profile-bottom-sheet-navigator.react.js b/native/user-profile/user-profile-bottom-sheet-navigator.react.js --- a/native/user-profile/user-profile-bottom-sheet-navigator.react.js +++ b/native/user-profile/user-profile-bottom-sheet-navigator.react.js @@ -13,9 +13,11 @@ import { UserProfileBottomSheetRouteName, UserProfileAvatarModalRouteName, + UserRelationshipTooltipModalRouteName, type ScreenParamList, type UserProfileBottomSheetParamList, } from '../navigation/route-names.js'; +import UserRelationshipTooltipModal from '../profile/user-relationship-tooltip-modal.react.js'; export type UserProfileBottomSheetNavigationProp< RouteName: $Keys, @@ -44,6 +46,10 @@ name={UserProfileAvatarModalRouteName} component={UserProfileAvatarModal} /> + ); } diff --git a/native/user-profile/user-profile-menu-button.react.js b/native/user-profile/user-profile-menu-button.react.js new file mode 100644 --- /dev/null +++ b/native/user-profile/user-profile-menu-button.react.js @@ -0,0 +1,127 @@ +// @flow + +import { useNavigation, useRoute } from '@react-navigation/native'; +import invariant from 'invariant'; +import * as React from 'react'; +import { View, TouchableOpacity } from 'react-native'; + +import { useRelationshipPrompt } from 'lib/hooks/relationship-prompt.js'; +import { userRelationshipStatus } from 'lib/types/relationship-types.js'; +import type { ThreadInfo } from 'lib/types/thread-types.js'; +import type { UserInfo } from 'lib/types/user-types'; + +import { userProfileMenuButtonHeight } from './user-profile-constants.js'; +import SWMansionIcon from '../components/swmansion-icon.react.js'; +import { OverlayContext } from '../navigation/overlay-context.js'; +import { UserRelationshipTooltipModalRouteName } from '../navigation/route-names.js'; +import { useStyles } from '../themes/colors.js'; + +const onMenuButtonLayout = () => {}; + +type Props = { + +threadInfo: ThreadInfo, + +pendingPersonalThreadUserInfo: ?UserInfo, +}; + +function UserProfileMenuButton(props: Props): React.Node { + const { threadInfo, pendingPersonalThreadUserInfo } = props; + + const { otherUserInfo } = useRelationshipPrompt( + threadInfo, + undefined, + pendingPersonalThreadUserInfo, + ); + + const { navigate } = useNavigation(); + const route = useRoute(); + + const styles = useStyles(unboundStyles); + + const overlayContext = React.useContext(OverlayContext); + + const menuButtonRef = React.useRef(); + + const visibleEntryIDs = React.useMemo(() => { + const result = []; + + if (otherUserInfo?.relationshipStatus === userRelationshipStatus.FRIEND) { + result.push('unfriend'); + result.push('block'); + } else if ( + otherUserInfo?.relationshipStatus === + userRelationshipStatus.BOTH_BLOCKED || + otherUserInfo?.relationshipStatus === + userRelationshipStatus.BLOCKED_BY_VIEWER + ) { + result.push('unblock'); + } else { + result.push('block'); + } + + return result; + }, [otherUserInfo?.relationshipStatus]); + + const onPressMenuButton = React.useCallback(() => { + invariant( + overlayContext, + 'RelationshipListItem should have OverlayContext', + ); + overlayContext.setScrollBlockingModalStatus('open'); + + const currentMenuButtonRef = menuButtonRef.current; + if (!currentMenuButtonRef || !otherUserInfo) { + return; + } + + currentMenuButtonRef.measure((x, y, width, height, pageX, pageY) => { + const coordinates = { + x: pageX, + y: pageY, + width, + height, + }; + + const verticalBounds = { + height: userProfileMenuButtonHeight, + y: pageY, + }; + + const { relationshipStatus, ...restUserInfo } = otherUserInfo; + const relativeUserInfo = { + ...restUserInfo, + isViewer: false, + }; + navigate<'UserRelationshipTooltipModal'>({ + name: UserRelationshipTooltipModalRouteName, + params: { + presentedFrom: route.key, + initialCoordinates: coordinates, + verticalBounds, + visibleEntryIDs, + relativeUserInfo, + tooltipButtonIcon: 'menu', + }, + }); + }); + }, [navigate, otherUserInfo, overlayContext, route.key, visibleEntryIDs]); + + return ( + + + + + + ); +} + +const unboundStyles = { + iconContainer: { + alignSelf: 'flex-end', + }, + moreIcon: { + color: 'modalButtonLabel', + alignSelf: 'flex-end', + }, +}; + +export default UserProfileMenuButton; diff --git a/native/user-profile/user-profile.react.js b/native/user-profile/user-profile.react.js --- a/native/user-profile/user-profile.react.js +++ b/native/user-profile/user-profile.react.js @@ -19,6 +19,7 @@ userProfileMenuButtonHeight, userProfileActionButtonHeight, } from './user-profile-constants.js'; +import UserProfileMenuButton from './user-profile-menu-button.react.js'; import UserProfileMessageButton from './user-profile-message-button.react.js'; import UserProfileRelationshipButton from './user-profile-relationship-button.react.js'; import { BottomSheetContext } from '../bottom-sheet/bottom-sheet-provider.react.js'; @@ -81,6 +82,20 @@ const styles = useStyles(unboundStyles); + const menuButton = React.useMemo(() => { + if (!userProfileThreadInfo) { + return null; + } + + const { threadInfo, pendingPersonalThreadUserInfo } = userProfileThreadInfo; + return ( + + ); + }, [userProfileThreadInfo]); + const onPressCopyUsername = React.useCallback(async () => { Clipboard.setString(usernameText); setUsernameCopied(true); @@ -158,7 +173,7 @@ return ( - + {menuButton} @@ -176,10 +191,6 @@ container: { paddingHorizontal: 16, }, - moreIcon: { - color: 'modalButtonLabel', - alignSelf: 'flex-end', - }, userInfoContainer: { flexDirection: 'row', },