Changeset View
Changeset View
Standalone View
Standalone View
native/profile/relationship-list.react.js
// @flow | // @flow | ||||
import invariant from 'invariant'; | import invariant from 'invariant'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { View, Text, Platform } from 'react-native'; | import { View, Text, Platform } from 'react-native'; | ||||
import { FlatList } from 'react-native-gesture-handler'; | import { FlatList } from 'react-native-gesture-handler'; | ||||
import { | import { | ||||
updateRelationshipsActionTypes, | updateRelationshipsActionTypes, | ||||
updateRelationships, | updateRelationships, | ||||
} from 'lib/actions/relationship-actions.js'; | } from 'lib/actions/relationship-actions.js'; | ||||
import { useENSNames } from 'lib/hooks/ens-cache.js'; | import { useENSNames } from 'lib/hooks/ens-cache.js'; | ||||
import { registerFetchKey } from 'lib/reducers/loading-reducer.js'; | import { registerFetchKey } from 'lib/reducers/loading-reducer.js'; | ||||
import { useUserSearchIndex } from 'lib/selectors/nav-selectors.js'; | |||||
import { userRelationshipsSelector } from 'lib/selectors/relationship-selectors.js'; | import { userRelationshipsSelector } from 'lib/selectors/relationship-selectors.js'; | ||||
import { userStoreSearchIndex as userStoreSearchIndexSelector } from 'lib/selectors/user-selectors.js'; | |||||
import { useSearchUsers } from 'lib/shared/search-utils.js'; | import { useSearchUsers } from 'lib/shared/search-utils.js'; | ||||
import { | import { | ||||
userRelationshipStatus, | userRelationshipStatus, | ||||
relationshipActions, | relationshipActions, | ||||
} from 'lib/types/relationship-types.js'; | } from 'lib/types/relationship-types.js'; | ||||
import type { | import type { | ||||
GlobalAccountUserInfo, | GlobalAccountUserInfo, | ||||
AccountUserInfo, | AccountUserInfo, | ||||
} from 'lib/types/user-types.js'; | } from 'lib/types/user-types.js'; | ||||
import { | import { | ||||
useServerCall, | useServerCall, | ||||
useDispatchActionPromise, | useDispatchActionPromise, | ||||
} from 'lib/utils/action-utils.js'; | } from 'lib/utils/action-utils.js'; | ||||
import { values } from 'lib/utils/objects.js'; | |||||
import type { ProfileNavigationProp } from './profile.react.js'; | import type { ProfileNavigationProp } from './profile.react.js'; | ||||
import RelationshipListItem from './relationship-list-item.react.js'; | import RelationshipListItem from './relationship-list-item.react.js'; | ||||
import LinkButton from '../components/link-button.react.js'; | import LinkButton from '../components/link-button.react.js'; | ||||
import { createTagInput, BaseTagInput } from '../components/tag-input.react.js'; | import { createTagInput, BaseTagInput } from '../components/tag-input.react.js'; | ||||
import { KeyboardContext } from '../keyboard/keyboard-state.js'; | import { KeyboardContext } from '../keyboard/keyboard-state.js'; | ||||
import { OverlayContext } from '../navigation/overlay-context.js'; | import { OverlayContext } from '../navigation/overlay-context.js'; | ||||
import type { NavigationRoute } from '../navigation/route-names.js'; | import type { NavigationRoute } from '../navigation/route-names.js'; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
const tagDataLabelExtractor = (userInfo: GlobalAccountUserInfo) => | const tagDataLabelExtractor = (userInfo: GlobalAccountUserInfo) => | ||||
userInfo.username; | userInfo.username; | ||||
type Props = { | type Props = { | ||||
+navigation: ProfileNavigationProp<'FriendList' | 'BlockList'>, | +navigation: ProfileNavigationProp<'FriendList' | 'BlockList'>, | ||||
+route: NavigationRoute<'FriendList' | 'BlockList'>, | +route: NavigationRoute<'FriendList' | 'BlockList'>, | ||||
}; | }; | ||||
function RelationshipList(props: Props): React.Node { | function RelationshipList(props: Props): React.Node { | ||||
const userInfos = useSelector(state => state.userStore.userInfos); | |||||
const [searchInputText, setSearchInputText] = React.useState<string>(''); | |||||
const [userStoreSearchResults, setUserStoreSearchResults] = React.useState< | |||||
$ReadOnlySet<string>, | |||||
>(new Set()); | |||||
const { route } = props; | const { route } = props; | ||||
const routeName = route.name; | const routeName = route.name; | ||||
const excludeStatuses = React.useMemo( | const excludeStatuses = React.useMemo( | ||||
() => | () => | ||||
({ | ({ | ||||
[FriendListRouteName]: [ | [FriendListRouteName]: [ | ||||
userRelationshipStatus.BLOCKED_VIEWER, | userRelationshipStatus.BLOCKED_VIEWER, | ||||
userRelationshipStatus.BOTH_BLOCKED, | userRelationshipStatus.BOTH_BLOCKED, | ||||
], | ], | ||||
[BlockListRouteName]: [], | [BlockListRouteName]: [], | ||||
}[routeName]), | }[routeName]), | ||||
[routeName], | [routeName], | ||||
); | ); | ||||
const userInfos = useSelector(state => state.userStore.userInfos); | |||||
const userInfosArray = React.useMemo( | |||||
() => | |||||
values(userInfos).filter(userInfo => { | |||||
const relationship = userInfo.relationshipStatus; | |||||
return !excludeStatuses.includes(relationship); | |||||
}), | |||||
[userInfos, excludeStatuses], | |||||
); | |||||
const [searchInputText, setSearchInputText] = React.useState<string>(''); | |||||
const [userStoreSearchResults, setUserStoreSearchResults] = React.useState< | |||||
$ReadOnlySet<string>, | |||||
>(new Set()); | |||||
const serverSearchResults = useSearchUsers(searchInputText); | const serverSearchResults = useSearchUsers(searchInputText); | ||||
const filteredServerSearchResults = React.useMemo( | const filteredServerSearchResults = React.useMemo( | ||||
() => | () => | ||||
serverSearchResults.filter(searchUserInfo => { | serverSearchResults.filter(searchUserInfo => { | ||||
const userInfo = userInfos[searchUserInfo.id]; | const userInfo = userInfos[searchUserInfo.id]; | ||||
return ( | return ( | ||||
!userInfo || !excludeStatuses.includes(userInfo.relationshipStatus) | !userInfo || !excludeStatuses.includes(userInfo.relationshipStatus) | ||||
); | ); | ||||
}), | }), | ||||
[serverSearchResults, userInfos, excludeStatuses], | [serverSearchResults, userInfos, excludeStatuses], | ||||
); | ); | ||||
const userStoreSearchIndex = useSelector(userStoreSearchIndexSelector); | const userStoreSearchIndex = useUserSearchIndex(userInfosArray); | ||||
const onChangeSearchText = React.useCallback( | const onChangeSearchText = React.useCallback( | ||||
async (searchText: string) => { | async (searchText: string) => { | ||||
setSearchInputText(searchText); | setSearchInputText(searchText); | ||||
const results = userStoreSearchIndex | const results = userStoreSearchIndex.getSearchResults(searchText); | ||||
.getSearchResults(searchText) | |||||
.filter(userID => { | |||||
const relationship = userInfos[userID].relationshipStatus; | |||||
return !excludeStatuses.includes(relationship); | |||||
}); | |||||
setUserStoreSearchResults(new Set(results)); | setUserStoreSearchResults(new Set(results)); | ||||
}, | }, | ||||
[userStoreSearchIndex, userInfos, excludeStatuses], | [userStoreSearchIndex], | ||||
); | ); | ||||
const overlayContext = React.useContext(OverlayContext); | const overlayContext = React.useContext(OverlayContext); | ||||
invariant(overlayContext, 'RelationshipList should have OverlayContext'); | invariant(overlayContext, 'RelationshipList should have OverlayContext'); | ||||
const scrollEnabled = overlayContext.scrollBlockingModalStatus === 'closed'; | const scrollEnabled = overlayContext.scrollBlockingModalStatus === 'closed'; | ||||
const tagInputRef = React.useRef<?BaseTagInput<GlobalAccountUserInfo>>(); | const tagInputRef = React.useRef<?BaseTagInput<GlobalAccountUserInfo>>(); | ||||
const flatListContainerRef = React.useRef<?React.ElementRef<typeof View>>(); | const flatListContainerRef = React.useRef<?React.ElementRef<typeof View>>(); | ||||
▲ Show 20 Lines • Show All 339 Lines • Show Last 20 Lines |