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 @@ -24,10 +24,7 @@ ThreadInfo, RawThreadInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; -import { - thickThreadTypes, - threadTypeIsThick, -} from '../types/thread-types-enum.js'; +import { thickThreadTypes } from '../types/thread-types-enum.js'; import type { ChangeThreadSettingsPayload, LeaveThreadPayload, @@ -42,6 +39,7 @@ RoleModificationPayload, RoleDeletionRequest, RoleDeletionPayload, + UpdaetThickThreadRequest, } from '../types/thread-types.js'; import { values } from '../utils/objects.js'; import { useSelector } from '../utils/redux-utils.js'; @@ -119,9 +117,21 @@ }; }; -function useChangeThreadSettings( - threadInfo: ?ThreadInfo, -): (input: UpdateThreadRequest) => Promise { +export type UseChangeThreadSettingsInput = $ReadOnly< + | { + ...UpdateThreadRequest, + +thick: false, + } + | { + ...UpdaetThickThreadRequest, + +thick: true, + +threadInfo: ThreadInfo, + }, +>; + +function useChangeThreadSettings(): ( + input: UseChangeThreadSettingsInput, +) => Promise { const processAndSendDMOperation = useProcessAndSendDMOperation(); const viewerID = useSelector( state => state.currentUserInfo && state.currentUserInfo.id, @@ -130,9 +140,10 @@ const keyserverCall = useKeyserverCall(changeThreadSettings); return React.useCallback( - async (input: UpdateThreadRequest) => { - if (!threadInfo || !threadTypeIsThick(threadInfo.type)) { - return await keyserverCall(input); + async (input: UseChangeThreadSettingsInput) => { + if (!input.thick) { + const { thick, ...rest } = input; + return await keyserverCall({ ...rest }); } invariant(viewerID, 'viewerID should be set'); @@ -159,7 +170,7 @@ // To support `image` and `encrypted_image` avatars we first, need stop // sending multimedia metadata to keyserver. // ENG-8708 - + const { threadInfo } = input; const op: DMChangeThreadSettingsOperation = { type: 'change_thread_settings', threadID: threadInfo.id, @@ -188,7 +199,7 @@ newMessageInfos: [], }: ChangeThreadSettingsPayload); }, - [keyserverCall, processAndSendDMOperation, threadInfo, viewerID], + [keyserverCall, processAndSendDMOperation, viewerID], ); } 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 @@ -10,7 +10,7 @@ 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'; +import { threadTypeIsThick } from '../types/thread-types-enum.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector } from '../utils/redux-utils.js'; @@ -44,7 +44,7 @@ state => threadInfoSelector(state)[activeThreadID], ); const dispatchActionPromise = useDispatchActionPromise(); - const changeThreadSettingsCall = useChangeThreadSettings(threadInfo); + const changeThreadSettingsCall = useChangeThreadSettings(); const [ threadAvatarMediaUploadInProgress, @@ -73,7 +73,7 @@ // Use platform-specific `[web/native]SetThreadAvatar` instead. const baseSetThreadAvatar = React.useCallback( async (threadID: string, avatarRequest: UpdateUserAvatarRequest) => { - const updateThreadRequest: UpdateThreadRequest = { + const updateThreadRequest = { threadID, changes: { avatar: avatarRequest, @@ -86,13 +86,18 @@ ) { updateThreadAvatarMediaUploadInProgress(false); } - const promise = changeThreadSettingsCall(updateThreadRequest); + const updateThreadInput = threadTypeIsThick(threadInfo.type) + ? { thick: true, threadInfo, ...updateThreadRequest } + : { thick: false, ...updateThreadRequest }; + + const promise = changeThreadSettingsCall(updateThreadInput); void dispatchActionPromise(changeThreadSettingsActionTypes, promise, { customKeyName: `${action}:${threadID}:avatar`, }); await promise; }, [ + threadInfo, changeThreadSettingsCall, dispatchActionPromise, updateThreadAvatarMediaUploadInProgress, diff --git a/lib/hooks/promote-sidebar.react.js b/lib/hooks/promote-sidebar.react.js --- a/lib/hooks/promote-sidebar.react.js +++ b/lib/hooks/promote-sidebar.react.js @@ -69,6 +69,7 @@ changeThreadSettingsActionTypes, (async () => { return await callChangeThreadSettings({ + thick: false, threadID: threadInfo.id, changes: { type: threadTypes.COMMUNITY_OPEN_SUBTHREAD }, }); diff --git a/lib/shared/messages/text-message-spec.js b/lib/shared/messages/text-message-spec.js --- a/lib/shared/messages/text-message-spec.js +++ b/lib/shared/messages/text-message-spec.js @@ -27,10 +27,14 @@ } from '../../types/messages/text.js'; import type { ThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js'; import type { NotifTexts } from '../../types/notif-types.js'; -import { threadTypeIsSidebar } from '../../types/thread-types-enum.js'; +import { + threadTypeIsSidebar, + threadTypeIsThick, +} from '../../types/thread-types-enum.js'; import type { RelativeUserInfo } from '../../types/user-types.js'; import { ET } from '../../utils/entity-text.js'; import { useDispatchActionPromise } from '../../utils/redux-promise-utils.js'; +import { useAddDMThreadMembers } from '../dm-ops/dm-op-utils.js'; import { type ASTNode, type SingleASTNode, @@ -292,6 +296,7 @@ useCreationSideEffectsFunc: () => { const dispatchActionPromise = useDispatchActionPromise(); const callChangeThreadSettings = useChangeThreadSettings(); + const callAddDMThreadMembers = useAddDMThreadMembers(); return async ( messageInfo: RawTextMessageInfo, threadInfo: ThreadInfo, @@ -313,10 +318,24 @@ } const newMemberIDs = mentionedNewMembers.map(({ id }) => id); - const addMembersPromise = callChangeThreadSettings({ + if (threadTypeIsThick(threadInfo.type)) { + await callAddDMThreadMembers(newMemberIDs, threadInfo); + return; + } + + const changeThreadSettingsRequest = { threadID: threadInfo.id, changes: { newMemberIDs }, - }); + }; + + const changeThreadSettingsInput = { + thick: false, + ...changeThreadSettingsRequest, + }; + + const addMembersPromise = callChangeThreadSettings( + changeThreadSettingsInput, + ); void dispatchActionPromise( changeThreadSettingsActionTypes, diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -312,7 +312,7 @@ }, }; -export type ThreadChanges = Partial<{ +type BaseThreadChanges = { +type: ThinThreadType, +name: string, +description: string, @@ -320,7 +320,13 @@ +parentThreadID: ?string, +newMemberIDs: $ReadOnlyArray, +avatar: UpdateUserAvatarRequest, -}>; +}; + +export type ThreadChanges = Partial; + +export type ThickThreadChanges = $ReadOnly< + $Partial<$Rest }>>, +>; export type UpdateThreadRequest = { +threadID: string, @@ -328,6 +334,11 @@ +accountPassword?: empty, }; +export type UpdaetThickThreadRequest = $ReadOnly<{ + ...UpdateThreadRequest, + +changes: ThickThreadChanges, +}>; + export type BaseNewThreadRequest = { +id?: ?string, +name?: ?string, diff --git a/native/chat/settings/add-users-modal.react.js b/native/chat/settings/add-users-modal.react.js --- a/native/chat/settings/add-users-modal.react.js +++ b/native/chat/settings/add-users-modal.react.js @@ -79,6 +79,7 @@ const addUsersToThread = React.useCallback(async () => { try { const result = await callChangeThreadSettings({ + thick: false, threadID: threadID, changes: { newMemberIDs: userInfoInputIDs }, }); 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 @@ -7,12 +7,11 @@ import { changeThreadSettingsActionTypes, useChangeThreadSettings, + type UseChangeThreadSettingsInput, } from 'lib/actions/thread-actions.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { - type ChangeThreadSettingsPayload, - type UpdateThreadRequest, -} from 'lib/types/thread-types.js'; +import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; +import { type ChangeThreadSettingsPayload } from 'lib/types/thread-types.js'; import { type DispatchActionPromise, useDispatchActionPromise, @@ -78,7 +77,7 @@ +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +changeThreadSettings: ( - request: UpdateThreadRequest, + input: UseChangeThreadSettingsInput, ) => Promise, }; function ColorSelectorModal(props: Props): React.Node { @@ -99,10 +98,21 @@ async (newColor: string) => { const threadID = threadInfo.id; try { - return await updateThreadSettings({ + const changeThreadSettingRequest = { threadID, changes: { color: newColor }, - }); + }; + + const changeThreadSettingInput = threadTypeIsThick( + props.route.params.threadInfo.type, + ) + ? { + thick: true, + threadInfo: props.route.params.threadInfo, + ...changeThreadSettingRequest, + } + : { thick: false, ...changeThreadSettingRequest }; + return await updateThreadSettings(changeThreadSettingInput); } catch (e) { Alert.alert( unknownErrorAlertDetails.title, @@ -113,7 +123,12 @@ throw e; } }, - [onErrorAcknowledged, threadInfo.id, updateThreadSettings], + [ + onErrorAcknowledged, + threadInfo.id, + updateThreadSettings, + props.route.params.threadInfo, + ], ); const onColorSelected = React.useCallback( @@ -176,9 +191,7 @@ const windowWidth = useSelector(state => state.dimensions.width); const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings( - props.route.params.threadInfo, - ); + const callChangeThreadSettings = useChangeThreadSettings(); return ( Promise, +canEditThreadDescription: boolean, }; @@ -265,10 +264,22 @@ newDescription: string, ): Promise { try { - return await this.props.changeThreadSettings({ + const changeThreadSettingsRequest = { threadID: this.props.threadInfo.id, changes: { description: newDescription }, - }); + }; + + const changeThreadSettingsInput = threadTypeIsThick( + this.props.threadInfo.type, + ) + ? { + thick: true, + threadInfo: this.props.threadInfo, + ...changeThreadSettingsRequest, + } + : { thick: false, ...changeThreadSettingsRequest }; + + return await this.props.changeThreadSettings(changeThreadSettingsInput); } catch (e) { Alert.alert( unknownErrorAlertDetails.title, @@ -306,7 +317,7 @@ const styles = useStyles(unboundStyles); const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings(props.threadInfo); + const callChangeThreadSettings = useChangeThreadSettings(); const canEditThreadDescription = useThreadHasPermission( props.threadInfo, diff --git a/native/chat/settings/thread-settings-name.react.js b/native/chat/settings/thread-settings-name.react.js --- a/native/chat/settings/thread-settings-name.react.js +++ b/native/chat/settings/thread-settings-name.react.js @@ -12,14 +12,13 @@ import { changeThreadSettingsActionTypes, useChangeThreadSettings, + type UseChangeThreadSettingsInput, } from 'lib/actions/thread-actions.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { ResolvedThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import type { - ChangeThreadSettingsPayload, - UpdateThreadRequest, -} from 'lib/types/thread-types.js'; +import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; +import type { ChangeThreadSettingsPayload } from 'lib/types/thread-types.js'; import { useDispatchActionPromise, type DispatchActionPromise, @@ -77,7 +76,7 @@ +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +changeThreadSettings: ( - update: UpdateThreadRequest, + input: UseChangeThreadSettingsInput, ) => Promise, }; class ThreadSettingsName extends React.PureComponent { @@ -193,10 +192,22 @@ async editName(newName: string): Promise { try { - return await this.props.changeThreadSettings({ + const changeThreadSetingsRequest = { threadID: this.props.threadInfo.id, changes: { name: newName }, - }); + }; + + const changeThreadSettingsInput = threadTypeIsThick( + this.props.threadInfo.type, + ) + ? { + thick: true, + threadInfo: this.props.threadInfo, + ...changeThreadSetingsRequest, + } + : { thick: false, ...changeThreadSetingsRequest }; + + return await this.props.changeThreadSettings(changeThreadSettingsInput); } catch (e) { Alert.alert( unknownErrorAlertDetails.title, @@ -230,7 +241,7 @@ ); const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings(props.threadInfo); + const callChangeThreadSettings = useChangeThreadSettings(); return ( , + +queuedChanges: ThickThreadChanges, + +setQueuedChanges: SetState, }; function ThreadSettingsGeneralTab( props: ThreadSettingsGeneralTabProps, diff --git a/web/modals/threads/settings/thread-settings-modal.react.js b/web/modals/threads/settings/thread-settings-modal.react.js --- a/web/modals/threads/settings/thread-settings-modal.react.js +++ b/web/modals/threads/settings/thread-settings-modal.react.js @@ -20,7 +20,7 @@ import type { RelationshipButton } from 'lib/types/relationship-types.js'; import { threadPermissions } from 'lib/types/thread-permission-types.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; -import { type ThreadChanges } from 'lib/types/thread-types.js'; +import { type ThickThreadChanges } from 'lib/types/thread-types.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import ThreadSettingsDeleteButton from './thread-settings-delete-button.react.js'; @@ -60,9 +60,8 @@ const [errorMessage, setErrorMessage] = React.useState(''); const [currentTabType, setCurrentTabType] = React.useState('general'); - const [queuedChanges, setQueuedChanges] = React.useState( - Object.freeze({}), - ); + const [queuedChanges, setQueuedChanges] = + React.useState(Object.freeze({})); const threadInfoWithNoName = React.useMemo(() => { invariant(threadInfo, 'threadInfo should exist in threadInfoWithNoName'); diff --git a/web/modals/threads/settings/thread-settings-privacy-tab.react.js b/web/modals/threads/settings/thread-settings-privacy-tab.react.js --- a/web/modals/threads/settings/thread-settings-privacy-tab.react.js +++ b/web/modals/threads/settings/thread-settings-privacy-tab.react.js @@ -7,7 +7,7 @@ import { type SetState } from 'lib/types/hook-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; -import { type ThreadChanges } from 'lib/types/thread-types.js'; +import { type ThickThreadChanges } from 'lib/types/thread-types.js'; import css from './thread-settings-privacy-tab.css'; import EnumSettingsOption from '../../../components/enum-settings-option.react.js'; @@ -32,8 +32,8 @@ type ThreadSettingsPrivacyTabProps = { +threadInfo: ThreadInfo, - +queuedChanges: ThreadChanges, - +setQueuedChanges: SetState, + +queuedChanges: ThickThreadChanges, + +setQueuedChanges: SetState, }; function ThreadSettingsPrivacyTab( props: ThreadSettingsPrivacyTabProps, diff --git a/web/modals/threads/settings/thread-settings-save-button.react.js b/web/modals/threads/settings/thread-settings-save-button.react.js --- a/web/modals/threads/settings/thread-settings-save-button.react.js +++ b/web/modals/threads/settings/thread-settings-save-button.react.js @@ -4,7 +4,7 @@ import type { SetState } from 'lib/types/hook-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import type { ThreadChanges } from 'lib/types/thread-types.js'; +import type { ThickThreadChanges } from 'lib/types/thread-types.js'; import { useOnSaveGeneralThreadSettings, @@ -15,8 +15,8 @@ type Props = { +activeTab: 'general' | 'privacy', +threadInfo: ThreadInfo, - +queuedChanges: ThreadChanges, - +setQueuedChanges: SetState, + +queuedChanges: ThickThreadChanges, + +setQueuedChanges: SetState, +setErrorMessage: SetState, +threadSettingsOperationInProgress: boolean, }; diff --git a/web/modals/threads/settings/thread-settings-utils.js b/web/modals/threads/settings/thread-settings-utils.js --- a/web/modals/threads/settings/thread-settings-utils.js +++ b/web/modals/threads/settings/thread-settings-utils.js @@ -12,7 +12,8 @@ import { containedThreadInfos } from 'lib/selectors/thread-selectors.js'; import { type SetState } from 'lib/types/hook-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { type ThreadChanges } from 'lib/types/thread-types.js'; +import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; +import type { ThickThreadChanges } from 'lib/types/thread-types.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import ThreadDeleteConfirmationModal from './thread-settings-delete-confirmation-modal.react.js'; @@ -20,8 +21,8 @@ type UseOnSaveGeneralThreadSettingsParams = { +threadInfo: ThreadInfo, - +queuedChanges: ThreadChanges, - +setQueuedChanges: SetState, + +queuedChanges: ThickThreadChanges, + +setQueuedChanges: SetState, +setErrorMessage: SetState, }; @@ -32,15 +33,25 @@ params; const dispatchActionPromise = useDispatchActionPromise(); - const callChangeThreadSettings = useChangeThreadSettings(threadInfo); + const callChangeThreadSettings = useChangeThreadSettings(); const changeThreadSettingsAction = React.useCallback(async () => { try { setErrorMessage(''); - return await callChangeThreadSettings({ + const changeThreadSettingsRequest = { threadID: threadInfo.id, changes: queuedChanges, - }); + }; + + const changeThreadSettingsInput = threadTypeIsThick(threadInfo.type) + ? { + thick: true, + threadInfo, + ...changeThreadSettingsRequest, + } + : { thick: false, ...changeThreadSettingsRequest }; + + return await callChangeThreadSettings(changeThreadSettingsInput); } catch (e) { setErrorMessage('unknown_error'); throw e; @@ -52,7 +63,7 @@ queuedChanges, setErrorMessage, setQueuedChanges, - threadInfo.id, + threadInfo, ]); const onSubmit = React.useCallback( @@ -71,8 +82,8 @@ type UseOnSavePrivacySettingsParams = { +threadInfo: ThreadInfo, - +queuedChanges: ThreadChanges, - +setQueuedChanges: SetState, + +queuedChanges: ThickThreadChanges, + +setQueuedChanges: SetState, +setErrorMessage: SetState, }; @@ -90,6 +101,7 @@ try { setErrorMessage(''); const response = await callChangeThreadSettings({ + thick: false, threadID: threadInfo.id, changes: queuedChanges, });