Skip to content

Commit

Permalink
[lib] Exclude users that don't support thick threads from results
Browse files Browse the repository at this point in the history
Summary:
When a user can't be added to thick thread, they shouldn't be included in search results.

https://linear.app/comm/issue/ENG-9257/limit-chat-candidates-to-users-who-are-registered-with-identity

Depends on D13404

Test Plan: Checked that the user who don't support thick threads aren't shown in add member modals of thick threads.

Reviewers: kamil, will

Reviewed By: kamil

Subscribers: ashoat

Differential Revision: https://phab.comm.dev/D13405
  • Loading branch information
palys-swm committed Sep 20, 2024
1 parent 2d42431 commit b74fa44
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 17 deletions.
48 changes: 35 additions & 13 deletions lib/shared/search-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import * as React from 'react';

import { messageID } from './message-utils.js';
import SearchIndex from './search-index.js';
import { getContainingThreadID, userIsMember } from './thread-utils.js';
import {
getContainingThreadID,
userIsMember,
userSupportsThickThreads,
} from './thread-utils.js';
import {
searchMessagesActionTypes,
useSearchMessages as useSearchMessagesAction,
Expand All @@ -24,6 +28,7 @@ import type {
} from '../selectors/chat-selectors.js';
import { useUserSearchIndex } from '../selectors/nav-selectors.js';
import { relationshipBlockedInEitherDirection } from '../shared/relationship-utils.js';
import type { AuxUserInfos } from '../types/aux-user-types.js';
import type { MessageInfo, RawMessageInfo } from '../types/message-types.js';
import type {
RoleInfo,
Expand All @@ -36,6 +41,7 @@ import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types
import {
type ThreadType,
threadTypeIsSidebar,
threadTypeIsThick,
} from '../types/thread-types-enum.js';
import type {
AccountUserInfo,
Expand All @@ -52,7 +58,7 @@ const notFriendNotice = 'not friend';

function appendUserInfo({
results,
excludeUserIDs,
shouldExcludeUserFromResult,
userInfo,
parentThreadInfo,
communityThreadInfo,
Expand All @@ -65,14 +71,14 @@ function appendUserInfo({
isMemberOfContainingThread: boolean,
},
},
+excludeUserIDs: $ReadOnlyArray<string>,
+shouldExcludeUserFromResult: (userID: string) => boolean,
+userInfo: AccountUserInfo | GlobalAccountUserInfo,
+parentThreadInfo: ?ThreadInfo,
+communityThreadInfo: ?ThreadInfo,
+containingThreadInfo: ?ThreadInfo,
}) {
const { id } = userInfo;
if (excludeUserIDs.includes(id) || id in results) {
if (id in results || shouldExcludeUserFromResult(id)) {
return;
}

Expand Down Expand Up @@ -104,6 +110,7 @@ function appendUserInfo({
function usePotentialMemberItems({
text,
userInfos,
auxUserInfos,
excludeUserIDs,
includeServerSearchUsers,
inputParentThreadInfo,
Expand All @@ -112,6 +119,7 @@ function usePotentialMemberItems({
}: {
+text: string,
+userInfos: { +[id: string]: AccountUserInfo },
+auxUserInfos: AuxUserInfos,
+excludeUserIDs: $ReadOnlyArray<string>,
+includeServerSearchUsers?: $ReadOnlyArray<GlobalAccountUserInfo>,
+inputParentThreadInfo?: ?ThreadInfo,
Expand Down Expand Up @@ -149,6 +157,20 @@ function usePotentialMemberItems({
return null;
}, [containingThreadID, communityThreadInfo, parentThreadInfo]);

const shouldExcludeUserFromResult = React.useCallback(
(userID: string) => {
if (excludeUserIDs.includes(userID)) {
return true;
}
return !!(
threadType &&
threadTypeIsThick(threadType) &&
!userSupportsThickThreads(userID, auxUserInfos)
);
},
[auxUserInfos, excludeUserIDs, threadType],
);

const filteredUserResults = React.useMemo(() => {
const results: {
[id: string]: {
Expand All @@ -161,7 +183,7 @@ function usePotentialMemberItems({
for (const id in userInfos) {
appendUserInfo({
results,
excludeUserIDs,
shouldExcludeUserFromResult,
userInfo: userInfos[id],
parentThreadInfo,
communityThreadInfo,
Expand All @@ -173,7 +195,7 @@ function usePotentialMemberItems({
for (const id of ids) {
appendUserInfo({
results,
excludeUserIDs,
shouldExcludeUserFromResult,
userInfo: userInfos[id],
parentThreadInfo,
communityThreadInfo,
Expand All @@ -186,7 +208,7 @@ function usePotentialMemberItems({
for (const userInfo of includeServerSearchUsers) {
appendUserInfo({
results,
excludeUserIDs,
shouldExcludeUserFromResult,
userInfo,
parentThreadInfo,
communityThreadInfo,
Expand Down Expand Up @@ -214,14 +236,14 @@ function usePotentialMemberItems({

return userResults;
}, [
text,
userInfos,
searchIndex,
excludeUserIDs,
communityThreadInfo,
containingThreadInfo,
includeServerSearchUsers,
parentThreadInfo,
containingThreadInfo,
communityThreadInfo,
searchIndex,
shouldExcludeUserFromResult,
text,
userInfos,
]);

const sortedMembers = React.useMemo(() => {
Expand Down
7 changes: 3 additions & 4 deletions lib/shared/thread-actions-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
threadIsPending,
threadOtherMembers,
pendingThreadType,
userSupportsThickThreads,
} from './thread-utils.js';
import {
newThreadActionTypes,
Expand Down Expand Up @@ -136,10 +137,8 @@ async function createRealThreadFromPendingThread({
otherMemberIDs.length > 0,
'otherMemberIDs should not be empty for threads',
);
const allUsersSupportThickThreads = otherMemberIDs.every(
memberID =>
auxUserInfos[memberID]?.deviceList &&
auxUserInfos[memberID].deviceList.devices.length > 0,
const allUsersSupportThickThreads = otherMemberIDs.every(memberID =>
userSupportsThickThreads(memberID, auxUserInfos),
);
if (threadTypeIsThick(threadInfo.type) && allUsersSupportThickThreads) {
const type = assertThickThreadType(
Expand Down
12 changes: 12 additions & 0 deletions lib/shared/thread-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
getRelativeMemberInfos,
usersWithPersonalThreadSelector,
} from '../selectors/user-selectors.js';
import type { AuxUserInfos } from '../types/aux-user-types.js';
import type {
RelativeMemberInfo,
RawThreadInfo,
Expand Down Expand Up @@ -1795,6 +1796,16 @@ function createThreadTimestamps(
};
}

function userSupportsThickThreads(
userID: string,
auxUserInfos: AuxUserInfos,
): boolean {
return (
!!auxUserInfos[userID]?.deviceList &&
auxUserInfos[userID].deviceList.devices.length > 0
);
}

export {
threadHasPermission,
useCommunityRootMembersToRole,
Expand Down Expand Up @@ -1861,4 +1872,5 @@ export {
extractMentionedMembers,
isMemberActive,
createThreadTimestamps,
userSupportsThickThreads,
};
2 changes: 2 additions & 0 deletions native/chat/compose-subchannel.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,11 @@ function ComposeSubchannel(props: Props): React.Node {
const communityThreadInfo = useSelector(state =>
community ? threadInfoSelector(state)[community] : null,
);
const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userSearchResults = usePotentialMemberItems({
text: usernameInputText,
userInfos: otherUserInfos,
auxUserInfos,
excludeUserIDs: userInfoInputIDs,
inputParentThreadInfo: parentThreadInfo,
inputCommunityThreadInfo: communityThreadInfo,
Expand Down
2 changes: 2 additions & 0 deletions native/chat/message-list-container.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,11 @@ const ConnectedMessageListContainer: React.ComponentType<BaseProps> =

const serverSearchResults = useSearchUsers(usernameInputText);

const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userSearchResults = usePotentialMemberItems({
text: usernameInputText,
userInfos: otherUserInfos,
auxUserInfos,
excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id),
includeServerSearchUsers: serverSearchResults,
});
Expand Down
2 changes: 2 additions & 0 deletions native/chat/settings/add-users-modal.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,11 @@ function AddUsersModal(props: Props): React.Node {
const communityThreadInfo = useSelector(state =>
community ? threadInfoSelector(state)[community] : null,
);
const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userSearchResults = usePotentialMemberItems({
text: usernameInputText,
userInfos: otherUserInfos,
auxUserInfos,
excludeUserIDs,
inputParentThreadInfo: parentThreadInfo,
inputCommunityThreadInfo: communityThreadInfo,
Expand Down
3 changes: 3 additions & 0 deletions web/chat/chat-thread-composer.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import Search from '../components/search.react.js';
import type { InputState } from '../input/input-state.js';
import Alert from '../modals/alert.react.js';
import { updateNavInfoActionType } from '../redux/action-types.js';
import { useSelector } from '../redux/redux-utils.js';

type Props = {
+userInfoInputArray: $ReadOnlyArray<AccountUserInfo>,
Expand All @@ -57,9 +58,11 @@ function ChatThreadComposer(props: Props): React.Node {

const searchResults = useSearchUsers(usernameInputText);

const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userListItems = usePotentialMemberItems({
text: usernameInputText,
userInfos: otherUserInfos,
auxUserInfos,
excludeUserIDs: userInfoInputIDs,
includeServerSearchUsers: searchResults,
});
Expand Down
4 changes: 4 additions & 0 deletions web/settings/relationship/add-users-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,11 @@ function useAddMembersListUserInfos(params: UseAddMembersListUserInfosParams): {
[previouslySelectedUsers, threadInfo.members],
);

const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userSearchResults = usePotentialMemberItems({
text: searchText,
userInfos: otherUserInfos,
auxUserInfos,
excludeUserIDs,
inputParentThreadInfo: parentThreadInfo,
inputCommunityThreadInfo: communityThreadInfo,
Expand Down Expand Up @@ -213,9 +215,11 @@ function useSubchannelAddMembersListUserInfos(
[previouslySelectedUsers],
);

const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userSearchResults = usePotentialMemberItems({
text: searchText,
userInfos: otherUserInfos,
auxUserInfos,
excludeUserIDs: previouslySelectedUserIDs,
inputParentThreadInfo: parentThreadInfo,
inputCommunityThreadInfo: communityThreadInfo,
Expand Down

0 comments on commit b74fa44

Please sign in to comment.