diff --git a/src/web/common/components/ContextMenu/ChangeEdgeTypeMenuItem.tsx b/src/web/common/components/ContextMenu/ChangeEdgeTypeMenuItem.tsx index e1365092..baa38fa3 100644 --- a/src/web/common/components/ContextMenu/ChangeEdgeTypeMenuItem.tsx +++ b/src/web/common/components/ContextMenu/ChangeEdgeTypeMenuItem.tsx @@ -5,6 +5,7 @@ import { getSameCategoryEdgeTypes } from "@/common/edge"; import { ContextMenuItem } from "@/web/common/components/ContextMenu/CloseOnClickMenuItem"; import { useSessionUser } from "@/web/common/hooks"; import { changeEdgeType } from "@/web/topic/store/actions"; +import { useIsTableEdge } from "@/web/topic/store/edgeHooks"; import { useUserCanEditTopicData } from "@/web/topic/store/userHooks"; import { Edge } from "@/web/topic/utils/graph"; @@ -17,7 +18,11 @@ export const ChangeEdgeTypeMenuItem = ({ edge, parentMenuOpen }: Props) => { const { sessionUser } = useSessionUser(); const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username); + const isTableEdge = useIsTableEdge(edge.id); + if (!userCanEditTopicData) return <>; + // don't allow modifying edges that are part of the table, because they should always exist as long as their nodes do + if (isTableEdge) return <>; return ( <> diff --git a/src/web/common/components/ContextMenu/DeleteEdgeMenuItem.tsx b/src/web/common/components/ContextMenu/DeleteEdgeMenuItem.tsx index a237b37b..c9d387d3 100644 --- a/src/web/common/components/ContextMenu/DeleteEdgeMenuItem.tsx +++ b/src/web/common/components/ContextMenu/DeleteEdgeMenuItem.tsx @@ -2,6 +2,7 @@ import { justificationRelationNames } from "@/common/edge"; import { ContextMenuItem } from "@/web/common/components/ContextMenu/CloseOnClickMenuItem"; import { useSessionUser } from "@/web/common/hooks"; import { deleteEdge } from "@/web/topic/store/createDeleteActions"; +import { useIsTableEdge } from "@/web/topic/store/edgeHooks"; import { useUserCanEditTopicData } from "@/web/topic/store/userHooks"; import { Edge } from "@/web/topic/utils/graph"; @@ -9,9 +10,13 @@ export const DeleteEdgeMenuItem = ({ edge }: { edge: Edge }) => { const { sessionUser } = useSessionUser(); const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username); + const isTableEdge = useIsTableEdge(edge.id); + if (!userCanEditTopicData) return <>; // doesn't make sense to delete justification edges because they're a tree not a graph - just delete the nodes if (justificationRelationNames.includes(edge.label)) return <>; + // don't allow modifying edges that are part of the table, because they should always exist as long as their nodes do + if (isTableEdge) return <>; return deleteEdge(edge.id)}>Delete edge; }; diff --git a/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx b/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx index 406b4761..9889967b 100644 --- a/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx +++ b/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx @@ -19,6 +19,7 @@ import { useSessionUser } from "@/web/common/hooks"; import { HelpMenu } from "@/web/topic/components/TopicWorkspace/HelpMenu"; import { MoreActionsDrawer } from "@/web/topic/components/TopicWorkspace/MoreActionsDrawer"; import { deleteGraphPart } from "@/web/topic/store/createDeleteActions"; +import { useIsTableEdge } from "@/web/topic/store/edgeHooks"; import { useOnPlayground } from "@/web/topic/store/topicHooks"; import { useUserCanEditTopicData } from "@/web/topic/store/userHooks"; import { redo, undo } from "@/web/topic/store/utilActions"; @@ -44,15 +45,18 @@ import { export const WorkspaceToolbar = () => { const { sessionUser } = useSessionUser(); const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username); + const onPlayground = useOnPlayground(); const [canUndo, canRedo] = useTemporalHooks(); const [canGoBack, canGoForward] = useCanGoBackForward(); + const isComparingPerspectives = useIsComparingPerspectives(); const flashlightMode = useFlashlightMode(); const readonlyMode = useReadonlyMode(); const [hasErrored, setHasErrored] = useState(false); const selectedGraphPart = useSelectedGraphPart(); + const partIsTableEdge = useIsTableEdge(selectedGraphPart?.id ?? ""); const [isMoreActionsDrawerOpen, setIsMoreActionsDrawerOpen] = useState(false); const [helpAnchorEl, setHelpAnchorEl] = useState(null); @@ -124,7 +128,8 @@ export const WorkspaceToolbar = () => { deleteGraphPart(selectedGraphPart); } }} - disabled={!selectedGraphPart} + // don't allow modifying edges that are part of the table, because they should always exist as long as their nodes do + disabled={!selectedGraphPart || partIsTableEdge} className="hidden sm:flex" > diff --git a/src/web/topic/store/edgeHooks.ts b/src/web/topic/store/edgeHooks.ts index 0511472b..2fd886c8 100644 --- a/src/web/topic/store/edgeHooks.ts +++ b/src/web/topic/store/edgeHooks.ts @@ -17,3 +17,17 @@ export const useIsNodeSelected = (edgeId: string) => { return useIsAnyGraphPartSelected(neighborNodes.map((node) => node.id)); }; + +export const useIsTableEdge = (edgeId: string) => { + return useTopicStore((state) => { + try { + const edge = findEdgeOrThrow(edgeId, state.edges); + if (edge.label !== "fulfills") return false; + + const [parentNode, childNode] = nodes(edge, state.nodes); + return parentNode.type === "criterion" && childNode.type === "solution"; + } catch { + return false; + } + }); +}; diff --git a/src/web/topic/utils/edge.ts b/src/web/topic/utils/edge.ts index 340e8f5f..9b080b27 100644 --- a/src/web/topic/utils/edge.ts +++ b/src/web/topic/utils/edge.ts @@ -246,7 +246,7 @@ export const childNode = (edge: Edge, nodes: Node[]) => { return findNodeOrThrow(edge.target, nodes); }; -export const nodes = (edge: Edge, nodes: Node[]) => { +export const nodes = (edge: Edge, nodes: Node[]): [Node, Node] => { return [parentNode(edge, nodes), childNode(edge, nodes)]; };