From e28e45233c85e0dc0feefb3dc5f08f1cfbc86ca7 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Wed, 25 Oct 2023 18:16:17 -0400 Subject: [PATCH] Create a common undo button --- src/components/Buttons/UndoButton.tsx | 61 +++++++++++++ src/components/Buttons/index.ts | 2 + .../MergeDuplicates/MergeDupsCompleted.tsx | 79 ++++------------- .../ReviewEntries/ReviewEntriesCompleted.tsx | 85 +++++-------------- 4 files changed, 99 insertions(+), 128 deletions(-) create mode 100644 src/components/Buttons/UndoButton.tsx diff --git a/src/components/Buttons/UndoButton.tsx b/src/components/Buttons/UndoButton.tsx new file mode 100644 index 0000000000..f5b3c2cff6 --- /dev/null +++ b/src/components/Buttons/UndoButton.tsx @@ -0,0 +1,61 @@ +import { Button, Grid } from "@mui/material"; +import { ReactElement, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { CancelConfirmDialog } from "components/Dialogs"; + +interface UndoButtonProps { + buttonIdEnabled?: string; + buttonIdCancel?: string; + buttonIdConfirm?: string; + textIdDialog: string; + textIdDisabled: string; + textIdEnabled: string; + isUndoAllowed: () => Promise; + undo: () => Promise; +} + +export default function UndoButton(props: UndoButtonProps): ReactElement { + const isUndoAllowed = props.isUndoAllowed; + + const [isUndoEnabled, setUndoEnabled] = useState(false); + const [undoDialogOpen, setUndoDialogOpen] = useState(false); + + const { t } = useTranslation(); + + useEffect(() => { + if (!undoDialogOpen) { + isUndoAllowed().then(setUndoEnabled); + } + }, [isUndoAllowed, undoDialogOpen]); + + return ( + + {isUndoEnabled ? ( +
+ + setUndoDialogOpen(false)} + handleConfirm={() => + props.undo().then(() => setUndoDialogOpen(false)) + } + buttonIdCancel={props.buttonIdCancel} + buttonIdConfirm={props.buttonIdConfirm} + /> +
+ ) : ( +
+ +
+ )} +
+ ); +} diff --git a/src/components/Buttons/index.ts b/src/components/Buttons/index.ts index a5135428ee..3889259e5f 100644 --- a/src/components/Buttons/index.ts +++ b/src/components/Buttons/index.ts @@ -4,6 +4,7 @@ import IconButtonWithTooltip from "components/Buttons/IconButtonWithTooltip"; import LoadingButton from "components/Buttons/LoadingButton"; import LoadingDoneButton from "components/Buttons/LoadingDoneButton"; import PartOfSpeechButton from "components/Buttons/PartOfSpeechButton"; +import UndoButton from "components/Buttons/UndoButton"; export { FileInputButton, @@ -12,4 +13,5 @@ export { LoadingButton, LoadingDoneButton, PartOfSpeechButton, + UndoButton, }; diff --git a/src/goals/MergeDuplicates/MergeDupsCompleted.tsx b/src/goals/MergeDuplicates/MergeDupsCompleted.tsx index c078e1b4bd..0f9dfbf308 100644 --- a/src/goals/MergeDuplicates/MergeDupsCompleted.tsx +++ b/src/goals/MergeDuplicates/MergeDupsCompleted.tsx @@ -1,13 +1,12 @@ import { ArrowRightAlt } from "@mui/icons-material"; -import { Button, Card, Grid, Paper, Typography } from "@mui/material"; +import { Card, Grid, Paper, Typography } from "@mui/material"; import { ReactElement, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; import { Flag, MergeUndoIds, Sense, Word } from "api/models"; import { getFrontierWords, getWord, undoMerge } from "backend"; -import { FlagButton } from "components/Buttons"; -import { CancelConfirmDialog } from "components/Dialogs"; +import { FlagButton, UndoButton } from "components/Buttons"; import SenseCardContent from "goals/MergeDuplicates/MergeDupsStep/SenseCardContent"; import { MergesCompleted } from "goals/MergeDuplicates/MergeDupsTypes"; import { StoreState } from "types"; @@ -45,6 +44,9 @@ export function MergesCount(changes: MergesCompleted): ReactElement { } function MergeChange(change: MergeUndoIds): ReactElement { + const handleIsUndoAllowed = (): Promise => + getFrontierWords().then((words) => doWordsIncludeMerges(words, change)); + return (
)} { + await undoMerge(change); + }} />
); } -interface UndoButtonProps { - merge: MergeUndoIds; - textId: string; - dialogId: string; - disabledId: string; -} - -function UndoButton(props: UndoButtonProps): ReactElement { - const [isUndoBtnEnabled, setUndoBtnEnabled] = useState(false); - const [undoDialogOpen, setUndoDialogOpen] = useState(false); - const { t } = useTranslation(); - - useEffect(() => { - function checkFrontier(): void { - getFrontierWords().then((words) => - setUndoBtnEnabled( - props.merge ? doWordsIncludeMerges(words, props.merge) : false - ) - ); - } - checkFrontier(); - }); - - if (isUndoBtnEnabled) { - return ( - -
- - setUndoDialogOpen(false)} - handleConfirm={() => - undoMerge(props.merge).then(() => setUndoDialogOpen(false)) - } - buttonIdCancel="merge-undo-cancel" - buttonIdConfirm="merge-undo-confirm" - /> -
-
- ); - } - return ( - -
- -
-
- ); -} - export function doWordsIncludeMerges( words: Word[], merge: MergeUndoIds diff --git a/src/goals/ReviewEntries/ReviewEntriesCompleted.tsx b/src/goals/ReviewEntries/ReviewEntriesCompleted.tsx index d612c0d1fe..73c550e79c 100644 --- a/src/goals/ReviewEntries/ReviewEntriesCompleted.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesCompleted.tsx @@ -1,11 +1,11 @@ import { ArrowRightAlt } from "@mui/icons-material"; -import { Button, Grid, Typography } from "@mui/material"; -import { ReactElement, useEffect, useState } from "react"; +import { Grid, Typography } from "@mui/material"; +import { ReactElement } from "react"; import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; import { getFrontierWords, getWord, updateWord } from "backend"; -import { CancelConfirmDialog } from "components/Dialogs"; +import { UndoButton } from "components/Buttons"; import { EntriesEdited, EntryEdit, @@ -41,7 +41,17 @@ export function EditsCount(changes: EntriesEdited): ReactElement { ); } +async function undoEdit(edit: EntryEdit): Promise { + const oldWord = await getWord(edit.oldId); + await updateWord({ ...oldWord, id: edit.newId }); +} + function EditedEntry(props: { edit: EntryEdit }): ReactElement { + const handleIsUndoAllowed = (): Promise => + getFrontierWords().then( + (words) => words.findIndex((w) => w.id === props.edit.newId) !== -1 + ); + return ( {props.edit.oldId} @@ -58,68 +68,15 @@ function EditedEntry(props: { edit: EntryEdit }): ReactElement { {props.edit.newId} undoEdit(props.edit)} /> ); } - -interface UndoButtonProps { - edit: EntryEdit; - textId: string; - dialogId: string; - disabledId: string; -} - -function UndoButton(props: UndoButtonProps): ReactElement { - const [isUndoEnabled, setUndoEnabled] = useState(false); - const [undoDialogOpen, setUndoDialogOpen] = useState(false); - const { t } = useTranslation(); - - useEffect(() => { - function checkFrontier(): void { - getFrontierWords().then((words) => - setUndoEnabled(words.findIndex((w) => w.id === props.edit.newId) !== -1) - ); - } - checkFrontier(); - }); - - return isUndoEnabled ? ( - -
- - setUndoDialogOpen(false)} - handleConfirm={() => - undoEdit(props.edit).then(() => setUndoDialogOpen(false)) - } - buttonIdCancel="edit-undo-cancel" - buttonIdConfirm="edit-undo-confirm" - /> -
-
- ) : ( - -
- -
-
- ); -} - -const undoEdit = async (edit: EntryEdit): Promise => { - const oldWord = await getWord(edit.oldId); - await updateWord({ ...oldWord, id: edit.newId }); -};