Page MenuHomePhorge

D6435.1768524800.diff
No OneTemporary

Size
27 KB
Referenced Files
None
Subscribers
None

D6435.1768524800.diff

diff --git a/native/profile/relationship-list.react.js b/native/profile/relationship-list.react.js
--- a/native/profile/relationship-list.react.js
+++ b/native/profile/relationship-list.react.js
@@ -4,7 +4,6 @@
import * as React from 'react';
import { View, Text, Alert, Platform } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
-import { createSelector } from 'reselect';
import {
updateRelationshipsActionTypes,
@@ -14,47 +13,30 @@
import { registerFetchKey } from 'lib/reducers/loading-reducer';
import { userRelationshipsSelector } from 'lib/selectors/relationship-selectors';
import { userStoreSearchIndex as userStoreSearchIndexSelector } from 'lib/selectors/user-selectors';
-import SearchIndex from 'lib/shared/search-index';
import {
- type UserRelationships,
- type RelationshipRequest,
- type RelationshipErrors,
userRelationshipStatus,
relationshipActions,
} from 'lib/types/relationship-types';
-import type { UserSearchResult } from 'lib/types/search-types';
import type {
- UserInfos,
GlobalAccountUserInfo,
AccountUserInfo,
} from 'lib/types/user-types';
import {
- type DispatchActionPromise,
useServerCall,
useDispatchActionPromise,
} from 'lib/utils/action-utils';
import LinkButton from '../components/link-button.react';
import { createTagInput, BaseTagInput } from '../components/tag-input.react';
-import {
- type KeyboardState,
- KeyboardContext,
-} from '../keyboard/keyboard-state';
-import {
- OverlayContext,
- type OverlayContextType,
-} from '../navigation/overlay-context';
+import { KeyboardContext } from '../keyboard/keyboard-state';
+import { OverlayContext } from '../navigation/overlay-context';
import type { NavigationRoute } from '../navigation/route-names';
import {
FriendListRouteName,
BlockListRouteName,
} from '../navigation/route-names';
import { useSelector } from '../redux/redux-utils';
-import {
- useStyles,
- type IndicatorStyle,
- useIndicatorStyle,
-} from '../themes/colors';
+import { useStyles, useIndicatorStyle } from '../themes/colors';
import type { VerticalBounds } from '../types/layout-types';
import type { ProfileNavigationProp } from './profile.react';
import RelationshipListItem from './relationship-list-item.react';
@@ -83,297 +65,105 @@
+verticalBounds: ?VerticalBounds,
};
-type BaseProps = {
- +navigation: ProfileNavigationProp<>,
- +route: NavigationRoute<'FriendList' | 'BlockList'>,
-};
-type Props = {
- ...BaseProps,
- // Redux state
- +relationships: UserRelationships,
- +userInfos: UserInfos,
- +viewerID: ?string,
- +userStoreSearchIndex: SearchIndex,
- +styles: typeof unboundStyles,
- +indicatorStyle: IndicatorStyle,
- // Redux dispatch functions
- +dispatchActionPromise: DispatchActionPromise,
- // async functions that hit server APIs
- +searchUsers: (usernamePrefix: string) => Promise<UserSearchResult>,
- +updateRelationships: (
- request: RelationshipRequest,
- ) => Promise<RelationshipErrors>,
- // withOverlayContext
- +overlayContext: ?OverlayContextType,
- // withKeyboardState
- +keyboardState: ?KeyboardState,
-};
-type State = {
- +verticalBounds: ?VerticalBounds,
- +searchInputText: string,
- +serverSearchResults: $ReadOnlyArray<GlobalAccountUserInfo>,
- +currentTags: $ReadOnlyArray<GlobalAccountUserInfo>,
- +userStoreSearchResults: Set<string>,
-};
-type PropsAndState = { ...Props, ...State };
-class RelationshipList extends React.PureComponent<Props, State> {
- flatListContainerRef = React.createRef();
- tagInput: ?BaseTagInput<GlobalAccountUserInfo> = null;
- state: State = {
- verticalBounds: null,
- searchInputText: '',
- serverSearchResults: [],
- userStoreSearchResults: new Set(),
- currentTags: [],
- };
-
- componentDidMount() {
- this.setSaveButton(false);
- }
-
- componentDidUpdate(prevProps: Props, prevState: State) {
- const prevTags = prevState.currentTags.length;
- const currentTags = this.state.currentTags.length;
- if (prevTags !== 0 && currentTags === 0) {
- this.setSaveButton(false);
- } else if (prevTags === 0 && currentTags !== 0) {
- this.setSaveButton(true);
- }
- }
-
- setSaveButton(enabled: boolean) {
- this.props.navigation.setOptions({
- headerRight: () => (
- <LinkButton text="Save" onPress={this.onPressAdd} disabled={!enabled} />
- ),
- });
- }
-
- static keyExtractor = (item: ListItem) => {
- if (item.userInfo) {
- return item.userInfo.id;
- } else if (item.type === 'empty') {
- return 'empty';
- } else if (item.type === 'header') {
- return 'header';
- } else if (item.type === 'footer') {
- return 'footer';
- }
- invariant(false, 'keyExtractor conditions should be exhaustive');
- };
-
- get listData() {
- return this.listDataSelector({ ...this.props, ...this.state });
- }
-
- static getOverlayContext(props: Props) {
- const { overlayContext } = props;
- invariant(overlayContext, 'RelationshipList should have OverlayContext');
- return overlayContext;
- }
-
- static scrollDisabled(props: Props) {
- const overlayContext = RelationshipList.getOverlayContext(props);
- return overlayContext.scrollBlockingModalStatus !== 'closed';
- }
-
- render() {
- const inputProps = {
- ...tagInputProps,
- onSubmitEditing: this.onPressAdd,
- };
- return (
- <View style={this.props.styles.container}>
- <View style={this.props.styles.tagInputContainer}>
- <Text style={this.props.styles.tagInputLabel}>Search:</Text>
- <View style={this.props.styles.tagInput}>
- <TagInput
- value={this.state.currentTags}
- onChange={this.onChangeTagInput}
- text={this.state.searchInputText}
- onChangeText={this.onChangeSearchText}
- labelExtractor={this.tagDataLabelExtractor}
- ref={this.tagInputRef}
- inputProps={inputProps}
- maxHeight={36}
- />
- </View>
- </View>
- <View
- ref={this.flatListContainerRef}
- onLayout={this.onFlatListContainerLayout}
- style={this.props.styles.container}
- >
- <FlatList
- contentContainerStyle={this.props.styles.contentContainer}
- data={this.listData}
- renderItem={this.renderItem}
- keyExtractor={RelationshipList.keyExtractor}
- scrollEnabled={!RelationshipList.scrollDisabled(this.props)}
- indicatorStyle={this.props.indicatorStyle}
- keyboardShouldPersistTaps="handled"
- />
- </View>
- </View>
- );
+function keyExtractor(item: ListItem) {
+ if (item.userInfo) {
+ return item.userInfo.id;
+ } else if (item.type === 'empty') {
+ return 'empty';
+ } else if (item.type === 'header') {
+ return 'header';
+ } else if (item.type === 'footer') {
+ return 'footer';
}
+ invariant(false, 'keyExtractor conditions should be exhaustive');
+}
- listDataSelector = createSelector(
- (propsAndState: PropsAndState) => propsAndState.relationships,
- (propsAndState: PropsAndState) => propsAndState.route.name,
- (propsAndState: PropsAndState) => propsAndState.verticalBounds,
- (propsAndState: PropsAndState) => propsAndState.searchInputText,
- (propsAndState: PropsAndState) => propsAndState.serverSearchResults,
- (propsAndState: PropsAndState) => propsAndState.userStoreSearchResults,
- (propsAndState: PropsAndState) => propsAndState.userInfos,
- (propsAndState: PropsAndState) => propsAndState.viewerID,
- (propsAndState: PropsAndState) => propsAndState.currentTags,
- (
- relationships: UserRelationships,
- routeName: 'FriendList' | 'BlockList',
- verticalBounds: ?VerticalBounds,
- searchInputText: string,
- serverSearchResults: $ReadOnlyArray<GlobalAccountUserInfo>,
- userStoreSearchResults: Set<string>,
- userInfos: UserInfos,
- viewerID: ?string,
- currentTags: $ReadOnlyArray<GlobalAccountUserInfo>,
- ) => {
- const defaultUsers = {
- [FriendListRouteName]: relationships.friends,
- [BlockListRouteName]: relationships.blocked,
- }[routeName];
-
- const excludeUserIDsArray = currentTags
- .map(userInfo => userInfo.id)
- .concat(viewerID || []);
-
- const excludeUserIDs = new Set(excludeUserIDsArray);
-
- let displayUsers = defaultUsers;
-
- if (searchInputText !== '') {
- const mergedUserInfos: { [id: string]: AccountUserInfo } = {};
- for (const userInfo of serverSearchResults) {
- mergedUserInfos[userInfo.id] = userInfo;
- }
- for (const id of userStoreSearchResults) {
- const { username, relationshipStatus } = userInfos[id];
- if (username) {
- mergedUserInfos[id] = { id, username, relationshipStatus };
- }
- }
-
- const sortToEnd = [];
- const userSearchResults = [];
- const sortRelationshipTypesToEnd = {
- [FriendListRouteName]: [userRelationshipStatus.FRIEND],
- [BlockListRouteName]: [
- userRelationshipStatus.BLOCKED_BY_VIEWER,
- userRelationshipStatus.BOTH_BLOCKED,
- ],
- }[routeName];
- for (const userID in mergedUserInfos) {
- if (excludeUserIDs.has(userID)) {
- continue;
- }
-
- const userInfo = mergedUserInfos[userID];
- if (
- sortRelationshipTypesToEnd.includes(userInfo.relationshipStatus)
- ) {
- sortToEnd.push(userInfo);
- } else {
- userSearchResults.push(userInfo);
- }
- }
-
- displayUsers = userSearchResults.concat(sortToEnd);
- }
+const tagDataLabelExtractor = (userInfo: GlobalAccountUserInfo) =>
+ userInfo.username;
- let emptyItem;
- if (displayUsers.length === 0 && searchInputText === '') {
- emptyItem = { type: 'empty', because: 'no-relationships' };
- } else if (displayUsers.length === 0) {
- emptyItem = { type: 'empty', because: 'no-results' };
+type Props = {
+ +navigation: ProfileNavigationProp<>,
+ +route: NavigationRoute<'FriendList' | 'BlockList'>,
+};
+function RelationshipList(props: Props): React.Node {
+ const callSearchUsers = useServerCall(searchUsers);
+ const userInfos = useSelector(state => state.userStore.userInfos);
+ const searchUsersOnServer = React.useCallback(
+ async (usernamePrefix: string) => {
+ if (usernamePrefix.length === 0) {
+ return [];
}
- const mappedUsers = displayUsers.map((userInfo, index) => ({
- type: 'user',
- userInfo,
- lastListItem: displayUsers.length - 1 === index,
- verticalBounds,
- }));
-
- return []
- .concat(emptyItem ? emptyItem : [])
- .concat(emptyItem ? [] : { type: 'header' })
- .concat(mappedUsers)
- .concat(emptyItem ? [] : { type: 'footer' });
+ const userInfosResult = await callSearchUsers(usernamePrefix);
+ return userInfosResult.userInfos;
},
+ [callSearchUsers],
);
- tagInputRef = (tagInput: ?BaseTagInput<GlobalAccountUserInfo>) => {
- this.tagInput = tagInput;
- };
-
- tagDataLabelExtractor = (userInfo: GlobalAccountUserInfo) =>
- userInfo.username;
-
- onChangeTagInput = (currentTags: $ReadOnlyArray<GlobalAccountUserInfo>) => {
- this.setState({ currentTags });
- };
-
- onChangeSearchText = async (searchText: string) => {
- const excludeStatuses = {
- [FriendListRouteName]: [
- userRelationshipStatus.BLOCKED_VIEWER,
- userRelationshipStatus.BOTH_BLOCKED,
- ],
- [BlockListRouteName]: [],
- }[this.props.route.name];
-
- const results = this.props.userStoreSearchIndex
- .getSearchResults(searchText)
- .filter(userID => {
- const relationship = this.props.userInfos[userID].relationshipStatus;
- return !excludeStatuses.includes(relationship);
- });
-
- this.setState({
- searchInputText: searchText,
- userStoreSearchResults: new Set(results),
- });
-
- const serverSearchResults = await this.searchUsers(searchText);
- const filteredServerSearchResults = serverSearchResults.filter(
- searchUserInfo => {
- const userInfo = this.props.userInfos[searchUserInfo.id];
- return (
- !userInfo || !excludeStatuses.includes(userInfo.relationshipStatus)
- );
- },
- );
- this.setState({ serverSearchResults: filteredServerSearchResults });
- };
+ const [searchInputText, setSearchInputText] = React.useState<string>('');
+ const [userStoreSearchResults, setUserStoreSearchResults] = React.useState<
+ $ReadOnlySet<string>,
+ >(new Set());
+ const [serverSearchResults, setServerSearchResults] = React.useState<
+ $ReadOnlyArray<GlobalAccountUserInfo>,
+ >([]);
+
+ const { route } = props;
+ const routeName = route.name;
+ const userStoreSearchIndex = useSelector(userStoreSearchIndexSelector);
+ const onChangeSearchText = React.useCallback(
+ async (searchText: string) => {
+ setSearchInputText(searchText);
+
+ const excludeStatuses = {
+ [FriendListRouteName]: [
+ userRelationshipStatus.BLOCKED_VIEWER,
+ userRelationshipStatus.BOTH_BLOCKED,
+ ],
+ [BlockListRouteName]: [],
+ }[routeName];
+ const results = userStoreSearchIndex
+ .getSearchResults(searchText)
+ .filter(userID => {
+ const relationship = userInfos[userID].relationshipStatus;
+ return !excludeStatuses.includes(relationship);
+ });
+ setUserStoreSearchResults(new Set(results));
+
+ const searchResultsFromServer = await searchUsersOnServer(searchText);
+ const filteredServerSearchResults = searchResultsFromServer.filter(
+ searchUserInfo => {
+ const userInfo = userInfos[searchUserInfo.id];
+ return (
+ !userInfo || !excludeStatuses.includes(userInfo.relationshipStatus)
+ );
+ },
+ );
+ setServerSearchResults(filteredServerSearchResults);
+ },
+ [routeName, userStoreSearchIndex, userInfos, searchUsersOnServer],
+ );
- async searchUsers(usernamePrefix: string) {
- if (usernamePrefix.length === 0) {
- return [];
- }
+ const overlayContext = React.useContext(OverlayContext);
+ invariant(overlayContext, 'RelationshipList should have OverlayContext');
+ const scrollEnabled = overlayContext.scrollBlockingModalStatus === 'closed';
- const { userInfos } = await this.props.searchUsers(usernamePrefix);
- return userInfos;
- }
+ const tagInputRef = React.useRef<?BaseTagInput<GlobalAccountUserInfo>>();
+ const flatListContainerRef = React.useRef<?React.ElementRef<typeof View>>();
- onFlatListContainerLayout = () => {
- const { flatListContainerRef } = this;
+ const keyboardState = React.useContext(KeyboardContext);
+ const keyboardNotShowing = !!(
+ keyboardState && !keyboardState.keyboardShowing
+ );
+ const [verticalBounds, setVerticalBounds] = React.useState<?VerticalBounds>(
+ null,
+ );
+ const onFlatListContainerLayout = React.useCallback(() => {
if (!flatListContainerRef.current) {
return;
}
- const { keyboardState } = this.props;
- if (!keyboardState || keyboardState.keyboardShowing) {
+ if (!keyboardNotShowing) {
return;
}
@@ -387,107 +177,266 @@
) {
return;
}
- this.setState({ verticalBounds: { height, y: pageY } });
+ setVerticalBounds({ height, y: pageY });
},
);
- };
+ }, [keyboardNotShowing]);
+
+ const [currentTags, setCurrentTags] = React.useState<
+ $ReadOnlyArray<GlobalAccountUserInfo>,
+ >([]);
- onSelect = (selectedUser: GlobalAccountUserInfo) => {
- this.setState(state => {
- if (state.currentTags.find(o => o.id === selectedUser.id)) {
- return null;
+ const onSelect = React.useCallback(
+ (selectedUser: GlobalAccountUserInfo) => {
+ if (currentTags.find(o => o.id === selectedUser.id)) {
+ return;
}
- return {
- searchInputText: '',
- currentTags: state.currentTags.concat(selectedUser),
- };
- });
- };
+ setSearchInputText('');
+ setCurrentTags(prevCurrentTags => prevCurrentTags.concat(selectedUser));
+ },
+ [currentTags],
+ );
- onPressAdd = () => {
- if (this.state.currentTags.length === 0) {
- return;
- }
- this.props.dispatchActionPromise(
- updateRelationshipsActionTypes,
- this.updateRelationships(),
- );
- };
+ const onUnknownErrorAlertAcknowledged = React.useCallback(() => {
+ setCurrentTags([]);
+ setSearchInputText('');
+ invariant(tagInputRef.current, 'tagInput should be set');
+ tagInputRef.current.focus();
+ }, []);
- async updateRelationships() {
- const routeName = this.props.route.name;
+ const callUpdateRelationships = useServerCall(updateRelationships);
+ const updateRelationshipsOnServer = React.useCallback(async () => {
const action = {
[FriendListRouteName]: relationshipActions.FRIEND,
[BlockListRouteName]: relationshipActions.BLOCK,
}[routeName];
- const userIDs = this.state.currentTags.map(userInfo => userInfo.id);
-
+ const userIDs = currentTags.map(userInfo => userInfo.id);
try {
- const result = await this.props.updateRelationships({
+ const result = await callUpdateRelationships({
action,
userIDs,
});
- this.setState({
- currentTags: [],
- searchInputText: '',
- });
+ setCurrentTags([]);
+ setSearchInputText('');
return result;
} catch (e) {
Alert.alert(
'Unknown error',
'Uhh... try again?',
- [{ text: 'OK', onPress: this.onUnknownErrorAlertAcknowledged }],
- { cancelable: true, onDismiss: this.onUnknownErrorAlertAcknowledged },
+ [{ text: 'OK', onPress: onUnknownErrorAlertAcknowledged }],
+ { cancelable: true, onDismiss: onUnknownErrorAlertAcknowledged },
);
throw e;
}
- }
+ }, [
+ routeName,
+ currentTags,
+ callUpdateRelationships,
+ onUnknownErrorAlertAcknowledged,
+ ]);
+
+ const dispatchActionPromise = useDispatchActionPromise();
+ const noCurrentTags = currentTags.length === 0;
+ const onPressAdd = React.useCallback(() => {
+ if (noCurrentTags) {
+ return;
+ }
+ dispatchActionPromise(
+ updateRelationshipsActionTypes,
+ updateRelationshipsOnServer(),
+ );
+ }, [noCurrentTags, dispatchActionPromise, updateRelationshipsOnServer]);
+ const inputProps = React.useMemo(
+ () => ({
+ ...tagInputProps,
+ onSubmitEditing: onPressAdd,
+ }),
+ [onPressAdd],
+ );
- onErrorAcknowledged = () => {
- invariant(this.tagInput, 'tagInput should be set');
- this.tagInput.focus();
- };
+ const { navigation } = props;
+ const { navigate } = navigation;
+ const styles = useStyles(unboundStyles);
+ const renderItem = React.useCallback(
+ ({ item }: { item: ListItem, ... }) => {
+ if (item.type === 'empty') {
+ const action = {
+ [FriendListRouteName]: 'added',
+ [BlockListRouteName]: 'blocked',
+ }[routeName];
- onUnknownErrorAlertAcknowledged = () => {
- this.setState(
- {
- currentTags: [],
- searchInputText: '',
- },
- this.onErrorAcknowledged,
- );
- };
-
- renderItem = ({ item }: { item: ListItem, ... }) => {
- if (item.type === 'empty') {
- const action = {
- [FriendListRouteName]: 'added',
- [BlockListRouteName]: 'blocked',
- }[this.props.route.name];
-
- const emptyMessage =
- item.because === 'no-relationships'
- ? `You haven't ${action} any users yet`
- : 'No results';
-
- return <Text style={this.props.styles.emptyText}>{emptyMessage}</Text>;
- } else if (item.type === 'header' || item.type === 'footer') {
- return <View style={this.props.styles.separator} />;
- } else if (item.type === 'user') {
- return (
- <RelationshipListItem
- userInfo={item.userInfo}
- lastListItem={item.lastListItem}
- verticalBounds={item.verticalBounds}
- navigate={this.props.navigation.navigate}
- relationshipListRoute={this.props.route}
- onSelect={this.onSelect}
+ const emptyMessage =
+ item.because === 'no-relationships'
+ ? `You haven't ${action} any users yet`
+ : 'No results';
+
+ return <Text style={styles.emptyText}>{emptyMessage}</Text>;
+ } else if (item.type === 'header' || item.type === 'footer') {
+ return <View style={styles.separator} />;
+ } else if (item.type === 'user') {
+ return (
+ <RelationshipListItem
+ userInfo={item.userInfo}
+ lastListItem={item.lastListItem}
+ verticalBounds={item.verticalBounds}
+ navigate={navigate}
+ relationshipListRoute={route}
+ onSelect={onSelect}
+ />
+ );
+ } else {
+ invariant(false, `unexpected RelationshipList item type ${item.type}`);
+ }
+ },
+ [routeName, navigate, route, onSelect, styles.emptyText, styles.separator],
+ );
+
+ const { setOptions } = navigation;
+ const prevNoCurrentTags = React.useRef(noCurrentTags);
+ React.useEffect(() => {
+ let setSaveButtonDisabled;
+ if (!prevNoCurrentTags.current && noCurrentTags) {
+ setSaveButtonDisabled = true;
+ } else if (prevNoCurrentTags.current && !noCurrentTags) {
+ setSaveButtonDisabled = false;
+ }
+ prevNoCurrentTags.current = noCurrentTags;
+ if (setSaveButtonDisabled === undefined) {
+ return;
+ }
+ setOptions({
+ // eslint-disable-next-line react/display-name
+ headerRight: () => (
+ <LinkButton
+ text="Save"
+ onPress={onPressAdd}
+ disabled={setSaveButtonDisabled}
/>
- );
- } else {
- invariant(false, `unexpected RelationshipList item type ${item.type}`);
+ ),
+ });
+ }, [setOptions, noCurrentTags, onPressAdd]);
+
+ const relationships = useSelector(userRelationshipsSelector);
+ const viewerID = useSelector(
+ state => state.currentUserInfo && state.currentUserInfo.id,
+ );
+ const listData = React.useMemo(() => {
+ const defaultUsers = {
+ [FriendListRouteName]: relationships.friends,
+ [BlockListRouteName]: relationships.blocked,
+ }[routeName];
+
+ const excludeUserIDsArray = currentTags
+ .map(userInfo => userInfo.id)
+ .concat(viewerID || []);
+
+ const excludeUserIDs = new Set(excludeUserIDsArray);
+
+ let displayUsers = defaultUsers;
+
+ if (searchInputText !== '') {
+ const mergedUserInfos: { [id: string]: AccountUserInfo } = {};
+ for (const userInfo of serverSearchResults) {
+ mergedUserInfos[userInfo.id] = userInfo;
+ }
+ for (const id of userStoreSearchResults) {
+ const { username, relationshipStatus } = userInfos[id];
+ if (username) {
+ mergedUserInfos[id] = { id, username, relationshipStatus };
+ }
+ }
+
+ const sortToEnd = [];
+ const userSearchResults = [];
+ const sortRelationshipTypesToEnd = {
+ [FriendListRouteName]: [userRelationshipStatus.FRIEND],
+ [BlockListRouteName]: [
+ userRelationshipStatus.BLOCKED_BY_VIEWER,
+ userRelationshipStatus.BOTH_BLOCKED,
+ ],
+ }[routeName];
+ for (const userID in mergedUserInfos) {
+ if (excludeUserIDs.has(userID)) {
+ continue;
+ }
+
+ const userInfo = mergedUserInfos[userID];
+ if (sortRelationshipTypesToEnd.includes(userInfo.relationshipStatus)) {
+ sortToEnd.push(userInfo);
+ } else {
+ userSearchResults.push(userInfo);
+ }
+ }
+
+ displayUsers = userSearchResults.concat(sortToEnd);
}
- };
+
+ let emptyItem;
+ if (displayUsers.length === 0 && searchInputText === '') {
+ emptyItem = { type: 'empty', because: 'no-relationships' };
+ } else if (displayUsers.length === 0) {
+ emptyItem = { type: 'empty', because: 'no-results' };
+ }
+
+ const mappedUsers = displayUsers.map((userInfo, index) => ({
+ type: 'user',
+ userInfo,
+ lastListItem: displayUsers.length - 1 === index,
+ verticalBounds,
+ }));
+
+ return []
+ .concat(emptyItem ? emptyItem : [])
+ .concat(emptyItem ? [] : { type: 'header' })
+ .concat(mappedUsers)
+ .concat(emptyItem ? [] : { type: 'footer' });
+ }, [
+ routeName,
+ relationships,
+ verticalBounds,
+ searchInputText,
+ serverSearchResults,
+ userStoreSearchResults,
+ userInfos,
+ viewerID,
+ currentTags,
+ ]);
+
+ const indicatorStyle = useIndicatorStyle();
+ return (
+ <View style={styles.container}>
+ <View style={styles.tagInputContainer}>
+ <Text style={styles.tagInputLabel}>Search:</Text>
+ <View style={styles.tagInput}>
+ <TagInput
+ value={currentTags}
+ onChange={setCurrentTags}
+ text={searchInputText}
+ onChangeText={onChangeSearchText}
+ labelExtractor={tagDataLabelExtractor}
+ ref={tagInputRef}
+ inputProps={inputProps}
+ maxHeight={36}
+ />
+ </View>
+ </View>
+ <View
+ ref={flatListContainerRef}
+ onLayout={onFlatListContainerLayout}
+ style={styles.container}
+ >
+ <FlatList
+ contentContainerStyle={styles.contentContainer}
+ data={listData}
+ renderItem={renderItem}
+ keyExtractor={keyExtractor}
+ scrollEnabled={scrollEnabled}
+ indicatorStyle={indicatorStyle}
+ keyboardShouldPersistTaps="handled"
+ />
+ </View>
+ </View>
+ );
}
const unboundStyles = {
@@ -536,40 +485,9 @@
registerFetchKey(searchUsersActionTypes);
registerFetchKey(updateRelationshipsActionTypes);
-const ConnectedRelationshipList: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedRelationshipList(props: BaseProps) {
- const relationships = useSelector(userRelationshipsSelector);
- const userInfos = useSelector(state => state.userStore.userInfos);
- const viewerID = useSelector(
- state => state.currentUserInfo && state.currentUserInfo.id,
- );
- const userStoreSearchIndex = useSelector(userStoreSearchIndexSelector);
- const styles = useStyles(unboundStyles);
- const indicatorStyle = useIndicatorStyle();
- const overlayContext = React.useContext(OverlayContext);
- const keyboardState = React.useContext(KeyboardContext);
-
- const dispatchActionPromise = useDispatchActionPromise();
- const callSearchUsers = useServerCall(searchUsers);
- const callUpdateRelationships = useServerCall(updateRelationships);
-
- return (
- <RelationshipList
- {...props}
- relationships={relationships}
- userInfos={userInfos}
- viewerID={viewerID}
- userStoreSearchIndex={userStoreSearchIndex}
- styles={styles}
- indicatorStyle={indicatorStyle}
- overlayContext={overlayContext}
- keyboardState={keyboardState}
- dispatchActionPromise={dispatchActionPromise}
- searchUsers={callSearchUsers}
- updateRelationships={callUpdateRelationships}
- />
- );
- },
+const MemoizedRelationshipList: React.ComponentType<Props> = React.memo<Props>(
+ RelationshipList,
);
+MemoizedRelationshipList.displayName = 'RelationshipList';
-export default ConnectedRelationshipList;
+export default MemoizedRelationshipList;

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 16, 12:53 AM (16 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5941027
Default Alt Text
D6435.1768524800.diff (27 KB)

Event Timeline