diff --git a/native/chat/settings/thread-settings-edit-relationship.react.js b/native/chat/settings/thread-settings-edit-relationship.react.js index a8096bfdf..ec5fbd245 100644 --- a/native/chat/settings/thread-settings-edit-relationship.react.js +++ b/native/chat/settings/thread-settings-edit-relationship.react.js @@ -1,126 +1,129 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { Alert, Text, View } from 'react-native'; import { updateRelationships as serverUpdateRelationships, updateRelationshipsActionTypes, } from 'lib/actions/relationship-actions'; +import { useENSNames } from 'lib/hooks/ens-cache'; import { getRelationshipActionText, getRelationshipDispatchAction, } from 'lib/shared/relationship-utils'; import { getSingleOtherUser } from 'lib/shared/thread-utils'; import { type RelationshipAction, type RelationshipButton, } from 'lib/types/relationship-types'; import type { ThreadInfo } from 'lib/types/thread-types'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils'; import Button from '../../components/button.react'; import { useSelector } from '../../redux/redux-utils'; import { useStyles, useColors } from '../../themes/colors'; import type { ViewStyle } from '../../types/styles'; type Props = { +threadInfo: ThreadInfo, +buttonStyle: ViewStyle, +relationshipButton: RelationshipButton, }; const ThreadSettingsEditRelationship: React.ComponentType = React.memo( function ThreadSettingsEditRelationship(props: Props) { - const otherUserInfo = useSelector(state => { + const otherUserInfoFromRedux = useSelector(state => { const currentUserID = state.currentUserInfo?.id; const otherUserID = getSingleOtherUser(props.threadInfo, currentUserID); invariant(otherUserID, 'Other user should be specified'); const { userInfos } = state.userStore; return userInfos[otherUserID]; }); - invariant(otherUserInfo, 'Other user info should be specified'); + invariant(otherUserInfoFromRedux, 'Other user info should be specified'); + + const [otherUserInfo] = useENSNames([otherUserInfoFromRedux]); const callUpdateRelationships = useServerCall(serverUpdateRelationships); const updateRelationship = React.useCallback( async (action: RelationshipAction) => { try { return await callUpdateRelationships({ action, userIDs: [otherUserInfo.id], }); } catch (e) { Alert.alert('Unknown error', 'Uhh... try again?', [{ text: 'OK' }], { cancelable: true, }); throw e; } }, [callUpdateRelationships, otherUserInfo], ); const { relationshipButton } = props; const relationshipAction = React.useMemo( () => getRelationshipDispatchAction(relationshipButton), [relationshipButton], ); const dispatchActionPromise = useDispatchActionPromise(); const onButtonPress = React.useCallback(() => { dispatchActionPromise( updateRelationshipsActionTypes, updateRelationship(relationshipAction), ); }, [dispatchActionPromise, relationshipAction, updateRelationship]); const colors = useColors(); const { panelIosHighlightUnderlay } = colors; const styles = useStyles(unboundStyles); const otherUserInfoUsername = otherUserInfo.username; invariant(otherUserInfoUsername, 'Other user username should be specified'); const relationshipButtonText = React.useMemo( () => getRelationshipActionText(relationshipButton, otherUserInfoUsername), [otherUserInfoUsername, relationshipButton], ); return ( ); }, ); const unboundStyles = { button: { flexDirection: 'row', paddingHorizontal: 12, paddingVertical: 10, }, container: { backgroundColor: 'panelForeground', paddingHorizontal: 12, }, text: { color: 'redText', flex: 1, fontSize: 16, }, }; export default ThreadSettingsEditRelationship; diff --git a/native/chat/settings/thread-settings-member.react.js b/native/chat/settings/thread-settings-member.react.js index 372947050..c2fbbe31f 100644 --- a/native/chat/settings/thread-settings-member.react.js +++ b/native/chat/settings/thread-settings-member.react.js @@ -1,285 +1,289 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { View, Text, Platform, ActivityIndicator, TouchableOpacity, } from 'react-native'; import { removeUsersFromThreadActionTypes, changeThreadMemberRolesActionTypes, } from 'lib/actions/thread-actions'; +import { useENSNames } from 'lib/hooks/ens-cache'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import { memberIsAdmin, memberHasAdminPowers, getAvailableThreadMemberActions, } from 'lib/shared/thread-utils'; import { stringForUser } from 'lib/shared/user-utils'; import type { LoadingStatus } from 'lib/types/loading-types'; import { type ThreadInfo, type RelativeMemberInfo, } from 'lib/types/thread-types'; import PencilIcon from '../../components/pencil-icon.react'; import { SingleLine } from '../../components/single-line.react'; import { type KeyboardState, KeyboardContext, } from '../../keyboard/keyboard-state'; import { OverlayContext, type OverlayContextType, } from '../../navigation/overlay-context'; import { ThreadSettingsMemberTooltipModalRouteName } from '../../navigation/route-names'; import { useSelector } from '../../redux/redux-utils'; import { type Colors, useColors, useStyles } from '../../themes/colors'; import type { VerticalBounds } from '../../types/layout-types'; import type { ThreadSettingsNavigate } from './thread-settings.react'; type BaseProps = { +memberInfo: RelativeMemberInfo, +threadInfo: ThreadInfo, +canEdit: boolean, +navigate: ThreadSettingsNavigate, +firstListItem: boolean, +lastListItem: boolean, +verticalBounds: ?VerticalBounds, +threadSettingsRouteKey: string, }; type Props = { ...BaseProps, // Redux state +removeUserLoadingStatus: LoadingStatus, +changeRoleLoadingStatus: LoadingStatus, +colors: Colors, +styles: typeof unboundStyles, // withKeyboardState +keyboardState: ?KeyboardState, // withOverlayContext +overlayContext: ?OverlayContextType, }; class ThreadSettingsMember extends React.PureComponent { editButton: ?React.ElementRef; render() { const userText = stringForUser(this.props.memberInfo); let userInfo = null; if (this.props.memberInfo.username) { userInfo = ( {userText} ); } else { userInfo = ( {userText} ); } let editButton = null; if ( this.props.removeUserLoadingStatus === 'loading' || this.props.changeRoleLoadingStatus === 'loading' ) { editButton = ( ); } else if ( getAvailableThreadMemberActions( this.props.memberInfo, this.props.threadInfo, this.props.canEdit, ).length !== 0 ) { editButton = ( ); } let roleInfo = null; if (memberIsAdmin(this.props.memberInfo, this.props.threadInfo)) { roleInfo = ( admin ); } else if (memberHasAdminPowers(this.props.memberInfo)) { roleInfo = ( parent admin ); } const firstItem = this.props.firstListItem ? null : this.props.styles.topBorder; const lastItem = this.props.lastListItem ? this.props.styles.lastInnerContainer : null; return ( {userInfo} {editButton} {roleInfo} ); } editButtonRef = (editButton: ?React.ElementRef) => { this.editButton = editButton; }; onEditButtonLayout = () => {}; onPressEdit = () => { if (this.dismissKeyboardIfShowing()) { return; } const { editButton, props: { verticalBounds }, } = this; if (!editButton || !verticalBounds) { return; } const { overlayContext } = this.props; invariant( overlayContext, 'ThreadSettingsMember should have OverlayContext', ); overlayContext.setScrollBlockingModalStatus('open'); editButton.measure((x, y, width, height, pageX, pageY) => { const coordinates = { x: pageX, y: pageY, width, height }; this.props.navigate<'ThreadSettingsMemberTooltipModal'>({ name: ThreadSettingsMemberTooltipModalRouteName, params: { presentedFrom: this.props.threadSettingsRouteKey, initialCoordinates: coordinates, verticalBounds, visibleEntryIDs: getAvailableThreadMemberActions( this.props.memberInfo, this.props.threadInfo, this.props.canEdit, ), memberInfo: this.props.memberInfo, threadInfo: this.props.threadInfo, }, }); }); }; dismissKeyboardIfShowing = () => { const { keyboardState } = this.props; return !!(keyboardState && keyboardState.dismissKeyboardIfShowing()); }; } const unboundStyles = { anonymous: { color: 'panelForegroundTertiaryLabel', fontStyle: 'italic', }, container: { backgroundColor: 'panelForeground', flex: 1, paddingHorizontal: 12, }, editButton: { paddingLeft: 10, }, topBorder: { borderColor: 'panelForegroundBorder', borderTopWidth: 1, }, innerContainer: { flex: 1, paddingHorizontal: 12, paddingVertical: 8, }, lastInnerContainer: { paddingBottom: Platform.OS === 'ios' ? 12 : 10, }, role: { color: 'panelForegroundTertiaryLabel', flex: 1, fontSize: 14, paddingTop: 4, }, row: { flex: 1, flexDirection: 'row', }, username: { color: 'panelForegroundSecondaryLabel', flex: 1, fontSize: 16, lineHeight: 20, }, }; const ConnectedThreadSettingsMember: React.ComponentType = React.memo( function ConnectedThreadSettingsMember(props: BaseProps) { const memberID = props.memberInfo.id; const removeUserLoadingStatus = useSelector(state => createLoadingStatusSelector( removeUsersFromThreadActionTypes, `${removeUsersFromThreadActionTypes.started}:${memberID}`, )(state), ); const changeRoleLoadingStatus = useSelector(state => createLoadingStatusSelector( changeThreadMemberRolesActionTypes, `${changeThreadMemberRolesActionTypes.started}:${memberID}`, )(state), ); + const [memberInfo] = useENSNames([props.memberInfo]); + const colors = useColors(); const styles = useStyles(unboundStyles); const keyboardState = React.useContext(KeyboardContext); const overlayContext = React.useContext(OverlayContext); return ( ); }, ); export default ConnectedThreadSettingsMember;