Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/moody-spoons-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': minor
---

Disables the delete message confirmation button to prevent the action from being triggered while the request is in progress
38 changes: 8 additions & 30 deletions apps/meteor/client/lib/chats/flows/requestMessageDeletion.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { IMessage } from '@rocket.chat/core-typings';
import { GenericModal, imperativeModal } from '@rocket.chat/ui-client';
import { imperativeModal } from '@rocket.chat/ui-client';

import { t } from '../../../../app/utils/lib/i18n';
import DeleteMessageConfirmModal from '../../../views/room/modals/DeleteMessageConfirmModal';
import { dispatchToastMessage } from '../../toast';
import type { ChatAPI } from '../ChatAPI';

Expand All @@ -15,28 +16,6 @@ export const requestMessageDeletion = async (chat: ChatAPI, message: IMessage):

await new Promise<void>((resolve, reject) => {
const mid = chat.currentEditingMessage.getMID();
const onConfirm = async (): Promise<void> => {
try {
if (!(await chat.data.canDeleteMessage(message))) {
dispatchToastMessage({ type: 'error', message: t('Message_deleting_blocked') });
return;
}
await chat.data.deleteMessage(message);

imperativeModal.close();

if (mid === message._id) {
chat.currentEditingMessage.stop();
}
chat.composer?.focus();

dispatchToastMessage({ type: 'success', message: t('Your_entry_has_been_deleted') });
resolve();
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
reject(error);
}
};

const onCloseModal = async (): Promise<void> => {
imperativeModal.close();
Expand All @@ -50,14 +29,13 @@ export const requestMessageDeletion = async (chat: ChatAPI, message: IMessage):
};

imperativeModal.open({
component: GenericModal,
component: DeleteMessageConfirmModal,
props: {
title: t('Are_you_sure'),
children: room ? t('The_message_is_a_discussion_you_will_not_be_able_to_recover') : t('You_will_not_be_able_to_recover'),
variant: 'danger',
confirmText: t('Yes_delete_it'),
onConfirm,
onClose: onCloseModal,
room,
chat,
resolve,
reject,
message,
onCancel: onCloseModal,
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { IMessage, IRoom } from '@rocket.chat/core-typings';
import { GenericModal } from '@rocket.chat/ui-client';
import { useToastMessageDispatch } from '@rocket.chat/ui-contexts';
import { useMutation } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';

import type { ChatAPI } from '../../../../lib/chats/ChatAPI';

const DeleteMessageConfirmModal = ({
room,
chat,
resolve,
reject,
onCancel,
message,
}: {
room?: IRoom;
chat: ChatAPI;
resolve: () => void;
reject: (reason?: any) => void;
message: IMessage;
onCancel: () => void;
}) => {
const { t } = useTranslation();
const mid = chat.currentEditingMessage.getMID();
const dispatchToastMessage = useToastMessageDispatch();

const deleteMessageMutation = useMutation({
mutationFn: async () => {
if (!(await chat.data.canDeleteMessage(message))) {
throw new Error(t('Message_deleting_blocked'));
}

await chat.data.deleteMessage(message);
},
onSuccess: () => {
if (mid === message._id) {
chat.currentEditingMessage.stop();
}
chat.composer?.focus();

dispatchToastMessage({ type: 'success', message: t('Your_entry_has_been_deleted') });
resolve();
},
onError: (error) => {
dispatchToastMessage({ type: 'error', message: error });
reject(error);
},
onSettled: () => {
onCancel();
},
});

return (
<GenericModal
variant='danger'
title={t('Are_you_sure')}
confirmText={t('Yes_delete_it')}
children={room ? t('The_message_is_a_discussion_you_will_not_be_able_to_recover') : t('You_will_not_be_able_to_recover')}
onConfirm={deleteMessageMutation.mutate}
onCancel={onCancel}
confirmDisabled={deleteMessageMutation.isPending}
/>
);
};

export default DeleteMessageConfirmModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './DeleteMessageConfirmModal';
7 changes: 3 additions & 4 deletions apps/meteor/tests/e2e/message-actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,10 @@ test.describe.serial('message-actions', () => {
await expect(poHomeChannel.content.lastUserMessageBody).toHaveText('this message was edited');
});

test('expect message is deleted', async ({ page }) => {
test('should delete message ', async () => {
await poHomeChannel.content.sendMessage('Message to delete');
await poHomeChannel.content.openLastMessageMenu();
await page.locator('role=menuitem[name="Delete"]').click();
await page.locator('#modal-root .rcx-button-group--align-end .rcx-button--danger').click();
await poHomeChannel.content.deleteLastMessage();

await expect(poHomeChannel.content.lastUserMessage.locator('[data-qa-type="message-body"]:has-text("Message to delete")')).toHaveCount(
0,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,9 @@ export class HomeContent {
}

async openLastMessageMenu(): Promise<void> {
await this.page.locator('[data-qa-type="message"]').last().hover();
await this.page.locator('[data-qa-type="message"]').last().locator('role=button[name="More"]').waitFor();
await this.page.locator('[data-qa-type="message"]').last().locator('role=button[name="More"]').click();
await this.lastUserMessage.hover();
await this.lastUserMessage.getByRole('button', { name: 'More', exact: true }).waitFor();
await this.lastUserMessage.getByRole('button', { name: 'More', exact: true }).click();
}

get threadMessageList(): Locator {
Expand Down Expand Up @@ -574,6 +574,7 @@ export class HomeContent {
await this.openLastMessageMenu();
await this.btnOptionDeleteMessage.click();
await this.btnModalConfirmDelete.click();
await expect(this.btnModalConfirmDelete).toBeDisabled();
}

get btnClearSelection() {
Expand Down
Loading