Skip to content

Commit

Permalink
[MergeDups] Enable protection override
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec committed Nov 13, 2024
1 parent 7f23e66 commit 167527b
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export default function DragSense(props: DragSenseProps): ReactElement {
arraysEqual<string>
);
const dispatch = useAppDispatch();
const overrideProtection = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.overrideProtection
);
const sidebar = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.tree.sidebar
);
Expand Down Expand Up @@ -95,7 +98,7 @@ export default function DragSense(props: DragSenseProps): ReactElement {
isSenseProtected: props.isProtectedSense,
})}
index={props.index}
isDragDisabled={props.isOnlySenseInProtectedWord}
isDragDisabled={props.isOnlySenseInProtectedWord && !overrideProtection}
>
{(provided, snapshot): ReactElement => (
<Card
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ export default function SidebarDragSense(
const ref: MergeTreeReference = { wordId, mergeSenseId, order };
return JSON.stringify(ref);
});
const overrideProtection = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.overrideProtection
);

return (
<Draggable
key={props.mergeSense.sense.guid}
draggableId={draggableId}
index={props.index}
isDragDisabled={props.mergeSense.protected}
isDragDisabled={props.mergeSense.protected && !overrideProtection}
>
{(provided, snapshot): ReactElement => (
<div
Expand Down
20 changes: 14 additions & 6 deletions src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,46 @@ export const trashId = "trash-drop";

export default function MergeDragDrop(): ReactElement {
const dispatch = useAppDispatch();
const overrideProtection = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.overrideProtection
);
const sidebarOpen = useAppSelector(
(state: StoreState) =>
state.mergeDuplicateGoal.tree.sidebar.mergeSenses.length > 1
);
const sidebarProtected = useAppSelector((state: StoreState) => {
const ms = state.mergeDuplicateGoal.tree.sidebar.mergeSenses;
return ms.length && ms[0].protected;
const goal = state.mergeDuplicateGoal;
const ms = goal.tree.sidebar.mergeSenses;
return ms.length && ms[0].protected && !goal.overrideProtection;
});
const words = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.tree.words
);

const [senseToDelete, setSenseToDelete] = useState<string>("");
const [senseToDelete, setSenseToDelete] = useState("");
const { t } = useTranslation();

function handleDrop(res: DropResult): void {
const src: MergeTreeReference = JSON.parse(res.draggableId);
const srcWordId = res.source.droppableId;
const srcWord = words[srcWordId];
if (srcWord?.protected && Object.keys(srcWord.sensesGuids).length === 1) {
if (
srcWord?.protected &&
!overrideProtection &&
Object.keys(srcWord.sensesGuids).length === 1
) {
// Case 0: The final sense of a protected word cannot be moved.
return;
} else if (res.destination?.droppableId === trashId) {
// Case 1: The sense was dropped on the trash icon.
if (src.isSenseProtected) {
if (src.isSenseProtected && !overrideProtection) {
// Case 1a: Cannot delete a protected sense.
return;
}
setSenseToDelete(res.draggableId);
} else if (res.combine) {
// Case 2: the sense was dropped on another sense.
if (src.isSenseProtected) {
if (src.isSenseProtected && !overrideProtection) {
// Case 2a: Cannot merge a protected sense into another sense.
if (srcWordId !== res.combine.droppableId) {
// The target sense is in a different word, so move instead of combine.
Expand Down
24 changes: 22 additions & 2 deletions src/goals/MergeDuplicates/MergeDupsStep/SaveDeferButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Grid } from "@mui/material";
import { Checkbox, FormControlLabel, Grid } from "@mui/material";
import { ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";

Expand All @@ -7,14 +7,23 @@ import {
deferMerge,
mergeAll,
setSidebar,
toggleOverrideProtection,
} from "goals/MergeDuplicates/Redux/MergeDupsActions";
import { asyncAdvanceStep } from "goals/Redux/GoalActions";
import { useAppDispatch } from "rootRedux/hooks";
import { useAppDispatch, useAppSelector } from "rootRedux/hooks";
import { StoreState } from "rootRedux/types";
import theme from "types/theme";

export default function SaveDeferButtons(): ReactElement {
const dispatch = useAppDispatch();

const hasProtected = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.hasProtected
);
const overrideProtection = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.overrideProtection
);

const [isDeferring, setIsDeferring] = useState(false);
const [isSaving, setIsSaving] = useState(false);

Expand Down Expand Up @@ -64,6 +73,17 @@ export default function SaveDeferButtons(): ReactElement {
>
{t("buttons.defer")}
</LoadingButton>
{hasProtected && (
<FormControlLabel
control={
<Checkbox
checked={overrideProtection}
onChange={() => dispatch(toggleOverrideProtection())}
/>
}
label={"Allow deletion of protected words or senses?"}
/>
)}
</Grid>
</Grid>
);
Expand Down
5 changes: 5 additions & 0 deletions src/goals/MergeDuplicates/Redux/MergeDupsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
setDataAction,
setSidebarAction,
setVernacularAction,
toggleOverrideProtectionAction,
} from "goals/MergeDuplicates/Redux/MergeDupsReducer";
import {
CombineSenseMergePayload,
Expand Down Expand Up @@ -94,6 +95,10 @@ export function setVern(payload: SetVernacularPayload): PayloadAction {
return setVernacularAction(payload);
}

export function toggleOverrideProtection(): Action {
return toggleOverrideProtectionAction();
}

// Dispatch Functions

export function deferMerge() {
Expand Down
10 changes: 9 additions & 1 deletion src/goals/MergeDuplicates/Redux/MergeDupsReducer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSlice } from "@reduxjs/toolkit";
import { v4 } from "uuid";

import { type Word } from "api/models";
import { Status, type Word } from "api/models";
import {
type MergeTreeReference,
type MergeTreeSense,
Expand Down Expand Up @@ -293,8 +293,10 @@ const mergeDuplicatesSlice = createSlice({
const wordsTree: Hash<MergeTreeWord> = {};
const counts: Hash<number> = {};
action.payload.forEach((word: Word) => {
state.hasProtected ||= word.accessibility === Status.Protected;
words[word.id] = JSON.parse(JSON.stringify(word));
word.senses.forEach((s, order) => {
state.hasProtected ||= s.accessibility === Status.Protected;
senses[s.guid] = convertSenseToMergeTreeSense(s, word.id, order);
});
wordsTree[word.id] = convertWordToMergeTreeWord(word);
Expand All @@ -304,12 +306,17 @@ const mergeDuplicatesSlice = createSlice({
state.tree = { ...defaultTree, words: wordsTree };
state.audio = { ...defaultAudio, counts };
state.mergeWords = [];
state.overrideProtection = false;
}
},

setVernacularAction: (state, action) => {
state.tree.words[action.payload.wordId].vern = action.payload.vern;
},

toggleOverrideProtectionAction: (state) => {
state.overrideProtection = !state.overrideProtection;
},
},
extraReducers: (builder) =>
builder.addCase(StoreActionTypes.RESET, () => defaultState),
Expand All @@ -329,6 +336,7 @@ export const {
setDataAction,
setSidebarAction,
setVernacularAction,
toggleOverrideProtectionAction,
} = mergeDuplicatesSlice.actions;

export default mergeDuplicatesSlice.reducer;
4 changes: 4 additions & 0 deletions src/goals/MergeDuplicates/Redux/MergeDupsReduxTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,18 @@ export interface MergeTreeState {
data: MergeData;
tree: MergeTree;
audio: MergeAudio;
hasProtected: boolean;
mergeWords: MergeWords[];
overrideProtection: boolean;
}

export const defaultState: MergeTreeState = {
data: defaultData,
tree: defaultTree,
audio: defaultAudio,
hasProtected: false,
mergeWords: [],
overrideProtection: false,
};

// Action payloads
Expand Down

0 comments on commit 167527b

Please sign in to comment.