diff --git a/native/more/relationship-list-item.react.js b/native/more/relationship-list-item.react.js index cdcb44322..894903256 100644 --- a/native/more/relationship-list-item.react.js +++ b/native/more/relationship-list-item.react.js @@ -1,330 +1,339 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { Alert, View, Text, TouchableOpacity, ActivityIndicator, } from 'react-native'; import { updateRelationshipsActionTypes, updateRelationships, } from 'lib/actions/relationship-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import type { LoadingStatus } from 'lib/types/loading-types'; import { type RelationshipRequest, + type RelationshipAction, userRelationshipStatus, relationshipActions, } from 'lib/types/relationship-types'; import type { AccountUserInfo, GlobalAccountUserInfo, } from 'lib/types/user-types'; import { type DispatchActionPromise, useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils'; 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 type { NavigationRoute } from '../navigation/route-names'; import { RelationshipListItemTooltipModalRouteName, FriendListRouteName, BlockListRouteName, } 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 { RelationshipListNavigate } from './relationship-list.react'; type BaseProps = {| +userInfo: AccountUserInfo, +lastListItem: boolean, +verticalBounds: ?VerticalBounds, +relationshipListRoute: NavigationRoute<'FriendList' | 'BlockList'>, +navigate: RelationshipListNavigate, +onSelect: (selectedUser: GlobalAccountUserInfo) => void, |}; type Props = {| ...BaseProps, // Redux state +removeUserLoadingStatus: LoadingStatus, +colors: Colors, +styles: typeof unboundStyles, // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +updateRelationships: (request: RelationshipRequest) => Promise, // withOverlayContext +overlayContext: ?OverlayContextType, // withKeyboardState +keyboardState: ?KeyboardState, |}; class RelationshipListItem extends React.PureComponent { editButton = React.createRef>(); render() { const { lastListItem, removeUserLoadingStatus, userInfo, relationshipListRoute, } = this.props; const relationshipsToEdit = { [FriendListRouteName]: [userRelationshipStatus.FRIEND], [BlockListRouteName]: [ userRelationshipStatus.BOTH_BLOCKED, userRelationshipStatus.BLOCKED_BY_VIEWER, ], }[relationshipListRoute.name]; const canEditFriendRequest = { [FriendListRouteName]: true, [BlockListRouteName]: false, }[relationshipListRoute.name]; const borderBottom = lastListItem ? null : this.props.styles.borderBottom; let editButton = null; if (removeUserLoadingStatus === 'loading') { editButton = ( ); } else if (relationshipsToEdit.includes(userInfo.relationshipStatus)) { editButton = ( ); } else if ( userInfo.relationshipStatus === userRelationshipStatus.REQUEST_RECEIVED && canEditFriendRequest ) { editButton = ( - - Accept - + + + Accept + + + Reject + + ); } else if ( userInfo.relationshipStatus === userRelationshipStatus.REQUEST_SENT && canEditFriendRequest ) { editButton = ( Cancel request ); } else { editButton = ( Add ); } return ( {this.props.userInfo.username} {editButton} ); } onSelect = () => { const { id, username } = this.props.userInfo; this.props.onSelect({ id, username }); }; visibleEntryIDs() { const { relationshipListRoute } = this.props; const id = { [FriendListRouteName]: 'unfriend', [BlockListRouteName]: 'unblock', }[relationshipListRoute.name]; return [id]; } onPressEdit = () => { if (this.props.keyboardState?.dismissKeyboardIfShowing()) { return; } const { editButton, props: { verticalBounds }, } = this; const { overlayContext, userInfo } = this.props; invariant( overlayContext, 'RelationshipListItem should have OverlayContext', ); overlayContext.setScrollBlockingModalStatus('open'); if (!editButton.current || !verticalBounds) { return; } const { relationshipStatus, ...restUserInfo } = userInfo; const relativeUserInfo = { ...restUserInfo, isViewer: false, }; editButton.current.measure((x, y, width, height, pageX, pageY) => { const coordinates = { x: pageX, y: pageY, width, height }; this.props.navigate({ name: RelationshipListItemTooltipModalRouteName, params: { presentedFrom: this.props.relationshipListRoute.key, initialCoordinates: coordinates, verticalBounds, visibleEntryIDs: this.visibleEntryIDs(), relativeUserInfo, }, }); }); }; // We need to set onLayout in order to allow .measure() to be on the ref onLayout = () => {}; - onPressUpdateFriendship = () => { + onPressFriendUser = () => { + this.onPressUpdateFriendship(relationshipActions.FRIEND); + }; + + onPressUnfriendUser = () => { + this.onPressUpdateFriendship(relationshipActions.UNFRIEND); + }; + + onPressUpdateFriendship(action: RelationshipAction) { const { id } = this.props.userInfo; const customKeyName = `${updateRelationshipsActionTypes.started}:${id}`; this.props.dispatchActionPromise( updateRelationshipsActionTypes, - this.updateFriendship(), + this.updateFriendship(action), { customKeyName }, ); - }; - - get updateFriendshipAction() { - const { userInfo } = this.props; - if ( - userInfo.relationshipStatus === userRelationshipStatus.REQUEST_RECEIVED - ) { - return relationshipActions.FRIEND; - } else if ( - userInfo.relationshipStatus === userRelationshipStatus.REQUEST_SENT - ) { - return relationshipActions.UNFRIEND; - } else { - return undefined; - } } - async updateFriendship() { + async updateFriendship(action: RelationshipAction) { try { - const action = this.updateFriendshipAction; - invariant(action, 'invalid relationshipAction'); return await this.props.updateRelationships({ action, userIDs: [this.props.userInfo.id], }); } catch (e) { Alert.alert('Unknown error', 'Uhh... try again?', [{ text: 'OK' }], { cancelable: true, }); throw e; } } } const unboundStyles = { editButton: { paddingLeft: 10, }, container: { flex: 1, paddingHorizontal: 12, backgroundColor: 'panelForeground', }, innerContainer: { paddingVertical: 10, paddingHorizontal: 12, borderColor: 'panelForegroundBorder', flexDirection: 'row', }, borderBottom: { borderBottomWidth: 1, }, + buttonContainer: { + flexDirection: 'row', + }, + editButtonWithMargin: { + marginLeft: 15, + }, username: { color: 'panelForegroundSecondaryLabel', flex: 1, fontSize: 16, lineHeight: 20, }, blueAction: { color: 'link', fontSize: 16, paddingLeft: 6, }, redAction: { color: 'redText', fontSize: 16, paddingLeft: 6, }, }; export default React.memo(function ConnectedRelationshipListItem( props: BaseProps, ) { const removeUserLoadingStatus = useSelector((state) => createLoadingStatusSelector( updateRelationshipsActionTypes, `${updateRelationshipsActionTypes.started}:${props.userInfo.id}`, )(state), ); const colors = useColors(); const styles = useStyles(unboundStyles); const dispatchActionPromise = useDispatchActionPromise(); const boundUpdateRelationships = useServerCall(updateRelationships); const overlayContext = React.useContext(OverlayContext); const keyboardState = React.useContext(KeyboardContext); return ( ); });