diff --git a/native/user-profile/user-profile-avatar.react.js b/native/user-profile/user-profile-avatar.react.js
new file mode 100644
index 000000000..c48e85217
--- /dev/null
+++ b/native/user-profile/user-profile-avatar.react.js
@@ -0,0 +1,73 @@
+// @flow
+
+import { useNavigation, useRoute } from '@react-navigation/native';
+import invariant from 'invariant';
+import * as React from 'react';
+import { View, TouchableOpacity } from 'react-native';
+
+import { userProfileUserInfoContainerHeight } from './user-profile-constants.js';
+import UserAvatar from '../avatars/user-avatar.react.js';
+import { OverlayContext } from '../navigation/overlay-context.js';
+import { UserProfileAvatarModalRouteName } from '../navigation/route-names.js';
+
+// We need to set onAvatarLayout in order to allow .measure() to be on the ref
+const onAvatarLayout = () => {};
+
+type Props = {
+ userID: ?string,
+};
+
+function UserProfileAvatar(props: Props): React.Node {
+ const { userID } = props;
+
+ const { navigate } = useNavigation();
+ const route = useRoute();
+
+ const overlayContext = React.useContext(OverlayContext);
+
+ const avatarRef = React.useRef();
+
+ const onPressAvatar = React.useCallback(() => {
+ invariant(overlayContext, 'UserProfileAvatar should have OverlayContext');
+ overlayContext.setScrollBlockingModalStatus('open');
+
+ const currentAvatarRef = avatarRef.current;
+ if (!currentAvatarRef) {
+ return;
+ }
+
+ currentAvatarRef.measure((x, y, width, height, pageX, pageY) => {
+ const coordinates = {
+ x: pageX,
+ y: pageY,
+ width,
+ height,
+ };
+
+ const verticalBounds = {
+ height: userProfileUserInfoContainerHeight,
+ y: pageY,
+ };
+
+ navigate<'UserProfileAvatarModal'>({
+ name: UserProfileAvatarModalRouteName,
+ params: {
+ presentedFrom: route.key,
+ initialCoordinates: coordinates,
+ verticalBounds,
+ userID,
+ },
+ });
+ });
+ }, [navigate, overlayContext, route.key, userID]);
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default UserProfileAvatar;
diff --git a/native/user-profile/user-profile.react.js b/native/user-profile/user-profile.react.js
index 0661a2cd5..7582a2c7d 100644
--- a/native/user-profile/user-profile.react.js
+++ b/native/user-profile/user-profile.react.js
@@ -1,228 +1,228 @@
// @flow
import Clipboard from '@react-native-clipboard/clipboard';
import invariant from 'invariant';
import * as React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { relationshipBlockedInEitherDirection } from 'lib/shared/relationship-utils.js';
import { useUserProfileThreadInfo } from 'lib/shared/thread-utils.js';
import { stringForUserExplicit } from 'lib/shared/user-utils.js';
import type { UserInfo } from 'lib/types/user-types';
import sleep from 'lib/utils/sleep.js';
+import UserProfileAvatar from './user-profile-avatar.react.js';
import {
userProfileUserInfoContainerHeight,
userProfileBottomPadding,
userProfileMenuButtonHeight,
userProfileActionButtonHeight,
} from './user-profile-constants.js';
import UserProfileMessageButton from './user-profile-message-button.react.js';
import UserProfileRelationshipButton from './user-profile-relationship-button.react.js';
-import UserAvatar from '../avatars/user-avatar.react.js';
import { BottomSheetContext } from '../bottom-sheet/bottom-sheet-provider.react.js';
import SingleLine from '../components/single-line.react.js';
import SWMansionIcon from '../components/swmansion-icon.react.js';
import { useStyles } from '../themes/colors.js';
type Props = {
+userInfo: ?UserInfo,
};
function UserProfile(props: Props): React.Node {
const { userInfo } = props;
const userProfileThreadInfo = useUserProfileThreadInfo(userInfo);
const usernameText = stringForUserExplicit(userInfo);
const [usernameCopied, setUsernameCopied] = React.useState(false);
const [
userProfileRelationshipButtonHeight,
setUserProfileRelationshipButtonHeight,
] = React.useState(0);
const bottomSheetContext = React.useContext(BottomSheetContext);
invariant(bottomSheetContext, 'bottomSheetContext should be set');
const { setContentHeight } = bottomSheetContext;
const insets = useSafeAreaInsets();
React.useLayoutEffect(() => {
let height =
insets.bottom +
userProfileUserInfoContainerHeight +
userProfileBottomPadding;
if (userProfileThreadInfo) {
height += userProfileMenuButtonHeight;
}
if (
userProfileThreadInfo &&
!relationshipBlockedInEitherDirection(userInfo?.relationshipStatus)
) {
// message button height + relationship button height
height +=
userProfileActionButtonHeight + userProfileRelationshipButtonHeight;
}
setContentHeight(height);
}, [
insets.bottom,
setContentHeight,
userInfo?.relationshipStatus,
userProfileRelationshipButtonHeight,
userProfileThreadInfo,
]);
const styles = useStyles(unboundStyles);
const onPressCopyUsername = React.useCallback(async () => {
Clipboard.setString(usernameText);
setUsernameCopied(true);
await sleep(3000);
setUsernameCopied(false);
}, [usernameText]);
const copyUsernameButton = React.useMemo(() => {
if (usernameCopied) {
return (
Username copied!
);
}
return (
Copy username
);
}, [
onPressCopyUsername,
styles.copyUsernameContainer,
styles.copyUsernameIcon,
styles.copyUsernameText,
usernameCopied,
]);
const messageButton = React.useMemo(() => {
if (
!userProfileThreadInfo ||
relationshipBlockedInEitherDirection(userInfo?.relationshipStatus)
) {
return null;
}
const { threadInfo, pendingPersonalThreadUserInfo } = userProfileThreadInfo;
return (
);
}, [userInfo?.relationshipStatus, userProfileThreadInfo]);
const relationshipButton = React.useMemo(() => {
if (
!userProfileThreadInfo ||
relationshipBlockedInEitherDirection(userInfo?.relationshipStatus)
) {
return null;
}
const { threadInfo, pendingPersonalThreadUserInfo } = userProfileThreadInfo;
return (
);
}, [userInfo?.relationshipStatus, userProfileThreadInfo]);
return (
-
+
{usernameText}
{copyUsernameButton}
{messageButton}
{relationshipButton}
);
}
const unboundStyles = {
container: {
paddingHorizontal: 16,
},
moreIcon: {
color: 'modalButtonLabel',
alignSelf: 'flex-end',
},
userInfoContainer: {
flexDirection: 'row',
},
usernameContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'flex-start',
paddingLeft: 16,
},
usernameText: {
color: 'modalForegroundLabel',
fontSize: 18,
fontWeight: '500',
},
copyUsernameContainer: {
flexDirection: 'row',
justifyContent: 'center',
paddingTop: 8,
},
copyUsernameIcon: {
color: 'purpleLink',
marginRight: 4,
},
copyUsernameText: {
color: 'purpleLink',
fontSize: 12,
},
messageButtonContainer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'purpleButton',
paddingVertical: 8,
marginTop: 16,
borderRadius: 8,
},
messageButtonIcon: {
color: 'floatingButtonLabel',
paddingRight: 8,
},
messageButtonText: {
color: 'floatingButtonLabel',
},
};
export default UserProfile;