Page MenuHomePhabricator

D9382.id31830.diff
No OneTemporary

D9382.id31830.diff

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<UserProfileBottomSheetParamList>,
@@ -44,6 +46,10 @@
name={UserProfileAvatarModalRouteName}
component={UserProfileAvatarModal}
/>
+ <UserProfileBottomSheetOverlayNavigator.Screen
+ name={UserRelationshipTooltipModalRouteName}
+ component={UserRelationshipTooltipModal}
+ />
</UserProfileBottomSheetOverlayNavigator.Navigator>
);
}
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,147 @@
+// @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';
+
+// We need to set onMenuButtonLayout in order to allow .measure()
+// to be on the ref
+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 visibleTooltipActionEntryIDs = 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,
+ 'UserProfileMenuButton 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: visibleTooltipActionEntryIDs,
+ relativeUserInfo,
+ tooltipButtonIcon: 'menu',
+ },
+ });
+ });
+ }, [
+ navigate,
+ otherUserInfo,
+ overlayContext,
+ route.key,
+ visibleTooltipActionEntryIDs,
+ ]);
+
+ const userProfileMenuButton = React.useMemo(
+ () => (
+ <TouchableOpacity
+ onPress={onPressMenuButton}
+ style={styles.iconContainer}
+ >
+ <View ref={menuButtonRef} onLayout={onMenuButtonLayout}>
+ <SWMansionIcon
+ name="menu-vertical"
+ size={24}
+ style={styles.moreIcon}
+ />
+ </View>
+ </TouchableOpacity>
+ ),
+ [onPressMenuButton, styles.iconContainer, styles.moreIcon],
+ );
+
+ return userProfileMenuButton;
+}
+
+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 (
+ <UserProfileMenuButton
+ threadInfo={threadInfo}
+ pendingPersonalThreadUserInfo={pendingPersonalThreadUserInfo}
+ />
+ );
+ }, [userProfileThreadInfo]);
+
const onPressCopyUsername = React.useCallback(async () => {
Clipboard.setString(usernameText);
setUsernameCopied(true);
@@ -158,7 +173,7 @@
return (
<View style={styles.container}>
- <SWMansionIcon name="menu-vertical" size={24} style={styles.moreIcon} />
+ {menuButton}
<View style={styles.userInfoContainer}>
<UserProfileAvatar userID={userInfo?.id} />
<View style={styles.usernameContainer}>
@@ -176,10 +191,6 @@
container: {
paddingHorizontal: 16,
},
- moreIcon: {
- color: 'modalButtonLabel',
- alignSelf: 'flex-end',
- },
userInfoContainer: {
flexDirection: 'row',
},

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 26, 7:55 PM (15 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2583340
Default Alt Text
D9382.id31830.diff (7 KB)

Event Timeline