From fe37dcd6d9bce38d98584a1d9e9956d94f240049 Mon Sep 17 00:00:00 2001 From: Antoine Jaussoin Date: Thu, 26 Jan 2023 18:31:58 +0000 Subject: [PATCH] Confirm post deletion (#461) --- backend/src/common/types.ts | 1 + frontend/src/common/types.ts | 1 + frontend/src/translations/locales/ar-SA.json | 8 +++- frontend/src/translations/locales/de-DE.json | 10 +++- frontend/src/translations/locales/en-GB.json | 32 +++++++------ frontend/src/translations/locales/es-ES.json | 8 +++- frontend/src/translations/locales/fr-FR.json | 8 +++- frontend/src/translations/locales/hu-HU.json | 8 +++- frontend/src/translations/locales/it-IT.json | 8 +++- frontend/src/translations/locales/ja-JP.json | 8 +++- frontend/src/translations/locales/nl-NL.json | 8 +++- frontend/src/translations/locales/pl-PL.json | 8 +++- frontend/src/translations/locales/pt-BR.json | 8 +++- frontend/src/translations/locales/pt-PT.json | 8 +++- frontend/src/translations/locales/uk-UA.json | 8 +++- frontend/src/translations/locales/zh-CN.json | 8 +++- frontend/src/translations/locales/zh-TW.json | 8 +++- frontend/src/views/game/board/post/Post.tsx | 22 ++++++++- .../game/board/post/__tests__/Post.test.tsx | 46 ++----------------- 19 files changed, 145 insertions(+), 71 deletions(-) diff --git a/backend/src/common/types.ts b/backend/src/common/types.ts index da3180efb..8ad54b7e1 100644 --- a/backend/src/common/types.ts +++ b/backend/src/common/types.ts @@ -195,6 +195,7 @@ export type TrackingEvent = | 'game/session/disconnect' | 'game/session/unexpected-disconnection' | 'game/session/user-ready' + | 'game/post/delete' | 'game/post/giphy/open' | 'game/post/giphy/choose' | 'game/post/giphy/toggle' diff --git a/frontend/src/common/types.ts b/frontend/src/common/types.ts index da3180efb..8ad54b7e1 100644 --- a/frontend/src/common/types.ts +++ b/frontend/src/common/types.ts @@ -195,6 +195,7 @@ export type TrackingEvent = | 'game/session/disconnect' | 'game/session/unexpected-disconnection' | 'game/session/user-ready' + | 'game/post/delete' | 'game/post/giphy/open' | 'game/post/giphy/choose' | 'game/post/giphy/toggle' diff --git a/frontend/src/translations/locales/ar-SA.json b/frontend/src/translations/locales/ar-SA.json index 8483684b3..646103210 100644 --- a/frontend/src/translations/locales/ar-SA.json +++ b/frontend/src/translations/locales/ar-SA.json @@ -136,7 +136,13 @@ "maxPostsReached": "لقد وصلت إلى الحد الأقصى لعدد المشاركات التي حددها المشرف", "iAmDone": "لقد انتهيت!", "iAmNotDoneYet": "أنا لم أنتهِ بعد...", - "userIsReady": "{{user}} جاهز!" + "userIsReady": "{{user}} جاهز!", + "deleteConfirmation": { + "title": "حذف هذا المنشور؟", + "description": "سيؤدي هذا إلى حذف المنشور وجميع أصواته. لا يمكن التراجع عن هذا الإجراء.", + "confirm": "حذف هذا المنشور", + "cancel": "لقد غيرت رأيي" + } }, "GameMenu": { "board": "المجلس", diff --git a/frontend/src/translations/locales/de-DE.json b/frontend/src/translations/locales/de-DE.json index 67ee2197a..c864819d8 100644 --- a/frontend/src/translations/locales/de-DE.json +++ b/frontend/src/translations/locales/de-DE.json @@ -136,7 +136,13 @@ "maxPostsReached": "Sie haben die vom Moderator festgelegte maximale Anzahl von Beiträgen erreicht.", "iAmDone": "Ich bin fertig!", "iAmNotDoneYet": "Ich bin noch nicht fertig...", - "userIsReady": "{{user}} ist bereit!" + "userIsReady": "{{user}} ist bereit!", + "deleteConfirmation": { + "title": "Diesen Beitrag löschen?", + "description": "Dies löscht den Beitrag und alle seine Abstimmungen. Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm": "Diesen Beitrag löschen", + "cancel": "Ich habe meine Meinung geändert" + } }, "GameMenu": { "board": "Board", @@ -182,7 +188,7 @@ "or": "oder" }, "SocialMediaLogin": { - "header": "OAuth", + "header": "OAuh", "info": "Dies wird einen Drittanbieter Ihrer Wahl verwenden, um Sie zu authentifizieren. Es wird kein Passwort gespeichert." }, "AuthCommon": { diff --git a/frontend/src/translations/locales/en-GB.json b/frontend/src/translations/locales/en-GB.json index ed9329348..0de4a0c01 100644 --- a/frontend/src/translations/locales/en-GB.json +++ b/frontend/src/translations/locales/en-GB.json @@ -121,22 +121,28 @@ "reconnect": "Reconnect", "notLoggedIn": "You are not logged in. You can view this session as a spectator, but must login to participate.", "error_action_unauthorised": "You are not allowed to perform this action.", - "error_cannot_edit_group": "Editing the group failed.", - "error_cannot_edit_post": "Editing the post failed.", - "error_cannot_get_session": "Could not get the session data. Please reload the page.", - "error_cannot_register_vote": "Your vote was not registered successfully.", - "error_cannot_save_group": "The group you created could not be saved.", - "error_cannot_save_post": "The post you created could not be saved.", - "error_cannot_delete_group": "The group could not be deleted", - "error_cannot_delete_post": "The post could not be deleted", - "error_cannot_rename_session": "Renaming the session failed", - "error_cannot_save_columns": "Saving columns failed", - "error_cannot_save_options": "Saving options failed", - "error_cannot_cancel_votes": "Cancelling votes failed", + "error_cannot_edit_group": "Editing the group failed.", + "error_cannot_edit_post": "Editing the post failed.", + "error_cannot_get_session": "Could not get the session data. Please reload the page.", + "error_cannot_register_vote": "Your vote was not registered successfully.", + "error_cannot_save_group": "The group you created could not be saved.", + "error_cannot_save_post": "The post you created could not be saved.", + "error_cannot_delete_group": "The group could not be deleted", + "error_cannot_delete_post": "The post could not be deleted", + "error_cannot_rename_session": "Renaming the session failed", + "error_cannot_save_columns": "Saving columns failed", + "error_cannot_save_options": "Saving options failed", + "error_cannot_cancel_votes": "Cancelling votes failed", "maxPostsReached": "You have reached the maximum number of posts set by the moderator.", "iAmDone": "I'm done!", "iAmNotDoneYet": "I'm not done yet...", - "userIsReady": "{{user}} is ready!" + "userIsReady": "{{user}} is ready!", + "deleteConfirmation": { + "title": "Delete this post?", + "description":"This will delete the post and all its votes. This action cannot be undone.", + "confirm": "Delete this post", + "cancel": "I have changed my mind" + } }, "GameMenu": { "board": "Board", diff --git a/frontend/src/translations/locales/es-ES.json b/frontend/src/translations/locales/es-ES.json index 0aacaa954..b9dcc0299 100644 --- a/frontend/src/translations/locales/es-ES.json +++ b/frontend/src/translations/locales/es-ES.json @@ -136,7 +136,13 @@ "maxPostsReached": "Has alcanzado el número máximo de mensajes establecidos por el moderador.", "iAmDone": "¡He terminado!", "iAmNotDoneYet": "No he terminado todavía...", - "userIsReady": "¡{{user}} está listo!" + "userIsReady": "¡{{user}} está listo!", + "deleteConfirmation": { + "title": "¿Eliminar esta publicación?", + "description": "Esto eliminará el mensaje y todos sus votos. Esta acción no se puede deshacer.", + "confirm": "Eliminar esta publicación", + "cancel": "He cambiado de opinión" + } }, "GameMenu": { "board": "Tablero", diff --git a/frontend/src/translations/locales/fr-FR.json b/frontend/src/translations/locales/fr-FR.json index 078a4798a..dd4a8e5a2 100644 --- a/frontend/src/translations/locales/fr-FR.json +++ b/frontend/src/translations/locales/fr-FR.json @@ -136,7 +136,13 @@ "maxPostsReached": "Vous avez atteint le nombre de posts maximum prévu par le modérateur.", "iAmDone": "J'ai fini !", "iAmNotDoneYet": "Je n'ai pas encore fini...", - "userIsReady": "{{user}} est prêt !" + "userIsReady": "{{user}} est prêt !", + "deleteConfirmation": { + "title": "Supprimer ce post ?", + "description": "Vous allez supprimer le post et tous ses votes. Cette action est définitive.", + "confirm": "Supprimer ce post", + "cancel": "J'ai changé d'avis" + } }, "GameMenu": { "board": "Board", diff --git a/frontend/src/translations/locales/hu-HU.json b/frontend/src/translations/locales/hu-HU.json index dbd34311e..043345b3b 100644 --- a/frontend/src/translations/locales/hu-HU.json +++ b/frontend/src/translations/locales/hu-HU.json @@ -136,7 +136,13 @@ "maxPostsReached": "Elérted a moderátor által beállított maximális bejegyzések számát.", "iAmDone": "Kész vagyok!", "iAmNotDoneYet": "Még nem végeztem...", - "userIsReady": "{{user}} kész!" + "userIsReady": "{{user}} kész!", + "deleteConfirmation": { + "title": "", + "description": "", + "confirm": "", + "cancel": "" + } }, "GameMenu": { "board": "Tábla", diff --git a/frontend/src/translations/locales/it-IT.json b/frontend/src/translations/locales/it-IT.json index 203f58170..196a40b49 100644 --- a/frontend/src/translations/locales/it-IT.json +++ b/frontend/src/translations/locales/it-IT.json @@ -136,7 +136,13 @@ "maxPostsReached": "Hai raggiunto il numero massimo di post impostati dal moderatore.", "iAmDone": "Ho finito!", "iAmNotDoneYet": "Non ho ancora finito...", - "userIsReady": "{{user}} è pronto!" + "userIsReady": "{{user}} è pronto!", + "deleteConfirmation": { + "title": "Eliminare questo post?", + "description": "Questo cancellerà il post e tutti i suoi voti. Questa azione non può essere annullata.", + "confirm": "Elimina questo post", + "cancel": "Ho cambiato idea" + } }, "GameMenu": { "board": "Tavola", diff --git a/frontend/src/translations/locales/ja-JP.json b/frontend/src/translations/locales/ja-JP.json index 44099d7dc..a9f4b7743 100644 --- a/frontend/src/translations/locales/ja-JP.json +++ b/frontend/src/translations/locales/ja-JP.json @@ -136,7 +136,13 @@ "maxPostsReached": "モデレーターが設定した投稿の最大数に達しました。", "iAmDone": "終わりました!", "iAmNotDoneYet": "まだ完了していません...", - "userIsReady": "{{user}} の準備ができました!" + "userIsReady": "{{user}} の準備ができました!", + "deleteConfirmation": { + "title": "この投稿を削除しますか?", + "description": "投稿とすべての投票を削除します。この操作は元に戻せません。", + "confirm": "この投稿を削除", + "cancel": "気が変わりました" + } }, "GameMenu": { "board": "ボード", diff --git a/frontend/src/translations/locales/nl-NL.json b/frontend/src/translations/locales/nl-NL.json index c4a71f15d..bb5cb8bfb 100644 --- a/frontend/src/translations/locales/nl-NL.json +++ b/frontend/src/translations/locales/nl-NL.json @@ -136,7 +136,13 @@ "maxPostsReached": "Je hebt het maximum aantal berichten bereikt dat door de moderator is ingesteld.", "iAmDone": "Ik ben klaar!", "iAmNotDoneYet": "Ik ben nog niet klaar...", - "userIsReady": "{{user}} is klaar!" + "userIsReady": "{{user}} is klaar!", + "deleteConfirmation": { + "title": "Dit bericht verwijderen?", + "description": "Dit zal het bericht en al zijn stemmen verwijderen. Deze actie kan niet ongedaan worden gemaakt.", + "confirm": "Dit bericht verwijderen", + "cancel": "Ik ben van gedachten veranderd" + } }, "GameMenu": { "board": "Bord", diff --git a/frontend/src/translations/locales/pl-PL.json b/frontend/src/translations/locales/pl-PL.json index 9f99cb4b7..3cc8c5b0b 100644 --- a/frontend/src/translations/locales/pl-PL.json +++ b/frontend/src/translations/locales/pl-PL.json @@ -136,7 +136,13 @@ "maxPostsReached": "Osiągnąłeś maksymalną liczbę postów ustawionych przez moderatora.", "iAmDone": "Gotowe!", "iAmNotDoneYet": "Jeszcze nie skończyłem...", - "userIsReady": "{{user}} jest gotowy!" + "userIsReady": "{{user}} jest gotowy!", + "deleteConfirmation": { + "title": "Usunąć ten post?", + "description": "Spowoduje to usunięcie wpisu i wszystkich jego głosów. Tej czynności nie można cofnąć.", + "confirm": "Usuń ten post", + "cancel": "Zmieniłem zdanie" + } }, "GameMenu": { "board": "Tablica", diff --git a/frontend/src/translations/locales/pt-BR.json b/frontend/src/translations/locales/pt-BR.json index 845697955..563bbb083 100644 --- a/frontend/src/translations/locales/pt-BR.json +++ b/frontend/src/translations/locales/pt-BR.json @@ -136,7 +136,13 @@ "maxPostsReached": "Você atingiu o número máximo de postagens definido pelo moderador.", "iAmDone": "Estou pronto!", "iAmNotDoneYet": "Eu não terminei ainda...", - "userIsReady": "{{user}} está pronto!" + "userIsReady": "{{user}} está pronto!", + "deleteConfirmation": { + "title": "Excluir esta publicação?", + "description": "Isto irá excluir a publicação e todos os seus votos. Esta ação não pode ser desfeita.", + "confirm": "Excluir esta publicação", + "cancel": "Mudei de ideia" + } }, "GameMenu": { "board": "Tabuleiro", diff --git a/frontend/src/translations/locales/pt-PT.json b/frontend/src/translations/locales/pt-PT.json index b09eac4c6..ffd16c66e 100644 --- a/frontend/src/translations/locales/pt-PT.json +++ b/frontend/src/translations/locales/pt-PT.json @@ -136,7 +136,13 @@ "maxPostsReached": "Você atingiu o número máximo de postagens definido pelo moderador.", "iAmDone": "Estou pronto!", "iAmNotDoneYet": "Eu não terminei ainda...", - "userIsReady": "{{user}} está pronto!" + "userIsReady": "{{user}} está pronto!", + "deleteConfirmation": { + "title": "Excluir esta publicação?", + "description": "Isto irá excluir a publicação e todos os seus votos. Esta ação não pode ser desfeita.", + "confirm": "Excluir esta publicação", + "cancel": "Mudei de ideia" + } }, "GameMenu": { "board": "Tabuleiro", diff --git a/frontend/src/translations/locales/uk-UA.json b/frontend/src/translations/locales/uk-UA.json index 8def98d3a..fc5964832 100644 --- a/frontend/src/translations/locales/uk-UA.json +++ b/frontend/src/translations/locales/uk-UA.json @@ -136,7 +136,13 @@ "maxPostsReached": "Ви досягли максимальної кількості повідомлень, встановлених модератором.", "iAmDone": "Я завершив!", "iAmNotDoneYet": "Я ще не завершив...", - "userIsReady": "{{user}} готовий!" + "userIsReady": "{{user}} готовий!", + "deleteConfirmation": { + "title": "Видалити це повідомлення?", + "description": "Ця дія видалить публікацію та всі її голоси. Цю дію не можна скасувати.", + "confirm": "Видалити це повідомлення", + "cancel": "Я передумала" + } }, "GameMenu": { "board": "Дошка", diff --git a/frontend/src/translations/locales/zh-CN.json b/frontend/src/translations/locales/zh-CN.json index f901fe164..531577c81 100644 --- a/frontend/src/translations/locales/zh-CN.json +++ b/frontend/src/translations/locales/zh-CN.json @@ -136,7 +136,13 @@ "maxPostsReached": "您已经达到了版主设置的帖子的最大数量。", "iAmDone": "我已完成!", "iAmNotDoneYet": "我还没有完成...", - "userIsReady": "{{user}} 已准备好了!" + "userIsReady": "{{user}} 已准备好了!", + "deleteConfirmation": { + "title": "删除此帖子?", + "description": "这将删除帖子及其所有投票。此操作无法撤消。", + "confirm": "删除此帖子", + "cancel": "我改变了主意。" + } }, "GameMenu": { "board": "棋盘", diff --git a/frontend/src/translations/locales/zh-TW.json b/frontend/src/translations/locales/zh-TW.json index 3e131c06a..fa3a35202 100644 --- a/frontend/src/translations/locales/zh-TW.json +++ b/frontend/src/translations/locales/zh-TW.json @@ -136,7 +136,13 @@ "maxPostsReached": "您已達到版主設置的最大帖子數。", "iAmDone": "我受夠了!", "iAmNotDoneYet": "我還沒搞定...", - "userIsReady": "{{user}} 準備好了!" + "userIsReady": "{{user}} 準備好了!", + "deleteConfirmation": { + "title": "", + "description": "", + "confirm": "", + "cancel": "" + } }, "GameMenu": { "board": "木板", diff --git a/frontend/src/views/game/board/post/Post.tsx b/frontend/src/views/game/board/post/Post.tsx index c55026743..f3858757b 100644 --- a/frontend/src/views/game/board/post/Post.tsx +++ b/frontend/src/views/game/board/post/Post.tsx @@ -38,6 +38,7 @@ import useCrypto from '../../../../crypto/useCrypto'; import { getLorem } from './lorem'; import useCanDecrypt from '../../../../crypto/useCanDecrypt'; import isSearchMatch from '../../is-search-match'; +import { useConfirm } from 'material-ui-confirm'; interface PostItemProps { index: number; @@ -104,6 +105,8 @@ const PostItem = ({ const postElement = useRef(null); const [actionsToggled, toggleAction] = useToggle(false); const [showGiphyEditor, setShowGiphyEditor] = useState(false); + const confirm = useConfirm(); + const upVotes = useMemo(() => countVotes(post, 'like'), [post]); const downVotes = useMemo(() => countVotes(post, 'dislike'), [post]); const upVoters = useMemo(() => enumerateVotes(post, 'like'), [post]); @@ -137,6 +140,23 @@ const PostItem = ({ }, [onEditAction, encrypt] ); + const handleDelete = useCallback(() => { + const buttonProps = { + color: 'error', + variant: 'contained', + 'data-cy': 'delete-post-confirm', + }; + confirm({ + title: t('PostBoard.deleteConfirmation.title'), + description: t('PostBoard.deleteConfirmation.description'), + confirmationText: t('PostBoard.deleteConfirmation.confirm'), + cancellationText: t('PostBoard.deleteConfirmation.cancel'), + confirmationButtonProps: buttonProps as any, + }).then(() => { + onDelete(); + trackEvent('game/post/delete'); + }); + }, [onDelete, confirm, t]); const actualContent = useMemo(() => { return isBlurred ? generateLoremIpsum(post.content) : decrypt(post.content); @@ -282,7 +302,7 @@ const PostItem = ({ }} /> } - onClick={onDelete} + onClick={handleDelete} /> )} diff --git a/frontend/src/views/game/board/post/__tests__/Post.test.tsx b/frontend/src/views/game/board/post/__tests__/Post.test.tsx index d26c543e3..0d501e57f 100644 --- a/frontend/src/views/game/board/post/__tests__/Post.test.tsx +++ b/frontend/src/views/game/board/post/__tests__/Post.test.tsx @@ -71,6 +71,7 @@ describe('Post', () => { onEdit={noop} onLike={noop} onEditAction={noop} + onCancelVotes={noop} color="#123456" search="" /> @@ -93,6 +94,7 @@ describe('Post', () => { onEdit={noop} onLike={likeHandler} onEditAction={noop} + onCancelVotes={noop} color="#123456" search="" /> @@ -118,48 +120,6 @@ describe('Post', () => { expect(deleteHandler).not.toHaveBeenCalled(); }); - it('Should let the user delete the post if the author is the user, but not like or dislike', () => { - const customPost: Post = { - ...post, - user: u('John Doe'), - }; - const deleteHandler = jest.fn(); - const likeHandler = jest.fn(); - const dislikeHandler = jest.fn(); - - const { getByLabelText } = renderWithRouter( - - ); - const deleteButton = getByLabelText(/delete/i, { selector: 'button' }); - const likeButton = getByLabelText(/^like/i); - const dislikeButton = getByLabelText(/dislike/i); - act(() => { - deleteButton.click(); - }); - - expect(deleteHandler).toHaveBeenCalledTimes(1); - - expect(likeButton).toBeDisabled(); - expect(dislikeButton).toBeDisabled(); - act(() => { - likeButton.click(); - dislikeButton.click(); - }); - expect(likeHandler).not.toHaveBeenCalled(); - expect(dislikeHandler).not.toHaveBeenCalled(); - }); - it('Should let the user edit the post if the author is the user', () => { const customPost: Post = { ...post, @@ -177,6 +137,7 @@ describe('Post', () => { onEdit={editHandler} onEditAction={noop} onLike={noop} + onCancelVotes={noop} color="#123456" search="" /> @@ -211,6 +172,7 @@ describe('Post', () => { onEdit={editHandler} onEditAction={noop} onLike={noop} + onCancelVotes={noop} color="#123456" search="" />