From 79bb2e3d408708142f0507ca08c8cc54817cb3bc Mon Sep 17 00:00:00 2001 From: Eric Mc Sween <5454374+emcsween@users.noreply.github.com> Date: Tue, 18 Feb 2025 08:57:32 -0500 Subject: [PATCH] Merge pull request #23673 from overleaf/em-reviewer-role-delete-replies Let reviewers delete their own comments GitOrigin-RevId: bd5341a917a3e886dc573d7e42d12343da6700cd --- .../Messages/MessageHttpController.js | 11 ++++++++ .../js/Features/Messages/MessageManager.js | 8 ++++++ services/chat/chat.yaml | 28 +++++++++++++++++++ .../app/src/Features/Chat/ChatApiHandler.js | 11 ++++++++ .../components/review-panel-comment.tsx | 20 +++++++++++-- .../context/threads-context.tsx | 6 ++++ 6 files changed, 82 insertions(+), 2 deletions(-) diff --git a/services/chat/app/js/Features/Messages/MessageHttpController.js b/services/chat/app/js/Features/Messages/MessageHttpController.js index a20d005864..45208e2c03 100644 --- a/services/chat/app/js/Features/Messages/MessageHttpController.js +++ b/services/chat/app/js/Features/Messages/MessageHttpController.js @@ -74,6 +74,10 @@ export async function deleteMessage(context) { return await callMessageHttpController(context, _deleteMessage) } +export async function deleteUserMessage(context) { + return await callMessageHttpController(context, _deleteUserMessage) +} + export async function getResolvedThreadIds(context) { return await callMessageHttpController(context, _getResolvedThreadIds) } @@ -190,6 +194,13 @@ const _deleteMessage = async (req, res) => { res.status(204) } +const _deleteUserMessage = async (req, res) => { + const { projectId, threadId, userId, messageId } = req.params + const room = await ThreadManager.findOrCreateThread(projectId, threadId) + await MessageManager.deleteUserMessage(userId, room._id, messageId) + res.status(204) +} + const _getResolvedThreadIds = async (req, res) => { const { projectId } = req.params const resolvedThreadIds = await ThreadManager.getResolvedThreadIds(projectId) diff --git a/services/chat/app/js/Features/Messages/MessageManager.js b/services/chat/app/js/Features/Messages/MessageManager.js index cb8818e3b6..efff22a2a4 100644 --- a/services/chat/app/js/Features/Messages/MessageManager.js +++ b/services/chat/app/js/Features/Messages/MessageManager.js @@ -77,6 +77,14 @@ export async function deleteMessage(roomId, messageId) { await db.messages.deleteOne(query) } +export async function deleteUserMessage(userId, roomId, messageId) { + await db.messages.deleteOne({ + _id: new ObjectId(messageId), + user_id: new ObjectId(userId), + room_id: new ObjectId(roomId), + }) +} + function _ensureIdsAreObjectIds(query) { if (query.user_id && !(query.user_id instanceof ObjectId)) { query.user_id = new ObjectId(query.user_id) diff --git a/services/chat/chat.yaml b/services/chat/chat.yaml index 3ccdf9bc30..35ed3d378d 100644 --- a/services/chat/chat.yaml +++ b/services/chat/chat.yaml @@ -177,6 +177,34 @@ paths: '204': description: No Content description: 'Delete message with Message ID provided, from the Thread with ThreadID and ProjectID provided' + '/project/{projectId}/thread/{threadId}/user/{userId}/messages/{messageId}': + parameters: + - schema: + type: string + name: projectId + in: path + required: true + - schema: + type: string + name: threadId + in: path + required: true + - schema: + type: string + name: userId + in: path + required: true + - schema: + type: string + name: messageId + in: path + required: true + delete: + summary: Delete message written by a given user + operationId: deleteUserMessage + responses: + '204': + description: No Content '/project/{projectId}/thread/{threadId}/resolve': parameters: - schema: diff --git a/services/web/app/src/Features/Chat/ChatApiHandler.js b/services/web/app/src/Features/Chat/ChatApiHandler.js index d581827a89..2929891c8c 100644 --- a/services/web/app/src/Features/Chat/ChatApiHandler.js +++ b/services/web/app/src/Features/Chat/ChatApiHandler.js @@ -90,6 +90,15 @@ async function deleteMessage(projectId, threadId, messageId) { ) } +async function deleteUserMessage(projectId, threadId, userId, messageId) { + await fetchNothing( + chatApiUrl( + `/project/${projectId}/thread/${threadId}/user/${userId}/messages/${messageId}` + ), + { method: 'DELETE' } + ) +} + async function getResolvedThreadIds(projectId) { const body = await fetchJson( chatApiUrl(`/project/${projectId}/resolved-thread-ids`) @@ -134,6 +143,7 @@ module.exports = { deleteThread: callbackify(deleteThread), editMessage: callbackify(editMessage), deleteMessage: callbackify(deleteMessage), + deleteUserMessage: callbackify(deleteUserMessage), getResolvedThreadIds: callbackify(getResolvedThreadIds), duplicateCommentThreads: callbackify(duplicateCommentThreads), generateThreadData: callbackify(generateThreadData), @@ -148,6 +158,7 @@ module.exports = { deleteThread, editMessage, deleteMessage, + deleteUserMessage, getResolvedThreadIds, duplicateCommentThreads, generateThreadData, diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx index ae0535e941..6585d00ee9 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx @@ -12,6 +12,7 @@ import { ThreadId, } from '../../../../../types/review-panel/review-panel' import { useModalsContext } from '@/features/ide-react/context/modals-context' +import { usePermissionsContext } from '@/features/ide-react/context/permissions-context' import { useTranslation } from 'react-i18next' import { debugConsole } from '@/utils/debugging' @@ -29,11 +30,13 @@ export const ReviewPanelComment = memo<{ resolveThread, editMessage, deleteMessage, + deleteOwnMessage, deleteThread, addMessage, } = useThreadsActionsContext() const { showGenericMessageModal } = useModalsContext() const { t } = useTranslation() + const permissions = usePermissionsContext() const [processing, setProcessing] = useState(false) @@ -74,7 +77,13 @@ export const ReviewPanelComment = memo<{ async (commentId: CommentId) => { setProcessing(true) try { - await deleteMessage(comment.op.t, commentId) + if (permissions.write) { + // Owners and editors can delete any message + await deleteMessage(comment.op.t, commentId) + } else { + // Reviewers can only delete their own messages + await deleteOwnMessage(comment.op.t, commentId) + } } catch (err) { debugConsole.error(err) showGenericMessageModal( @@ -85,7 +94,14 @@ export const ReviewPanelComment = memo<{ setProcessing(false) } }, - [comment.op.t, deleteMessage, showGenericMessageModal, t] + [ + comment.op.t, + deleteMessage, + deleteOwnMessage, + showGenericMessageModal, + t, + permissions.write, + ] ) const handleDeleteThread = useCallback( diff --git a/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx b/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx index 4bf5b002f7..b9597bae7e 100644 --- a/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx +++ b/services/web/frontend/js/features/review-panel-new/context/threads-context.tsx @@ -38,6 +38,7 @@ type ThreadsActions = { content: string ) => Promise deleteMessage: (threadId: ThreadId, commentId: CommentId) => Promise + deleteOwnMessage: (threadId: ThreadId, commentId: CommentId) => Promise } const ThreadsActionsContext = createContext( @@ -288,6 +289,11 @@ export const ThreadsProvider: FC = ({ children }) => { `/project/${projectId}/thread/${threadId}/messages/${commentId}` ) }, + async deleteOwnMessage(threadId: ThreadId, commentId: CommentId) { + await deleteJSON( + `/project/${projectId}/thread/${threadId}/own-messages/${commentId}` + ) + }, }), [currentDocument, projectId] )