Page MenuHomePhabricator

D12835.id42877.diff
No OneTemporary

D12835.id42877.diff

diff --git a/lib/shared/dm-ops/add-members-spec.js b/lib/shared/dm-ops/add-members-spec.js
new file mode 100644
--- /dev/null
+++ b/lib/shared/dm-ops/add-members-spec.js
@@ -0,0 +1,119 @@
+// @flow
+
+import invariant from 'invariant';
+import uuid from 'uuid';
+
+import {
+ createRoleAndPermissionForThickThreads,
+ createThickRawThreadInfo,
+} from './create-thread-spec.js';
+import type {
+ DMOperationSpec,
+ ProcessDMOperationUtilities,
+} from './dm-op-spec.js';
+import type { DMAddMembersOperation } from '../../types/dm-ops.js';
+import { messageTypes } from '../../types/message-types-enum.js';
+import { messageTruncationStatus } from '../../types/message-types.js';
+import type { ThickRawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
+import { minimallyEncodeMemberInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
+import { joinThreadSubscription } from '../../types/subscription-types.js';
+import type { ThickMemberInfo } from '../../types/thread-types.js';
+import { updateTypes } from '../../types/update-types-enum.js';
+import type { ClientUpdateInfo } from '../../types/update-types.js';
+import { values } from '../../utils/objects.js';
+import { roleIsDefaultRole, userIsMember } from '../thread-utils.js';
+
+const addMembersSpec: DMOperationSpec<DMAddMembersOperation> = Object.freeze({
+ processDMOperation: async (
+ dmOperation: DMAddMembersOperation,
+ viewerID: string,
+ utilities: ProcessDMOperationUtilities,
+ ) => {
+ const { editorID, time, messageID, addedUserIDs, existingThreadDetails } =
+ dmOperation;
+ const addMembersMessage = {
+ type: messageTypes.ADD_MEMBERS,
+ id: messageID,
+ threadID: existingThreadDetails.threadID,
+ creatorID: editorID,
+ time,
+ addedUserIDs: [...addedUserIDs],
+ };
+
+ const viewerIsAdded = addedUserIDs.includes(viewerID);
+ const updateInfos: Array<ClientUpdateInfo> = [];
+ if (viewerIsAdded) {
+ const newThread = createThickRawThreadInfo(
+ existingThreadDetails,
+ viewerID,
+ );
+ updateInfos.push(
+ {
+ type: updateTypes.JOIN_THREAD,
+ id: uuid.v4(),
+ time,
+ threadInfo: newThread,
+ rawMessageInfos: [],
+ truncationStatus: messageTruncationStatus.EXHAUSTIVE,
+ rawEntryInfos: [],
+ },
+ {
+ type: updateTypes.UPDATE_THREAD_READ_STATUS,
+ id: uuid.v4(),
+ time,
+ threadID: existingThreadDetails.threadID,
+ unread: true,
+ },
+ );
+ } else {
+ const currentThreadInfoOptional = utilities.getThreadInfo(
+ existingThreadDetails.threadID,
+ );
+ if (!currentThreadInfoOptional || !currentThreadInfoOptional.thick) {
+ // We can't perform this operation now. It should be queued for later.
+ return {
+ rawMessageInfos: [],
+ updateInfos: [],
+ };
+ }
+ const currentThreadInfo: ThickRawThreadInfo = currentThreadInfoOptional;
+ const defaultRoleID = values(currentThreadInfo.roles).find(role =>
+ roleIsDefaultRole(role),
+ )?.id;
+ invariant(defaultRoleID, 'Default role ID must exist');
+ const { membershipPermissions } = createRoleAndPermissionForThickThreads(
+ currentThreadInfo.type,
+ currentThreadInfo.id,
+ defaultRoleID,
+ );
+ const newMembers = addedUserIDs
+ .filter(userID => !userIsMember(currentThreadInfo, userID))
+ .map(userID =>
+ minimallyEncodeMemberInfo<ThickMemberInfo>({
+ id: userID,
+ role: defaultRoleID,
+ permissions: membershipPermissions,
+ isSender: editorID === viewerID,
+ subscription: joinThreadSubscription,
+ }),
+ );
+
+ const newThreadInfo = {
+ ...currentThreadInfo,
+ members: [...currentThreadInfo.members, ...newMembers],
+ };
+ updateInfos.push({
+ type: updateTypes.UPDATE_THREAD,
+ id: uuid.v4(),
+ time,
+ threadInfo: newThreadInfo,
+ });
+ }
+ return {
+ rawMessageInfos: [addMembersMessage],
+ updateInfos,
+ };
+ },
+});
+
+export { addMembersSpec };
diff --git a/lib/shared/dm-ops/create-thread-spec.js b/lib/shared/dm-ops/create-thread-spec.js
--- a/lib/shared/dm-ops/create-thread-spec.js
+++ b/lib/shared/dm-ops/create-thread-spec.js
@@ -26,10 +26,37 @@
minimallyEncodeThreadCurrentUserInfo,
} from '../../types/minimally-encoded-thread-permissions-types.js';
import { joinThreadSubscription } from '../../types/subscription-types.js';
+import type { ThreadPermissionsInfo } from '../../types/thread-permission-types.js';
+import type { ThickThreadType } from '../../types/thread-types-enum.js';
import type { ThickMemberInfo } from '../../types/thread-types.js';
import { updateTypes } from '../../types/update-types-enum.js';
import { generatePendingThreadColor } from '../color-utils.js';
+function createRoleAndPermissionForThickThreads(
+ threadType: ThickThreadType,
+ threadID: string,
+ roleID: string,
+): { +role: RoleInfo, +membershipPermissions: ThreadPermissionsInfo } {
+ const rolePermissions = getThickThreadRolePermissionsBlob(threadType);
+ const membershipPermissions = getAllThreadPermissions(
+ makePermissionsBlob(rolePermissions, null, threadID, threadType),
+ threadID,
+ );
+ const role: RoleInfo = {
+ ...minimallyEncodeRoleInfo({
+ id: roleID,
+ name: 'Members',
+ permissions: rolePermissions,
+ isDefault: true,
+ }),
+ specialRole: specialRoles.DEFAULT_ROLE,
+ };
+ return {
+ membershipPermissions,
+ role,
+ };
+}
+
type MutableThickRawThreadInfo = { ...ThickRawThreadInfo };
function createThickRawThreadInfo(
input: CreateThickRawThreadInfoInput,
@@ -55,20 +82,8 @@
const threadColor = color ?? generatePendingThreadColor(allMemberIDs);
- const rolePermissions = getThickThreadRolePermissionsBlob(threadType);
- const membershipPermissions = getAllThreadPermissions(
- makePermissionsBlob(rolePermissions, null, threadID, threadType),
- threadID,
- );
- const role: RoleInfo = {
- ...minimallyEncodeRoleInfo({
- id: roleID,
- name: 'Members',
- permissions: rolePermissions,
- isDefault: true,
- }),
- specialRole: specialRoles.DEFAULT_ROLE,
- };
+ const { membershipPermissions, role } =
+ createRoleAndPermissionForThickThreads(threadType, threadID, roleID);
const newThread: MutableThickRawThreadInfo = {
thick: true,
@@ -172,4 +187,8 @@
},
});
-export { createThickRawThreadInfo, createThreadSpec };
+export {
+ createThickRawThreadInfo,
+ createThreadSpec,
+ createRoleAndPermissionForThickThreads,
+};
diff --git a/lib/shared/dm-ops/dm-op-spec.js b/lib/shared/dm-ops/dm-op-spec.js
--- a/lib/shared/dm-ops/dm-op-spec.js
+++ b/lib/shared/dm-ops/dm-op-spec.js
@@ -2,10 +2,12 @@
import type { DMOperation, DMOperationResult } from '../../types/dm-ops.js';
import type { RawMessageInfo } from '../../types/message-types.js';
+import type { RawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
export type ProcessDMOperationUtilities = {
// Needed to fetch sidebar source messages
+fetchMessage: (messageID: string) => Promise<?RawMessageInfo>,
+ +getThreadInfo: (threadID: string) => ?RawThreadInfo,
};
export type DMOperationSpec<DMOp: DMOperation> = {
diff --git a/lib/shared/dm-ops/dm-op-specs.js b/lib/shared/dm-ops/dm-op-specs.js
--- a/lib/shared/dm-ops/dm-op-specs.js
+++ b/lib/shared/dm-ops/dm-op-specs.js
@@ -1,5 +1,6 @@
// @flow
+import { addMembersSpec } from './add-members-spec.js';
import { createSidebarSpec } from './create-sidebar-spec.js';
import { createThreadSpec } from './create-thread-spec.js';
import type { DMOperationSpec } from './dm-op-spec.js';
@@ -16,4 +17,5 @@
[dmOperationTypes.SEND_TEXT_MESSAGE]: sendTextMessageSpec,
[dmOperationTypes.SEND_REACTION_MESSAGE]: sendReactionMessageSpec,
[dmOperationTypes.SEND_EDIT_MESSAGE]: sendEditMessageSpec,
+ [dmOperationTypes.ADD_MEMBERS]: addMembersSpec,
});
diff --git a/lib/shared/dm-ops/process-dm-ops.js b/lib/shared/dm-ops/process-dm-ops.js
--- a/lib/shared/dm-ops/process-dm-ops.js
+++ b/lib/shared/dm-ops/process-dm-ops.js
@@ -9,15 +9,23 @@
type DMOperation,
processDMOpsActionType,
} from '../../types/dm-ops.js';
-import { useDispatch } from '../../utils/redux-utils.js';
+import { useDispatch, useSelector } from '../../utils/redux-utils.js';
function useProcessDMOperation(): (dmOp: DMOperation) => Promise<void> {
const fetchMessage = useGetLatestMessageEdit();
+ const threadInfos = useSelector(state => state.threadStore.threadInfos);
+
+ const getThreadInfo = React.useCallback(
+ (id: string) => threadInfos[id],
+ [threadInfos],
+ );
+
const utilities = React.useMemo(
() => ({
fetchMessage,
+ getThreadInfo,
}),
- [fetchMessage],
+ [fetchMessage, getThreadInfo],
);
const dispatch = useDispatch();
diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js
--- a/lib/types/dm-ops.js
+++ b/lib/types/dm-ops.js
@@ -1,7 +1,6 @@
// @flow
-import type { TInterface, TUnion } from 'tcomb';
-import t from 'tcomb';
+import t, { type TInterface, type TUnion } from 'tcomb';
import type { ClientAvatar } from './avatar-types.js';
import { clientAvatarValidator } from './avatar-types.js';
@@ -23,6 +22,7 @@
SEND_TEXT_MESSAGE: 'send_text_message',
SEND_REACTION_MESSAGE: 'send_reaction_message',
SEND_EDIT_MESSAGE: 'send_edit_message',
+ ADD_MEMBERS: 'add_members',
});
export type DMOperationType = $Values<typeof dmOperationTypes>;
@@ -170,18 +170,38 @@
text: t.String,
});
+export type DMAddMembersOperation = {
+ +type: 'add_members',
+ +editorID: string,
+ +time: number,
+ +messageID: string,
+ +addedUserIDs: $ReadOnlyArray<string>,
+ +existingThreadDetails: CreateThickRawThreadInfoInput,
+};
+export const dmAddMembersOperationValidator: TInterface<DMAddMembersOperation> =
+ tShape<DMAddMembersOperation>({
+ type: tString(dmOperationTypes.ADD_MEMBERS),
+ editorID: tUserID,
+ time: t.Number,
+ messageID: t.String,
+ addedUserIDs: t.list(tUserID),
+ existingThreadDetails: createThickRawThreadInfoInputValidator,
+ });
+
export type DMOperation =
| DMCreateThreadOperation
| DMCreateSidebarOperation
| DMSendTextMessageOperation
| DMSendReactionMessageOperation
- | DMSendEditMessageOperation;
+ | DMSendEditMessageOperation
+ | DMAddMembersOperation;
export const dmOperationValidator: TUnion<DMOperation> = t.union([
dmCreateThreadOperationValidator,
dmCreateSidebarOperationValidator,
dmSendTextMessageOperationValidator,
dmSendReactionMessageOperationValidator,
dmSendEditMessageOperationValidator,
+ dmAddMembersOperationValidator,
]);
export type DMOperationResult = {

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 9, 4:23 AM (19 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2448595
Default Alt Text
D12835.id42877.diff (10 KB)

Event Timeline