diff --git a/ts/components/conversation/conversation-details/EditConversationAttributesModal.tsx b/ts/components/conversation/conversation-details/EditConversationAttributesModal.tsx index 3a46ccde66d1..d2a54e6618fe 100644 --- a/ts/components/conversation/conversation-details/EditConversationAttributesModal.tsx +++ b/ts/components/conversation/conversation-details/EditConversationAttributesModal.tsx @@ -55,9 +55,11 @@ export const EditConversationAttributesModal: FunctionComponent = ({ const [avatar, setAvatar] = useState( externalAvatarPath ? TEMPORARY_AVATAR_VALUE : undefined ); - const [title, setTitle] = useState(externalTitle); + const [rawTitle, setRawTitle] = useState(externalTitle); const [hasAvatarChanged, setHasAvatarChanged] = useState(false); + const trimmedTitle = rawTitle.trim(); + useEffect(() => { const startingAvatarPath = startingAvatarPathRef.current; if (!startingAvatarPath) { @@ -90,14 +92,14 @@ export const EditConversationAttributesModal: FunctionComponent = ({ const hasChangedExternally = startingAvatarPathRef.current !== externalAvatarPath || startingTitleRef.current !== externalTitle; - const hasTitleChanged = title !== externalTitle; + const hasTitleChanged = trimmedTitle !== externalTitle.trim(); const isRequestActive = requestState === RequestState.Active; const canSubmit = !isRequestActive && (hasChangedExternally || hasTitleChanged || hasAvatarChanged) && - title.length > 0; + trimmedTitle.length > 0; const onSubmit: FormEventHandler = event => { event.preventDefault(); @@ -110,7 +112,7 @@ export const EditConversationAttributesModal: FunctionComponent = ({ request.avatar = avatar; } if (hasTitleChanged) { - request.title = title; + request.title = trimmedTitle; } makeRequest(request); }; @@ -150,8 +152,8 @@ export const EditConversationAttributesModal: FunctionComponent = ({ {requestState === RequestState.InactiveWithError && ( diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index ac3eb1dec0d9..03e5d5af35f1 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -731,7 +731,7 @@ function createGroup(): ThunkAction< try { const conversation = await groups.createGroupV2({ - name: composer.groupName, + name: composer.groupName.trim(), avatar: composer.groupAvatar, conversationIds: composer.selectedConversationIds, }); diff --git a/ts/test-electron/state/ducks/conversations_test.ts b/ts/test-electron/state/ducks/conversations_test.ts index e94f93b005a7..1543cfbad018 100644 --- a/ts/test-electron/state/ducks/conversations_test.ts +++ b/ts/test-electron/state/ducks/conversations_test.ts @@ -743,6 +743,28 @@ describe('both/state/ducks/conversations', () => { }); }); + it("trims the group's title before calling groups.createGroupV2", async () => { + await createGroup()( + sinon.spy(), + () => ({ + ...getEmptyRootState(), + conversations: { + ...conversationsState, + composer: { + ...conversationsState.composer, + groupName: ' To Trim \t', + }, + }, + }), + null + ); + + sinon.assert.calledWith( + createGroupStub, + sinon.match({ name: 'To Trim' }) + ); + }); + it('dispatches a CREATE_GROUP_REJECTED action if group creation fails, which marks the state with an error', async () => { createGroupStub.rejects(new Error('uh oh'));