diff --git a/lib/actions/thread-actions.js b/lib/actions/thread-actions.js --- a/lib/actions/thread-actions.js +++ b/lib/actions/thread-actions.js @@ -1,12 +1,28 @@ // @flow import invariant from 'invariant'; +import * as React from 'react'; +import uuid from 'uuid'; import genesis from '../facts/genesis.js'; import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js'; import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js'; import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js'; +import { + type OutboundDMOperationSpecification, + dmOperationSpecificationTypes, +} from '../shared/dm-ops/dm-op-utils.js'; +import { useProcessAndSendDMOperation } from '../shared/dm-ops/process-dm-ops.js'; import { permissionsAndAuthRelatedRequestTimeout } from '../shared/timeouts.js'; +import type { + DMChangeThreadSettingsOperation, + DMThreadSettingsChangesBase, +} from '../types/dm-ops.js'; +import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js'; +import { + thickThreadTypes, + threadTypeIsThick, +} from '../types/thread-types-enum.js'; import type { ChangeThreadSettingsPayload, LeaveThreadPayload, @@ -23,6 +39,7 @@ RoleDeletionPayload, } from '../types/thread-types.js'; import { values } from '../utils/objects.js'; +import { useSelector } from '../utils/redux-utils.js'; export type DeleteThreadInput = { +threadID: string, @@ -97,10 +114,77 @@ }; }; -function useChangeThreadSettings(): ( - input: UpdateThreadRequest, -) => Promise { - return useKeyserverCall(changeThreadSettings); +function useChangeThreadSettings( + threadInfo: ?ThreadInfo, +): (input: UpdateThreadRequest) => Promise { + const processAndSendDMOperation = useProcessAndSendDMOperation(); + const viewerID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + + const keyserverCall = useKeyserverCall(changeThreadSettings); + + return React.useCallback( + async (input: UpdateThreadRequest) => { + if (!threadInfo || !threadTypeIsThick(threadInfo.type)) { + return await keyserverCall(input); + } + + invariant(viewerID, 'viewerID should be set'); + + const changes: { ...DMThreadSettingsChangesBase } = {}; + if (input.changes.name) { + changes.name = input.changes.name; + } + if (input.changes.description) { + changes.description = input.changes.description; + } + if (input.changes.color) { + changes.color = input.changes.color; + } + if (input.changes.avatar && input.changes.avatar.type === 'emoji') { + changes.avatar = { + type: 'emoji', + emoji: input.changes.avatar.emoji, + color: input.changes.avatar.color, + }; + } else if (input.changes.avatar && input.changes.avatar.type === 'ens') { + changes.avatar = { type: 'ens' }; + } + // To support `image` and `encrypted_image` avatars we first, need stop + // sending multimedia metadata to keyserver. + // ENG-8708 + + const op: DMChangeThreadSettingsOperation = { + type: 'change_thread_settings', + threadID: threadInfo.id, + editorID: viewerID, + time: Date.now(), + changes, + messageIDsPrefix: uuid.v4(), + }; + const opSpecification: OutboundDMOperationSpecification = { + type: dmOperationSpecificationTypes.OUTBOUND, + op, + recipients: { + type: 'all_thread_members', + threadID: + threadInfo.type === thickThreadTypes.THICK_SIDEBAR && + threadInfo.parentThreadID + ? threadInfo.parentThreadID + : threadInfo.id, + }, + }; + + await processAndSendDMOperation(opSpecification); + return ({ + threadID: threadInfo.id, + updatesResult: { newUpdates: [] }, + newMessageInfos: [], + }: ChangeThreadSettingsPayload); + }, + [keyserverCall, processAndSendDMOperation, threadInfo, viewerID], + ); } export type RemoveUsersFromThreadInput = { diff --git a/lib/components/base-edit-thread-avatar-provider.react.js b/lib/components/base-edit-thread-avatar-provider.react.js --- a/lib/components/base-edit-thread-avatar-provider.react.js +++ b/lib/components/base-edit-thread-avatar-provider.react.js @@ -7,6 +7,7 @@ changeThreadSettingsActionTypes, } from '../actions/thread-actions.js'; import { createLoadingStatusSelector } from '../selectors/loading-selectors.js'; +import { threadInfoSelector } from '../selectors/thread-selectors.js'; import type { UpdateUserAvatarRequest } from '../types/avatar-types.js'; import type { LoadingStatus } from '../types/loading-types.js'; import type { UpdateThreadRequest } from '../types/thread-types.js'; @@ -39,8 +40,11 @@ ), ); + const threadInfo = useSelector( + state => threadInfoSelector(state)[activeThreadID], + ); const dispatchActionPromise = useDispatchActionPromise(); - const changeThreadSettingsCall = useChangeThreadSettings(); + const changeThreadSettingsCall = useChangeThreadSettings(threadInfo); const [ threadAvatarMediaUploadInProgress, diff --git a/native/chat/settings/color-selector-modal.react.js b/native/chat/settings/color-selector-modal.react.js --- a/native/chat/settings/color-selector-modal.react.js +++ b/native/chat/settings/color-selector-modal.react.js @@ -176,7 +176,9 @@ const windowWidth = useSelector(state => state.dimensions.width); const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings(); + const callChangeThreadSettings = useChangeThreadSettings( + props.route.params.threadInfo, + ); return ( { try {