Page MenuHomePhorge

D12881.1765349908.diff
No OneTemporary

Size
18 KB
Referenced Files
None
Subscribers
None

D12881.1765349908.diff

diff --git a/keyserver/src/responders/thread-responders.js b/keyserver/src/responders/thread-responders.js
--- a/keyserver/src/responders/thread-responders.js
+++ b/keyserver/src/responders/thread-responders.js
@@ -27,8 +27,7 @@
type RoleDeletionRequest,
type RoleDeletionResult,
} from 'lib/types/thread-types.js';
-import { updateUserAvatarRequestValidator } from 'lib/utils/avatar-utils.js';
-import { values } from 'lib/utils/objects.js';
+import { threadSettingsChangesValidator } from 'lib/types/validators/thread-validators.js';
import {
tShape,
tNumEnum,
@@ -118,15 +117,7 @@
export const updateThreadRequestInputValidator: TInterface<UpdateThreadRequest> =
tShape<UpdateThreadRequest>({
threadID: tID,
- changes: tShape({
- type: t.maybe(tNumEnum(values(threadTypes))),
- name: t.maybe(t.String),
- description: t.maybe(t.String),
- color: t.maybe(tColor),
- parentThreadID: t.maybe(tID),
- newMemberIDs: t.maybe(t.list(tUserID)),
- avatar: t.maybe(updateUserAvatarRequestValidator),
- }),
+ changes: threadSettingsChangesValidator,
accountPassword: t.maybe(tPassword),
});
diff --git a/lib/shared/dm-ops/add-members-spec.js b/lib/shared/dm-ops/add-members-spec.js
--- a/lib/shared/dm-ops/add-members-spec.js
+++ b/lib/shared/dm-ops/add-members-spec.js
@@ -24,113 +24,132 @@
import { values } from '../../utils/objects.js';
import { roleIsDefaultRole, userIsMember } from '../thread-utils.js';
+function createAddNewMembersResults(
+ dmOperation: DMAddMembersOperation,
+ viewerID: string,
+ utilities: ProcessDMOperationUtilities,
+): {
+ +rawMessageInfos: Array<RawMessageInfo>,
+ updateInfos: Array<ClientUpdateInfo>,
+ threadInfo: ?ThickRawThreadInfo,
+} {
+ 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> = [];
+ const rawMessageInfos: Array<RawMessageInfo> = [];
+ let resultThreadInfo: ?ThickRawThreadInfo;
+ if (viewerIsAdded) {
+ const newThread = createThickRawThreadInfo(
+ {
+ ...existingThreadDetails,
+ allMemberIDs: [...existingThreadDetails.allMemberIDs, ...addedUserIDs],
+ },
+ viewerID,
+ );
+ resultThreadInfo = newThread;
+ updateInfos.push(
+ {
+ type: updateTypes.JOIN_THREAD,
+ id: uuid.v4(),
+ time,
+ threadInfo: newThread,
+ rawMessageInfos: [addMembersMessage],
+ truncationStatus: messageTruncationStatus.EXHAUSTIVE,
+ rawEntryInfos: [],
+ },
+ {
+ type: updateTypes.UPDATE_THREAD_READ_STATUS,
+ id: uuid.v4(),
+ time,
+ threadID: existingThreadDetails.threadID,
+ unread: true,
+ },
+ );
+ } else {
+ const currentThreadInfoOptional =
+ utilities.threadInfos[existingThreadDetails.threadID];
+ if (!currentThreadInfoOptional || !currentThreadInfoOptional.thick) {
+ // We can't perform this operation now. It should be queued for later.
+ return {
+ rawMessageInfos: [],
+ updateInfos: [],
+ threadInfo: null,
+ };
+ }
+ 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],
+ };
+ resultThreadInfo = newThreadInfo;
+ updateInfos.push(
+ {
+ type: updateTypes.UPDATE_THREAD,
+ id: uuid.v4(),
+ time,
+ threadInfo: newThreadInfo,
+ },
+ {
+ type: updateTypes.UPDATE_THREAD_READ_STATUS,
+ id: uuid.v4(),
+ time,
+ threadID: existingThreadDetails.threadID,
+ unread: true,
+ },
+ );
+ rawMessageInfos.push(addMembersMessage);
+ }
+ return {
+ rawMessageInfos,
+ updateInfos,
+ threadInfo: resultThreadInfo,
+ };
+}
+
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> = [];
- const rawMessageInfos: Array<RawMessageInfo> = [];
- if (viewerIsAdded) {
- const newThread = createThickRawThreadInfo(
- {
- ...existingThreadDetails,
- allMemberIDs: [
- ...existingThreadDetails.allMemberIDs,
- ...addedUserIDs,
- ],
- },
- viewerID,
- );
- updateInfos.push(
- {
- type: updateTypes.JOIN_THREAD,
- id: uuid.v4(),
- time,
- threadInfo: newThread,
- rawMessageInfos: [addMembersMessage],
- truncationStatus: messageTruncationStatus.EXHAUSTIVE,
- rawEntryInfos: [],
- },
- {
- type: updateTypes.UPDATE_THREAD_READ_STATUS,
- id: uuid.v4(),
- time,
- threadID: existingThreadDetails.threadID,
- unread: true,
- },
- );
- } else {
- const currentThreadInfoOptional =
- utilities.threadInfos[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,
- },
- {
- type: updateTypes.UPDATE_THREAD_READ_STATUS,
- id: uuid.v4(),
- time,
- threadID: existingThreadDetails.threadID,
- unread: true,
- },
- );
- rawMessageInfos.push(addMembersMessage);
- }
- return {
- rawMessageInfos,
- updateInfos,
- };
+ const { rawMessageInfos, updateInfos } = createAddNewMembersResults(
+ dmOperation,
+ viewerID,
+ utilities,
+ );
+ return { rawMessageInfos, updateInfos };
},
});
-export { addMembersSpec };
+export { addMembersSpec, createAddNewMembersResults };
diff --git a/lib/shared/dm-ops/change-thread-settings-spec.js b/lib/shared/dm-ops/change-thread-settings-spec.js
new file mode 100644
--- /dev/null
+++ b/lib/shared/dm-ops/change-thread-settings-spec.js
@@ -0,0 +1,166 @@
+// @flow
+
+import uuid from 'uuid';
+
+import { createAddNewMembersResults } from './add-members-spec.js';
+import type {
+ DMOperationSpec,
+ ProcessDMOperationUtilities,
+} from './dm-op-spec.js';
+import type { DMChangeThreadSettingsOperation } from '../../types/dm-ops.js';
+import { messageTypes } from '../../types/message-types-enum.js';
+import type { RawMessageInfo } from '../../types/message-types.js';
+import type { RawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
+import type { LegacyRawThreadInfo } 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';
+
+const changeThreadSettingsSpec: DMOperationSpec<DMChangeThreadSettingsOperation> =
+ Object.freeze({
+ processDMOperation: async (
+ dmOperation: DMChangeThreadSettingsOperation,
+ viewerID: string,
+ utilities: ProcessDMOperationUtilities,
+ ) => {
+ const {
+ editorID,
+ time,
+ changes,
+ messageIDsPrefix,
+ existingThreadDetails,
+ } = dmOperation;
+ const {
+ name,
+ description,
+ color,
+ parentThreadID,
+ avatar,
+ type: threadType,
+ } = changes;
+ const threadID = existingThreadDetails.threadID;
+
+ const newMemberIDs =
+ changes.newMemberIDs && changes.newMemberIDs.length > 0
+ ? [...new Set(changes.newMemberIDs)]
+ : null;
+
+ let threadInfoToUpdate: ?(RawThreadInfo | LegacyRawThreadInfo) =
+ utilities.threadInfos[threadID];
+ if (!threadInfoToUpdate && !newMemberIDs?.includes(viewerID)) {
+ // We can't perform this operation now. It should be queued for later.
+ return {
+ rawMessageInfos: [],
+ updateInfos: [],
+ };
+ }
+
+ const updateInfos: Array<ClientUpdateInfo> = [];
+ const rawMessageInfos: Array<RawMessageInfo> = [];
+
+ if (newMemberIDs) {
+ const addMembersResult = createAddNewMembersResults(
+ {
+ type: 'add_members',
+ editorID,
+ time,
+ messageID: `${messageIDsPrefix}/add_members`,
+ addedUserIDs: newMemberIDs,
+ existingThreadDetails,
+ },
+ viewerID,
+ utilities,
+ );
+ if (addMembersResult.threadInfo) {
+ threadInfoToUpdate = addMembersResult.threadInfo;
+ }
+ updateInfos.push(...addMembersResult.updateInfos);
+ rawMessageInfos.push(...addMembersResult.rawMessageInfos);
+ }
+
+ if (!threadInfoToUpdate || !threadInfoToUpdate.thick) {
+ // We can't perform this operation now. It should be queued for later.
+ return {
+ rawMessageInfos: [],
+ updateInfos: [],
+ };
+ }
+
+ const changedFields: { [string]: string | number } = {};
+
+ if (name !== undefined && name !== null) {
+ changedFields.name = name;
+ threadInfoToUpdate = {
+ ...threadInfoToUpdate,
+ name,
+ };
+ }
+
+ if (description !== undefined && description !== null) {
+ changedFields.description = description;
+ threadInfoToUpdate = {
+ ...threadInfoToUpdate,
+ description,
+ };
+ }
+
+ if (color) {
+ changedFields.color = color;
+ threadInfoToUpdate = {
+ ...threadInfoToUpdate,
+ color,
+ };
+ }
+
+ if (parentThreadID !== undefined) {
+ // TODO do we want to support this for thick threads?
+ return {
+ rawMessageInfos: [],
+ updateInfos: [],
+ };
+ }
+
+ if (avatar) {
+ changedFields.avatar =
+ avatar.type !== 'remove' ? JSON.stringify(avatar) : '';
+ // TODO how to create an avatar?
+ }
+
+ if (threadType !== null && threadType !== undefined) {
+ // TODO do we want to support this for thick threads?
+ return {
+ rawMessageInfos: [],
+ updateInfos: [],
+ };
+ }
+
+ for (const fieldName in changedFields) {
+ const newValue = changedFields[fieldName];
+ rawMessageInfos.push({
+ type: messageTypes.CHANGE_SETTINGS,
+ threadID,
+ creatorID: editorID,
+ time,
+ field: fieldName,
+ value: newValue,
+ id: `${messageIDsPrefix}/${fieldName}`,
+ });
+ }
+
+ if (values(changedFields).length > 0) {
+ updateInfos.push({
+ type: updateTypes.UPDATE_THREAD,
+ id: uuid.v4(),
+ time,
+ threadInfo: threadInfoToUpdate,
+ });
+ }
+
+ return {
+ rawMessageInfos,
+ updateInfos,
+ };
+ },
+ });
+
+export { changeThreadSettingsSpec };
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,6 +1,7 @@
// @flow
import { addMembersSpec } from './add-members-spec.js';
+import { changeThreadSettingsSpec } from './change-thread-settings-spec.js';
import { createSidebarSpec } from './create-sidebar-spec.js';
import { createThreadSpec } from './create-thread-spec.js';
import type { DMOperationSpec } from './dm-op-spec.js';
@@ -24,4 +25,5 @@
[dmOperationTypes.JOIN_THREAD]: joinThreadSpec,
[dmOperationTypes.LEAVE_THREAD]: leaveThreadSpec,
[dmOperationTypes.REMOVE_MEMBERS]: removeMembersSpec,
+ [dmOperationTypes.CHANGE_THREAD_SETTINGS]: changeThreadSettingsSpec,
});
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
@@ -12,7 +12,9 @@
type ThickThreadType,
thickThreadTypeValidator,
} from './thread-types-enum.js';
+import type { ThreadChanges } from './thread-types.js';
import type { ClientUpdateInfo } from './update-types.js';
+import { threadSettingsChangesValidator } from './validators/thread-validators.js';
import { values } from '../utils/objects.js';
import { tShape, tString, tUserID } from '../utils/validation-utils.js';
@@ -26,6 +28,7 @@
JOIN_THREAD: 'join_thread',
LEAVE_THREAD: 'leave_thread',
REMOVE_MEMBERS: 'remove_members',
+ CHANGE_THREAD_SETTINGS: 'change_thread_settings',
});
export type DMOperationType = $Values<typeof dmOperationTypes>;
@@ -241,6 +244,24 @@
removedUserIDs: t.list(tUserID),
});
+export type DMChangeThreadSettingsOperation = {
+ +type: 'change_thread_settings',
+ +editorID: string,
+ +time: number,
+ +changes: ThreadChanges,
+ +messageIDsPrefix: string,
+ +existingThreadDetails: CreateThickRawThreadInfoInput,
+};
+export const dmChangeThreadSettingsOperationValidator: TInterface<DMChangeThreadSettingsOperation> =
+ tShape<DMChangeThreadSettingsOperation>({
+ type: tString(dmOperationTypes.CHANGE_THREAD_SETTINGS),
+ editorID: tUserID,
+ time: t.Number,
+ changes: threadSettingsChangesValidator,
+ messageIDsPrefix: t.String,
+ existingThreadDetails: createThickRawThreadInfoInputValidator,
+ });
+
export type DMOperation =
| DMCreateThreadOperation
| DMCreateSidebarOperation
@@ -250,7 +271,8 @@
| DMAddMembersOperation
| DMJoinThreadOperation
| DMLeaveThreadOperation
- | DMRemoveMembersOperation;
+ | DMRemoveMembersOperation
+ | DMChangeThreadSettingsOperation;
export const dmOperationValidator: TUnion<DMOperation> = t.union([
dmCreateThreadOperationValidator,
dmCreateSidebarOperationValidator,
@@ -261,6 +283,7 @@
dmJoinThreadOperationValidator,
dmLeaveThreadOperationValidator,
dmRemoveMembersOperationValidator,
+ dmChangeThreadSettingsOperationValidator,
]);
export type DMOperationResult = {
diff --git a/lib/types/validators/thread-validators.js b/lib/types/validators/thread-validators.js
--- a/lib/types/validators/thread-validators.js
+++ b/lib/types/validators/thread-validators.js
@@ -4,12 +4,21 @@
import type { TInterface } from 'tcomb';
import { mixedRawThreadInfoValidator } from '../../permissions/minimally-encoded-raw-thread-info-validators.js';
-import { tShape, tID } from '../../utils/validation-utils.js';
+import { updateUserAvatarRequestValidator } from '../../utils/avatar-utils.js';
+import { values } from '../../utils/objects.js';
+import {
+ tShape,
+ tID,
+ tNumEnum,
+ tColor,
+ tUserID,
+} from '../../utils/validation-utils.js';
import { mediaValidator } from '../media-types.js';
import {
rawMessageInfoValidator,
messageTruncationStatusesValidator,
} from '../message-types.js';
+import { threadTypes } from '../thread-types-enum.js';
import {
type ChangeThreadSettingsResult,
type LeaveThreadResult,
@@ -19,6 +28,7 @@
type ToggleMessagePinResult,
type RoleModificationResult,
type RoleDeletionResult,
+ type ThreadChanges,
} from '../thread-types.js';
import { serverUpdateInfoValidator } from '../update-types.js';
import { userInfosValidator } from '../user-types.js';
@@ -30,6 +40,17 @@
}),
});
+export const threadSettingsChangesValidator: TInterface<ThreadChanges> =
+ tShape<ThreadChanges>({
+ type: t.maybe(tNumEnum(values(threadTypes))),
+ name: t.maybe(t.String),
+ description: t.maybe(t.String),
+ color: t.maybe(tColor),
+ parentThreadID: t.maybe(tID),
+ newMemberIDs: t.maybe(t.list(tUserID)),
+ avatar: t.maybe(updateUserAvatarRequestValidator),
+ });
+
export const changeThreadSettingsResultValidator: TInterface<ChangeThreadSettingsResult> =
tShape<ChangeThreadSettingsResult>({
newMessageInfos: t.list(rawMessageInfoValidator),

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 10, 6:58 AM (12 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5861849
Default Alt Text
D12881.1765349908.diff (18 KB)

Event Timeline