MM-64243 Fix the animation delay while send the post in the channel (#8868)

* MM-64243 Fix the animation delay while send the post in the channel

* Updated test and logic

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Rajat Dabade 2025-06-02 10:59:17 +05:30 committed by GitHub
parent dffb30de47
commit 542420eb73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 27 additions and 132 deletions

View file

@ -162,6 +162,7 @@ describe('Send Draft', () => {
});
it('should call dismissBottomSheet after sending the draft and should call createPost', async () => {
jest.mocked(canPostDraftInChannelOrThread).mockResolvedValueOnce(true);
jest.mocked(sendMessageWithAlert).mockImplementation(() => {
return Promise.resolve();
});

View file

@ -8,12 +8,10 @@ import {removeDraft} from '@actions/local/draft';
import {createPost} from '@actions/remote/post';
import {General} from '@constants';
import {DRAFT_TYPE_DRAFT, DRAFT_TYPE_SCHEDULED} from '@constants/draft';
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import {renderWithEverything} from '@test/intl-test-helper';
import TestHelper from '@test/test_helper';
import {sendMessageWithAlert} from '@utils/post';
import {canPostDraftInChannelOrThread} from '@utils/scheduled_post';
import {showSnackBar} from '@utils/snack_bar';
import SendHandler from './send_handler';
@ -260,53 +258,4 @@ describe('components/post_draft/send_handler/SendHandler', () => {
// Varify removeDraft function is been called.
expect(removeDraft).toHaveBeenCalled();
});
it('should show snackbar if creating post failing when executing sendMessageHandler when send_draft_button is checked', async () => {
// Mock implementation to capture the sendMessageHandler
let capturedHandler: Function;
jest.mocked(sendMessageWithAlert).mockImplementation((params) => {
capturedHandler = params.sendMessageHandler;
return Promise.resolve();
});
jest.mocked(createPost).mockResolvedValueOnce({
error: new Error('Failed to create post'),
});
const props = {
...baseProps,
isFromDraftView: true,
draftType: DRAFT_TYPE_DRAFT,
value: 'test message',
};
const wrapper = renderWithEverything(
<SendHandler {...props}/>, {database},
);
// Find and press the send button
const sendButton = wrapper.getByTestId('send_draft_button');
await act(async () => {
fireEvent.press(sendButton);
});
// Verify sendMessageWithAlert was called
expect(sendMessageWithAlert).toHaveBeenCalledWith(expect.objectContaining({
sendMessageHandler: expect.any(Function),
}));
// Now execute the captured handler to simulate user confirming the send
await act(async () => {
await capturedHandler();
});
expect(showSnackBar).toHaveBeenCalledWith({
barType: SNACK_BAR_TYPE.CREATE_POST_ERROR,
customMessage: 'Failed to create post',
type: 'error',
});
// Varify removeDraft function is been called.
expect(removeDraft).not.toHaveBeenCalled();
});
});

View file

@ -13,13 +13,11 @@ import {createPost} from '@actions/remote/post';
import {createScheduledPost} from '@actions/remote/scheduled_post';
import {handleCallsSlashCommand} from '@calls/actions';
import {Events, Screens} from '@constants';
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import {useServerUrl} from '@context/server';
import DatabaseManager from '@database/manager';
import DraftUploadManager from '@managers/draft_upload_manager';
import {getPostById} from '@queries/servers/post';
import * as DraftUtils from '@utils/draft';
import {showSnackBar} from '@utils/snack_bar';
import {useHandleSendMessage} from './handle_send_message';
@ -87,6 +85,10 @@ describe('useHandleSendMessage', () => {
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('should handle basic message send', async () => {
const {result} = renderHook(() => useHandleSendMessage(defaultProps), {wrapper});
@ -395,24 +397,6 @@ describe('useHandleSendMessage', () => {
expect(defaultProps.clearDraft).toHaveBeenCalled();
});
it('should handle failed post creation', async () => {
jest.mocked(createPost).mockResolvedValueOnce({
error: new Error('Failed to create post'),
});
const props = {
...defaultProps,
value: 'test message',
};
const {result} = renderHook(() => useHandleSendMessage(props), {wrapper});
await act(async () => {
const response = await result.current.handleSendMessage();
expect(response?.error).toBeDefined();
});
});
it('should fetch and handle channel timezones', async () => {
jest.mocked(getChannelTimezones).mockResolvedValueOnce({
channelTimezones: ['UTC', 'America/New_York'],
@ -434,21 +418,6 @@ describe('useHandleSendMessage', () => {
);
});
it('should not allow sending during message submission', () => {
const props = {
...defaultProps,
value: 'test message',
};
const {result} = renderHook(() => useHandleSendMessage(props), {wrapper});
act(() => {
result.current.handleSendMessage();
});
expect(result.current.canSend).toBe(false);
});
it('should handle channel-wide mentions confirmation', async () => {
jest.spyOn(DraftUtils, 'textContainsAtAllAtChannel').mockReturnValue(true);
jest.spyOn(DraftUtils, 'alertChannelWideMention');
@ -662,33 +631,6 @@ describe('useHandleSendMessage', () => {
});
});
it('should handle failed post creation', async () => {
jest.mocked(createPost).mockResolvedValueOnce({
error: new Error('Failed to create post'),
});
const props = {
...defaultProps,
isFromDraftView: true,
value: 'test message',
};
const {result} = renderHook(() => useHandleSendMessage(props), {wrapper});
await act(async () => {
const response = await result.current.handleSendMessage();
expect(response?.error).toBeDefined();
});
expect(showSnackBar).toHaveBeenCalledWith({
barType: SNACK_BAR_TYPE.CREATE_POST_ERROR,
customMessage: 'Failed to create post',
type: 'error',
});
expect(defaultProps.clearDraft).not.toHaveBeenCalled();
expect(DeviceEventEmitter.emit).not.toHaveBeenCalled();
});
it('should show alert when root post is not found', async () => {
jest.mocked(getPostById).mockResolvedValueOnce(undefined);

View file

@ -106,7 +106,7 @@ export const useHandleSendMessage = ({
setSendingMessage(false);
}, [serverUrl, rootId, clearDraft]);
const doSubmitMessage = useCallback(async (schedulingInfo?: SchedulingInfo): Promise<CreateResponse> => {
const doSubmitMessage = useCallback(async (schedulingInfo?: SchedulingInfo) => {
const postFiles = files.filter((f) => !f.failed);
const post = {
user_id: currentUserId,
@ -128,20 +128,16 @@ export const useHandleSendMessage = ({
let response: CreateResponse;
if (schedulingInfo) {
response = await createScheduledPost(serverUrl, scheduledPostFromPost(post, schedulingInfo, postPriority, postFiles));
} else {
response = await createPost(serverUrl, post, postFiles);
}
if (response.error && isFromDraftView) {
showSnackBar({
barType: SNACK_BAR_TYPE.CREATE_POST_ERROR,
customMessage: getErrorMessage(response.error),
type: MESSAGE_TYPE.ERROR,
});
return response;
}
if (isFromDraftView) {
if (response.error) {
showSnackBar({
barType: SNACK_BAR_TYPE.SCHEDULED_POST_CREATION_ERROR,
customMessage: getErrorMessage(response.error),
type: MESSAGE_TYPE.ERROR,
});
} else {
clearDraft();
}
} else if (isFromDraftView) {
const shouldClearDraft = await canPostDraftInChannelOrThread({
serverUrl,
rootId,
@ -152,16 +148,23 @@ export const useHandleSendMessage = ({
deactivatedChannel,
});
if (shouldClearDraft) {
clearDraft();
if (!shouldClearDraft) {
return;
}
createPost(serverUrl, post, postFiles);
clearDraft();
// Early return to avoid calling DeviceEventEmitter.emit
return;
} else {
// Response error is handled at the post level so don't have to wait to clear draft
createPost(serverUrl, post, postFiles);
clearDraft();
}
setSendingMessage(false);
DeviceEventEmitter.emit(Events.POST_LIST_SCROLL_TO_BOTTOM, rootId ? Screens.THREAD : Screens.CHANNEL);
return response;
}, [files, currentUserId, channelId, rootId, value, postPriority, isFromDraftView, serverUrl, intl, canPost, channelIsArchived, channelIsReadOnly, deactivatedChannel, clearDraft]);
const showSendToAllOrChannelOrHereAlert = useCallback((calculatedMembersCount: number, atHere: boolean, schedulingInfo?: SchedulingInfo) => {