Skip to content

Commit

Permalink
[lib] Create a thin thread when some users don't support thick threads
Browse files Browse the repository at this point in the history
Summary:
Check if all the users support thick threads and chose thread type accordingly.

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

Test Plan: On both native and web checked if the correct thread type is created, when sending text message, depending on the presence of members in `AuxUserStore`

Reviewers: kamil, will

Reviewed By: kamil

Subscribers: ashoat

Differential Revision: https://phab.comm.dev/D13404
  • Loading branch information
palys-swm committed Sep 20, 2024
1 parent 5b25a90 commit 2d42431
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 32 deletions.
34 changes: 23 additions & 11 deletions lib/selectors/thread-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
roleIsAdminRole,
threadIsPending,
getPendingThreadID,
pendingThreadType,
} from '../shared/thread-utils.js';
import type { ClientAvatar, ClientEmojiAvatar } from '../types/avatar-types';
import type { EntryInfo } from '../types/entry-types.js';
Expand Down Expand Up @@ -461,17 +462,28 @@ const pendingToRealizedThreadIDsSelector: (
const actualMemberIDs = rawThreadInfo.members
.filter(member => member.role)
.map(member => member.id);
const pendingThreadID = getPendingThreadID(
rawThreadInfo.type,
actualMemberIDs,
rawThreadInfo.sourceMessageID,
);
const existingResult = result.get(pendingThreadID);
if (
!existingResult ||
rawThreadInfos[existingResult].creationTime > rawThreadInfo.creationTime
) {
result.set(pendingThreadID, threadID);
// In this function we're generating possible pending thread IDs that
// could become `rawThreadInfos`. It is possible that a thick pending
// thread becomes a thin thread, so we're including it twice in a map -
// for each possible pending thread ID.
const possiblePendingThreadTypes = [
pendingThreadType(actualMemberIDs.length - 1, 'thin', true),
pendingThreadType(actualMemberIDs.length - 1, 'thick', true),
];
for (const type of possiblePendingThreadTypes) {
const pendingThreadID = getPendingThreadID(
type,
actualMemberIDs,
rawThreadInfo.sourceMessageID,
);
const existingResult = result.get(pendingThreadID);
if (
!existingResult ||
rawThreadInfos[existingResult].creationTime >
rawThreadInfo.creationTime
) {
result.set(pendingThreadID, threadID);
}
}
}
return result;
Expand Down
29 changes: 25 additions & 4 deletions lib/shared/thread-actions-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
removeUsersFromThreadActionTypes,
type RemoveUsersFromThreadInput,
} from '../actions/thread-actions.js';
import type { AuxUserInfos } from '../types/aux-user-types.js';
import type { CalendarQuery } from '../types/entry-types.js';
import type {
RelativeMemberInfo,
Expand All @@ -23,6 +24,7 @@ import {
assertThickThreadType,
threadTypeIsThick,
} from '../types/thread-types-enum.js';
import type { ThreadType } from '../types/thread-types-enum.js';
import type {
ChangeThreadSettingsPayload,
ClientNewThinThreadRequest,
Expand Down Expand Up @@ -60,6 +62,7 @@ type CreateRealThreadParameters = {
+handleError?: () => mixed,
+calendarQuery: CalendarQuery,
+usingOlmViaTunnelbrokerForDMs: boolean,
+auxUserInfos: AuxUserInfos,
};

async function createRealThreadFromPendingThread({
Expand All @@ -71,12 +74,20 @@ async function createRealThreadFromPendingThread({
viewerID,
calendarQuery,
usingOlmViaTunnelbrokerForDMs,
}: CreateRealThreadParameters): Promise<string> {
auxUserInfos,
}: CreateRealThreadParameters): Promise<{
+threadID: string,
+threadType: ThreadType,
}> {
if (!threadIsPending(threadInfo.id)) {
return threadInfo.id;
return {
threadID: threadInfo.id,
threadType: threadInfo.type,
};
}

let newThreadID;
let newThreadType = threadInfo.type;

const otherMemberIDs = threadOtherMembers(threadInfo.members, viewerID).map(
member => member.id,
Expand Down Expand Up @@ -125,7 +136,12 @@ async function createRealThreadFromPendingThread({
otherMemberIDs.length > 0,
'otherMemberIDs should not be empty for threads',
);
if (threadTypeIsThick(threadInfo.type)) {
const allUsersSupportThickThreads = otherMemberIDs.every(
memberID =>
auxUserInfos[memberID]?.deviceList &&
auxUserInfos[memberID].deviceList.devices.length > 0,
);
if (threadTypeIsThick(threadInfo.type) && allUsersSupportThickThreads) {
const type = assertThickThreadType(
pendingThreadType(
otherMemberIDs.length,
Expand All @@ -144,6 +160,7 @@ async function createRealThreadFromPendingThread({
initialMemberIDs: otherMemberIDs,
color: threadInfo.color,
});
newThreadType = type;
} else {
const type = assertThinThreadType(
pendingThreadType(
Expand All @@ -166,9 +183,13 @@ async function createRealThreadFromPendingThread({
void dispatchActionPromise(newThreadActionTypes, resultPromise);
const result = await resultPromise;
newThreadID = result.newThreadID;
newThreadType = type;
}
}
return newThreadID;
return {
threadID: newThreadID,
threadType: newThreadType,
};
}

export { removeMemberFromThread, createRealThreadFromPendingThread };
35 changes: 27 additions & 8 deletions native/input/input-state-container.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
threadIsPending,
threadIsPendingSidebar,
} from 'lib/shared/thread-utils.js';
import type { AuxUserInfos } from 'lib/types/aux-user-types';
import type { CalendarQuery } from 'lib/types/entry-types.js';
import type {
Media,
Expand Down Expand Up @@ -85,6 +86,7 @@ import {
threadTypeIsThick,
threadTypeIsSidebar,
} from 'lib/types/thread-types-enum.js';
import type { ThreadType } from 'lib/types/thread-types-enum.js';
import {
type ClientNewThinThreadRequest,
type NewThreadResult,
Expand Down Expand Up @@ -170,6 +172,7 @@ type Props = {
+newThickThread: (request: NewThickThreadRequest) => Promise<string>,
+textMessageCreationSideEffectsFunc: CreationSideEffectsFunc<RawTextMessageInfo>,
+usingOlmViaTunnelbrokerForDMs: boolean,
+auxUserInfos: AuxUserInfos,
};
type State = {
+pendingUploads: PendingMultimediaUploads,
Expand All @@ -185,7 +188,13 @@ class InputStateContainer extends React.PureComponent<Props, State> {
(params: EditInputBarMessageParameters) => void,
> = [];
scrollToMessageCallbacks: Array<(messageID: string) => void> = [];
pendingThreadCreations: Map<string, Promise<string>> = new Map();
pendingThreadCreations: Map<
string,
Promise<{
+threadID: string,
+threadType: ThreadType,
}>,
> = new Map();
pendingThreadUpdateHandlers: Map<string, (ThreadInfo) => mixed> = new Map();
// TODO: flip the switch
// Note that this enables Blob service for encrypted media only
Expand Down Expand Up @@ -349,7 +358,8 @@ class InputStateContainer extends React.PureComponent<Props, State> {
// again.
throw new Error('Thread creation failed');
}
newThreadID = await threadCreationPromise;
const result = await threadCreationPromise;
newThreadID = result.threadID;
} catch (e) {
const copy = cloneError(e);
copy.localID = messageInfo.localID;
Expand Down Expand Up @@ -505,9 +515,9 @@ class InputStateContainer extends React.PureComponent<Props, State> {
}
}

let newThreadID = null;
let threadCreationResult = null;
try {
newThreadID = await this.startThreadCreation(threadInfo);
threadCreationResult = await this.startThreadCreation(threadInfo);
} catch (e) {
const copy = cloneError(e);
copy.localID = messageInfo.localID;
Expand All @@ -524,13 +534,14 @@ class InputStateContainer extends React.PureComponent<Props, State> {

const newMessageInfo = {
...messageInfo,
threadID: newThreadID,
threadID: threadCreationResult?.threadID,
time: Date.now(),
};

const newThreadInfo = {
...threadInfo,
id: newThreadID,
id: threadCreationResult?.threadID,
type: threadCreationResult?.threadType ?? threadInfo.type,
};

void this.props.dispatchActionPromise(
Expand All @@ -545,9 +556,14 @@ class InputStateContainer extends React.PureComponent<Props, State> {
);
};

startThreadCreation(threadInfo: ThreadInfo): Promise<string> {
startThreadCreation(
threadInfo: ThreadInfo,
): Promise<{ +threadID: string, +threadType: ThreadType }> {
if (!threadIsPending(threadInfo.id)) {
return Promise.resolve(threadInfo.id);
return Promise.resolve({
threadID: threadInfo.id,
threadType: threadInfo.type,
});
}
let threadCreationPromise = this.pendingThreadCreations.get(threadInfo.id);
if (!threadCreationPromise) {
Expand All @@ -561,6 +577,7 @@ class InputStateContainer extends React.PureComponent<Props, State> {
viewerID: this.props.viewerID,
calendarQuery,
usingOlmViaTunnelbrokerForDMs: this.props.usingOlmViaTunnelbrokerForDMs,
auxUserInfos: this.props.auxUserInfos,
});
this.pendingThreadCreations.set(threadInfo.id, threadCreationPromise);
}
Expand Down Expand Up @@ -1773,6 +1790,7 @@ const ConnectedInputStateContainer: React.ComponentType<BaseProps> =
const textMessageCreationSideEffectsFunc =
useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);

return (
<InputStateContainer
Expand All @@ -1794,6 +1812,7 @@ const ConnectedInputStateContainer: React.ComponentType<BaseProps> =
staffCanSee={staffCanSee}
textMessageCreationSideEffectsFunc={textMessageCreationSideEffectsFunc}
usingOlmViaTunnelbrokerForDMs={usingOlmViaTunnelbrokerForDMs}
auxUserInfos={auxUserInfos}
/>
);
});
Expand Down
41 changes: 32 additions & 9 deletions web/input/input-state-container.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
threadIsPending,
threadIsPendingSidebar,
} from 'lib/shared/thread-utils.js';
import type { AuxUserInfos } from 'lib/types/aux-user-types.js';
import type { CalendarQuery } from 'lib/types/entry-types.js';
import type {
MediaMission,
Expand All @@ -83,6 +84,7 @@ import {
threadTypeIsSidebar,
threadTypeIsThick,
} from 'lib/types/thread-types-enum.js';
import type { ThreadType } from 'lib/types/thread-types-enum.js';
import {
type ClientNewThinThreadRequest,
type NewThreadResult,
Expand Down Expand Up @@ -170,6 +172,7 @@ type Props = {
+textMessageCreationSideEffectsFunc: CreationSideEffectsFunc<RawTextMessageInfo>,
+identityContext: ?IdentityClientContextType,
+usingOlmViaTunnelbrokerForDMs: boolean,
+auxUserInfos: AuxUserInfos,
};
type WritableState = {
pendingUploads: {
Expand Down Expand Up @@ -201,9 +204,18 @@ class InputStateContainer extends React.PureComponent<Props, State> {
},
};
replyCallbacks: Array<(message: string) => void> = [];
pendingThreadCreations: Map<string, Promise<string>> = new Map<
pendingThreadCreations: Map<
string,
Promise<string>,
Promise<{
+threadID: string,
+threadType: ThreadType,
}>,
> = new Map<
string,
Promise<{
+threadID: string,
+threadType: ThreadType,
}>,
>();
// TODO: flip the switch
// Note that this enables Blob service for encrypted media only
Expand Down Expand Up @@ -481,7 +493,8 @@ class InputStateContainer extends React.PureComponent<Props, State> {
// again.
throw new Error('Thread creation failed');
}
newThreadID = await threadCreationPromise;
const result = await threadCreationPromise;
newThreadID = result.threadID;
} catch (e) {
const copy = cloneError(e);
copy.localID = messageInfo.localID;
Expand Down Expand Up @@ -577,9 +590,15 @@ class InputStateContainer extends React.PureComponent<Props, State> {
}
}

startThreadCreation(threadInfo: ThreadInfo): Promise<string> {
startThreadCreation(threadInfo: ThreadInfo): Promise<{
+threadID: string,
+threadType: ThreadType,
}> {
if (!threadIsPending(threadInfo.id)) {
return Promise.resolve(threadInfo.id);
return Promise.resolve({
threadID: threadInfo.id,
threadType: threadInfo.type,
});
}
let threadCreationPromise = this.pendingThreadCreations.get(threadInfo.id);
if (!threadCreationPromise) {
Expand All @@ -593,6 +612,7 @@ class InputStateContainer extends React.PureComponent<Props, State> {
viewerID: this.props.viewerID,
calendarQuery,
usingOlmViaTunnelbrokerForDMs: this.props.usingOlmViaTunnelbrokerForDMs,
auxUserInfos: this.props.auxUserInfos,
});
this.pendingThreadCreations.set(threadInfo.id, threadCreationPromise);
}
Expand Down Expand Up @@ -1323,9 +1343,9 @@ class InputStateContainer extends React.PureComponent<Props, State> {
}
}

let newThreadID = null;
let threadCreationResult = null;
try {
newThreadID = await this.startThreadCreation(threadInfo);
threadCreationResult = await this.startThreadCreation(threadInfo);
} catch (e) {
const copy = cloneError(e);
copy.localID = messageInfo.localID;
Expand All @@ -1342,13 +1362,14 @@ class InputStateContainer extends React.PureComponent<Props, State> {

const newMessageInfo = {
...messageInfo,
threadID: newThreadID,
threadID: threadCreationResult?.threadID,
time: Date.now(),
};

const newThreadInfo = {
...threadInfo,
id: newThreadID,
id: threadCreationResult?.threadID,
type: threadCreationResult?.threadType ?? threadInfo.type,
};
void this.props.dispatchActionPromise(
sendTextMessageActionTypes,
Expand Down Expand Up @@ -1709,6 +1730,7 @@ const ConnectedInputStateContainer: React.ComponentType<BaseProps> =
const textMessageCreationSideEffectsFunc =
useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);

return (
<InputStateContainer
Expand All @@ -1735,6 +1757,7 @@ const ConnectedInputStateContainer: React.ComponentType<BaseProps> =
textMessageCreationSideEffectsFunc={textMessageCreationSideEffectsFunc}
identityContext={identityContext}
usingOlmViaTunnelbrokerForDMs={usingOlmViaTunnelbrokerForDMs}
auxUserInfos={auxUserInfos}
/>
);
});
Expand Down

0 comments on commit 2d42431

Please sign in to comment.