diff --git a/native/user-profile/user-profile.react.js b/native/user-profile/user-profile.react.js index b91463f45..59cb5baa3 100644 --- a/native/user-profile/user-profile.react.js +++ b/native/user-profile/user-profile.react.js @@ -1,239 +1,243 @@ // @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 { useENSName } from 'lib/hooks/ens-cache.js'; 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 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'; 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 resolvedUsernameText = useENSName(usernameText); 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 menuButton = React.useMemo(() => { if (!userProfileThreadInfo) { return null; } const { threadInfo, pendingPersonalThreadUserInfo } = userProfileThreadInfo; return ( ); }, [userProfileThreadInfo]); const onPressCopyUsername = React.useCallback(async () => { - Clipboard.setString(usernameText); + Clipboard.setString(resolvedUsernameText); setUsernameCopied(true); await sleep(3000); setUsernameCopied(false); - }, [usernameText]); + }, [resolvedUsernameText]); 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 ( {menuButton} - {usernameText} + + {resolvedUsernameText} + {copyUsernameButton} {messageButton} {relationshipButton} ); } const unboundStyles = { container: { paddingHorizontal: 16, }, 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; diff --git a/web/modals/user-profile/user-profile.react.js b/web/modals/user-profile/user-profile.react.js index 64f3d5433..4162e40ce 100644 --- a/web/modals/user-profile/user-profile.react.js +++ b/web/modals/user-profile/user-profile.react.js @@ -1,100 +1,104 @@ // @flow import * as React from 'react'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; +import { useENSName } from 'lib/hooks/ens-cache.js'; import { relationshipBlockedInEitherDirection } from 'lib/shared/relationship-utils.js'; import { stringForUserExplicit } from 'lib/shared/user-utils.js'; import type { UserProfileThreadInfo } from 'lib/types/thread-types'; import type { UserInfo } from 'lib/types/user-types'; import sleep from 'lib/utils/sleep.js'; import UserProfileActionButtons from './user-profile-action-buttons.react.js'; import UserProfileAvatarModal from './user-profile-avatar-modal.react.js'; import css from './user-profile.css'; import UserAvatar from '../../avatars/user-avatar.react.js'; import SingleLine from '../../components/single-line.react.js'; type Props = { +userInfo: ?UserInfo, +userProfileThreadInfo: ?UserProfileThreadInfo, }; function UserProfile(props: Props): React.Node { const { userInfo, userProfileThreadInfo } = props; const { pushModal } = useModalContext(); const usernameText = stringForUserExplicit(userInfo); + const resolvedUsernameText = useENSName(usernameText); const [usernameCopied, setUsernameCopied] = React.useState(false); const onClickUserAvatar = React.useCallback(() => { pushModal(); }, [pushModal, userInfo?.id]); const onClickCopyUsername = React.useCallback(async () => { if (usernameCopied) { return; } - await navigator.clipboard.writeText(usernameText); + await navigator.clipboard.writeText(resolvedUsernameText); setUsernameCopied(true); await sleep(3000); setUsernameCopied(false); - }, [usernameCopied, usernameText]); + }, [usernameCopied, resolvedUsernameText]); const actionButtons = React.useMemo(() => { if ( !userProfileThreadInfo || relationshipBlockedInEitherDirection(userInfo?.relationshipStatus) ) { return null; } return ( ); }, [userInfo?.relationshipStatus, userProfileThreadInfo]); const userProfile = React.useMemo( () => (
- {usernameText} + + {resolvedUsernameText} +

{!usernameCopied ? 'Copy username' : 'Username copied!'}

{actionButtons}
), [ actionButtons, onClickCopyUsername, onClickUserAvatar, userInfo?.id, usernameCopied, - usernameText, + resolvedUsernameText, ], ); return userProfile; } export default UserProfile;