diff --git a/lib/shared/search-utils.js b/lib/shared/search-utils.js --- a/lib/shared/search-utils.js +++ b/lib/shared/search-utils.js @@ -1,6 +1,7 @@ // @flow import * as React from 'react'; +import { useSelector } from 'react-redux'; import SearchIndex from './search-index.js'; import { @@ -12,6 +13,10 @@ searchMessages, searchMessagesActionTypes, } from '../actions/message-actions.js'; +import { + searchUsers, + searchUsersActionTypes, +} from '../actions/user-actions.js'; import genesis from '../facts/genesis.js'; import type { RawMessageInfo } from '../types/message-types.js'; import { userRelationshipStatus } from '../types/relationship-types.js'; @@ -243,4 +248,47 @@ ); } -export { getPotentialMemberItems, notFriendNotice, useSearchMessages }; +function useSearchUsers( + usernameInputText: string, +): $ReadOnlyArray { + const currentUserID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + + const [serverSearchResults, setServerSearchResults] = React.useState< + $ReadOnlyArray, + >([]); + const callSearchUsers = useServerCall(searchUsers); + const dispatchActionPromise = useDispatchActionPromise(); + React.useEffect(() => { + const searchUsersPromise = (async () => { + if (usernameInputText.length === 0) { + setServerSearchResults([]); + } else { + try { + const { userInfos } = await callSearchUsers(usernameInputText); + setServerSearchResults( + userInfos.filter(({ id }) => id !== currentUserID), + ); + } catch (err) { + setServerSearchResults([]); + } + } + })(); + dispatchActionPromise(searchUsersActionTypes, searchUsersPromise); + }, [ + callSearchUsers, + currentUserID, + dispatchActionPromise, + usernameInputText, + ]); + + return serverSearchResults; +} + +export { + getPotentialMemberItems, + notFriendNotice, + useSearchMessages, + useSearchUsers, +}; diff --git a/lib/types/user-types.js b/lib/types/user-types.js --- a/lib/types/user-types.js +++ b/lib/types/user-types.js @@ -131,11 +131,9 @@ }; export type UserListItem = { - +id: string, - +username: string, + ...AccountUserInfo, +disabled?: boolean, +notice?: string, +alertText?: string, +alertTitle?: string, - +avatar?: ?ClientAvatar, }; diff --git a/native/chat/compose-subchannel.react.js b/native/chat/compose-subchannel.react.js --- a/native/chat/compose-subchannel.react.js +++ b/native/chat/compose-subchannel.react.js @@ -248,13 +248,13 @@ ); const onUserSelect = React.useCallback( - (userID: string) => { - if (userInfoInputIDs.some(existingUserID => userID === existingUserID)) { + ({ id }: AccountUserInfo) => { + if (userInfoInputIDs.some(existingUserID => id === existingUserID)) { return; } setUserInfoInputArray(oldUserInfoInputArray => [ ...oldUserInfoInputArray, - otherUserInfos[userID], + otherUserInfos[id], ]); setUsernameInputText(''); }, diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js --- a/native/chat/message-list-container.react.js +++ b/native/chat/message-list-container.react.js @@ -12,7 +12,10 @@ userInfoSelectorForPotentialMembers, userSearchIndexForPotentialMembers, } from 'lib/selectors/user-selectors.js'; -import { getPotentialMemberItems } from 'lib/shared/search-utils.js'; +import { + getPotentialMemberItems, + useSearchUsers, +} from 'lib/shared/search-utils.js'; import { useExistingThreadInfoFinder, pendingThreadType, @@ -58,7 +61,6 @@ +userInfoInputArray: $ReadOnlyArray, +updateTagInput: (items: $ReadOnlyArray) => void, +resolveToUser: (user: AccountUserInfo) => void, - +otherUserInfos: { [id: string]: AccountUserInfo }, +userSearchResults: $ReadOnlyArray, +threadInfo: ThreadInfo, +genesisThreadInfo: ?ThreadInfo, @@ -154,7 +156,6 @@ userInfoInputArray={userInfoInputArray} updateTagInput={this.props.updateTagInput} resolveToUser={this.props.resolveToUser} - otherUserInfos={this.props.otherUserInfos} userSearchResults={this.props.userSearchResults} /> @@ -249,16 +250,24 @@ const otherUserInfos = useSelector(userInfoSelectorForPotentialMembers); const userSearchIndex = useSelector(userSearchIndexForPotentialMembers); - const userSearchResults = React.useMemo( - () => - getPotentialMemberItems({ - text: usernameInputText, - userInfos: otherUserInfos, - searchIndex: userSearchIndex, - excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id), - }), - [usernameInputText, otherUserInfos, userSearchIndex, userInfoInputArray], - ); + + const serverSearchResults = useSearchUsers(usernameInputText); + + const userSearchResults = React.useMemo(() => { + return getPotentialMemberItems({ + text: usernameInputText, + userInfos: otherUserInfos, + searchIndex: userSearchIndex, + excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id), + includeServerSearchUsers: serverSearchResults, + }); + }, [ + usernameInputText, + otherUserInfos, + userSearchIndex, + userInfoInputArray, + serverSearchResults, + ]); const [baseThreadInfo, setBaseThreadInfo] = React.useState( props.route.params.threadInfo, @@ -421,7 +430,6 @@ userInfoInputArray={userInfoInputArray} updateTagInput={updateTagInput} resolveToUser={resolveToUser} - otherUserInfos={otherUserInfos} userSearchResults={userSearchResults} threadInfo={threadInfo} genesisThreadInfo={genesisThreadInfo} diff --git a/native/chat/message-list-thread-search.react.js b/native/chat/message-list-thread-search.react.js --- a/native/chat/message-list-thread-search.react.js +++ b/native/chat/message-list-thread-search.react.js @@ -19,7 +19,6 @@ +userInfoInputArray: $ReadOnlyArray, +updateTagInput: (items: $ReadOnlyArray) => void, +resolveToUser: (user: AccountUserInfo) => void, - +otherUserInfos: { [id: string]: AccountUserInfo }, +userSearchResults: $ReadOnlyArray, }; @@ -36,7 +35,6 @@ userInfoInputArray, updateTagInput, resolveToUser, - otherUserInfos, userSearchResults, }) { const styles = useStyles(unboundStyles); @@ -61,14 +59,13 @@ }, [userSearchResults, userInfoInputArray]); const onUserSelect = React.useCallback( - (userID: string) => { + (userInfo: AccountUserInfo) => { for (const existingUserInfo of userInfoInputArray) { - if (userID === existingUserInfo.id) { + if (userInfo.id === existingUserInfo.id) { return; } } - const userInfo = otherUserInfos[userID]; - if (nonFriends.has(userID)) { + if (nonFriends.has(userInfo.id)) { resolveToUser(userInfo); return; } @@ -79,7 +76,6 @@ [ userInfoInputArray, nonFriends, - otherUserInfos, updateTagInput, resolveToUser, updateUsernameInput, diff --git a/native/chat/settings/add-users-modal.react.js b/native/chat/settings/add-users-modal.react.js --- a/native/chat/settings/add-users-modal.react.js +++ b/native/chat/settings/add-users-modal.react.js @@ -210,16 +210,16 @@ ); const onUserSelect = React.useCallback( - (userID: string) => { + ({ id }: AccountUserInfo) => { if (isLoading) { return; } - if (userInfoInputIDs.some(existingUserID => userID === existingUserID)) { + if (userInfoInputIDs.some(existingUserID => id === existingUserID)) { return; } setUserInfoInputArray(oldUserInfoInputArray => [ ...oldUserInfoInputArray, - otherUserInfos[userID], + otherUserInfos[id], ]); setUsernameInputText(''); }, diff --git a/native/community-creation/community-creation-members.react.js b/native/community-creation/community-creation-members.react.js --- a/native/community-creation/community-creation-members.react.js +++ b/native/community-creation/community-creation-members.react.js @@ -165,13 +165,13 @@ ); const onSelectUser = React.useCallback( - userID => { - if (selectedUserIDs.some(existingUserID => userID === existingUserID)) { + ({ id }: AccountUserInfo) => { + if (selectedUserIDs.some(existingUserID => id === existingUserID)) { return; } setSelectedUsers(oldUserInfoInputArray => [ ...oldUserInfoInputArray, - otherUserInfos[userID], + otherUserInfos[id], ]); setUsernameInputText(''); }, diff --git a/native/components/user-list-user.react.js b/native/components/user-list-user.react.js --- a/native/components/user-list-user.react.js +++ b/native/components/user-list-user.react.js @@ -3,7 +3,7 @@ import * as React from 'react'; import { Text, Platform, Alert } from 'react-native'; -import type { UserListItem } from 'lib/types/user-types.js'; +import type { UserListItem, AccountUserInfo } from 'lib/types/user-types.js'; import Button from './button.react.js'; import { SingleLine } from './single-line.react.js'; @@ -19,7 +19,7 @@ type BaseProps = { +userInfo: UserListItem, - +onSelect: (userID: string) => void, + +onSelect: (user: AccountUserInfo) => void, +textStyle?: TextStyle, }; type Props = { @@ -58,7 +58,9 @@ onSelect = () => { const { userInfo } = this.props; if (!userInfo.alertText) { - this.props.onSelect(userInfo.id); + const { alertText, alertTitle, notice, disabled, ...accountUserInfo } = + userInfo; + this.props.onSelect(accountUserInfo); return; } Alert.alert(userInfo.alertTitle, userInfo.alertText, [{ text: 'OK' }], { diff --git a/native/components/user-list.react.js b/native/components/user-list.react.js --- a/native/components/user-list.react.js +++ b/native/components/user-list.react.js @@ -4,7 +4,7 @@ import * as React from 'react'; import { FlatList } from 'react-native'; -import type { UserListItem } from 'lib/types/user-types.js'; +import type { UserListItem, AccountUserInfo } from 'lib/types/user-types.js'; import { UserListUser, getUserListItemHeight } from './user-list-user.react.js'; import { type IndicatorStyle, useIndicatorStyle } from '../themes/colors.js'; @@ -12,7 +12,7 @@ type BaseProps = { +userInfos: $ReadOnlyArray, - +onSelect: (userID: string) => void, + +onSelect: (user: AccountUserInfo) => void, +itemTextStyle?: TextStyle, }; type Props = {