Skip to content

Commit

Permalink
Drop currentModId as a concept (#9490)
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamlangford authored Nov 15, 2024
1 parent 63a5f4a commit 55db8d3
Show file tree
Hide file tree
Showing 22 changed files with 116 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ test("can save a new trigger mod", async ({

test("#9349: can save new mod with multiple components", async ({
page,
extensionId,
newPageEditorPage,
}) => {
await page.goto("/");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
type SelectStringOption,
type SetActiveField,
} from "@/components/formBuilder/formBuilderTypes";
import FieldEditor from "./fieldEditor/FieldEditor";
import {
moveStringInArray,
getNormalizedUiOrder,
Expand All @@ -13,6 +12,7 @@ import FieldTemplate from "@/components/form/FieldTemplate";
import LayoutWidget from "@/components/LayoutWidget";
import { findLast } from "lodash";
import { type FormikErrors } from "formik";
import FieldEditor from "@/components/formBuilder/edit/fieldEditor/FieldEditor";

export const ActiveField: React.FC<{
name: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ function useCreateModFromUnsavedMod(): UseCreateModFromUnsavedModReturn {
if (activeModId === unsavedModId) {
// If the mod list item is selected, reselect the mod item using the new id
dispatch(editorActions.setActiveModId(newModId));
// Preserve the activeModComponentId if there is one
if (activeModComponentFormState?.uuid) {
dispatch(
editorActions.setActiveModComponentId(
activeModComponentFormState.uuid,
),
);
}
} else if (
activeModComponentFormState?.modMetadata.id === unsavedModId
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { actions } from "@/pageEditor/store/editor/editorSlice";
import { modComponentToFormState } from "@/pageEditor/starterBricks/adapter";
import {
selectCurrentModId,
selectActiveModId,
selectGetUntouchedActivatedModComponentsForMod,
} from "@/pageEditor/store/editor/editorSelectors";
import { useDispatch, useSelector } from "react-redux";
Expand All @@ -30,13 +30,13 @@ import { useEffect } from "react";
*/
function useEnsureFormStates(): void {
const dispatch = useDispatch();
const currentModId = useSelector(selectCurrentModId);
const activeModId = useSelector(selectActiveModId);
const getUntouchedActivatedModComponentsForMod = useSelector(
selectGetUntouchedActivatedModComponentsForMod,
);

const untouchedModComponents = currentModId
? getUntouchedActivatedModComponentsForMod(currentModId)
const untouchedModComponents = activeModId
? getUntouchedActivatedModComponentsForMod(activeModId)
: null;

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { useDispatch, useSelector } from "react-redux";
import {
selectActiveModComponentFormState,
selectActiveModComponentId,
selectCurrentModId,
selectActiveModId,
selectEditorUpdateKey,
selectGetModDraftStateForModId,
} from "@/pageEditor/store/editor/editorSelectors";
Expand Down Expand Up @@ -113,7 +113,7 @@ function updateDraftModInstance() {
const state = getState();

const activeModComponentId = selectActiveModComponentId(state);
const modId = selectCurrentModId(state);
const modId = selectActiveModId(state);

if (!modId) {
// Skip if the modId has somehow become null before the microtask for this async method got scheduled
Expand Down Expand Up @@ -175,7 +175,7 @@ function updateDraftModInstance() {
*/
function useRegisterDraftModInstanceOnAllFrames(): void {
const dispatch = useDispatch<AppDispatch>();
const modId = useSelector(selectCurrentModId);
const modId = useSelector(selectActiveModId);
const editorUpdateKey = useSelector(selectEditorUpdateKey);

assertNotNullish(modId, "modId is required");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
import { useDispatch, useSelector } from "react-redux";
import cx from "classnames";
import {
selectActiveModComponentFormState,
selectActiveModComponentId,
selectActiveModId,
selectDirtyMetadataForModId,
selectExpandedModId,
Expand All @@ -47,24 +47,19 @@ const ModListItem: React.FC<
const dispatch = useDispatch();
const activeModId = useSelector(selectActiveModId);
const expandedModId = useSelector(selectExpandedModId);
const activeModComponentFormState = useSelector(
selectActiveModComponentFormState,
);
const activeModComponentId = useSelector(selectActiveModComponentId);

const { id: modId, name: savedName, version: activatedVersion } = modMetadata;

const isActive = activeModId === modId;
const isModComponentSelected = activeModComponentId != null;
const isModSelected = activeModId === modId && !isModComponentSelected;
const isExpanded = expandedModId === modId;

// TODO: Fix this so it pulls from registry, after registry single-item-api-fetch is implemented
// (See: https://github.com/pixiebrix/pixiebrix-extension/issues/7184)
const { data: modDefinition } = useGetModDefinitionQuery({ modId });
const latestModVersion = modDefinition?.metadata?.version;

// Set the alternate background if a mod component in this mod is active
const hasModBackground =
activeModComponentFormState?.modMetadata.id === modId;

const dirtyName = useSelector(selectDirtyMetadataForModId(modId))?.name;
const name = dirtyName ?? savedName ?? "Loading...";

Expand All @@ -81,16 +76,19 @@ const ModListItem: React.FC<
eventKey={modId}
as={ListGroup.Item}
className={cx(styles.root, "list-group-item-action", {
[styles.modBackground ?? ""]: hasModBackground,
// Set the alternate background if a mod component in this mod is active
[styles.modBackground ?? ""]: isModSelected || isModComponentSelected,
})}
tabIndex={0} // Avoid using `button` because this item includes more buttons #2343
active={isActive}
key={`mod-${modId}`}
active={isModSelected}
key={modId}
onClick={() => {
dispatch(actions.setActiveModId(modId));
// Collapse if the user clicks the mod item when it's already active/selected in the listing pane
dispatch(
actions.setExpandedModId(isExpanded && isActive ? null : modId),
actions.setExpandedModId(
isExpanded && isModSelected ? null : modId,
),
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,25 +77,33 @@ const ModSidebarListItems: React.FunctionComponent = () => {
],
);

const listItems = filteredSidebarItems.map((sidebarItem) => {
const { modMetadata, modComponents } = sidebarItem;
const listItems = useMemo(
() =>
filteredSidebarItems.map((sidebarItem) => {
const { modMetadata, modComponents } = sidebarItem;

return (
<ModListItem key={modMetadata.id} modMetadata={modMetadata}>
{modComponents.map((modComponentSidebarItem) => (
<ModComponentListItem
key={getDraftModComponentId(modComponentSidebarItem)}
modComponentSidebarItem={modComponentSidebarItem}
availableActivatedModComponentIds={
availableActivatedModComponentIds
}
availableDraftModComponentIds={availableDraftModComponentIds}
isNested
/>
))}
</ModListItem>
);
});
return (
<ModListItem key={modMetadata.id} modMetadata={modMetadata}>
{modComponents.map((modComponentSidebarItem) => (
<ModComponentListItem
key={getDraftModComponentId(modComponentSidebarItem)}
modComponentSidebarItem={modComponentSidebarItem}
availableActivatedModComponentIds={
availableActivatedModComponentIds
}
availableDraftModComponentIds={availableDraftModComponentIds}
isNested
/>
))}
</ModListItem>
);
}),
[
availableActivatedModComponentIds,
availableDraftModComponentIds,
filteredSidebarItems,
],
);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useDispatch, useSelector } from "react-redux";
import {
getModalDataSelector,
selectActiveModComponentFormState,
selectCurrentModId,
selectActiveModId,
selectEditorModalVisibilities,
selectModMetadataMap,
} from "@/pageEditor/store/editor/editorSelectors";
Expand Down Expand Up @@ -138,8 +138,8 @@ const CreateModModalBody: React.FC<{ onHide: () => void }> = ({ onHide }) => {
const dispatch = useDispatch();
const isMounted = useIsMounted();

const currentModId = useSelector(selectCurrentModId);
assertNotNullish(currentModId, "Expected mod or mod component to be active");
const activeModId = useSelector(selectActiveModId);
assertNotNullish(activeModId, "Expected mod or mod component to be active");

const activeModComponentFormState = useSelector(
selectActiveModComponentFormState,
Expand All @@ -156,11 +156,11 @@ const CreateModModalBody: React.FC<{ onHide: () => void }> = ({ onHide }) => {
const { createModFromComponent } = useCreateModFromModComponent();

const { data: modDefinition, isFetching: isModDefinitionFetching } =
useOptionalModDefinition(currentModId);
useOptionalModDefinition(activeModId);

const formSchema = useFormSchema();

const initialFormState = useInitialFormState(currentModId);
const initialFormState = useInitialFormState(activeModId);

const onSubmit: OnSubmit<ModMetadataFormState> = async (values, helpers) => {
if (isModDefinitionFetching) {
Expand All @@ -185,10 +185,10 @@ const CreateModModalBody: React.FC<{ onHide: () => void }> = ({ onHide }) => {
) {
// Handle "Save As" case where the mod is unsaved or the user no longer has access to the mod definition
assertNotNullish(
currentModId,
activeModId,
"Expected mod to be selected in the editor",
);
await createModFromUnsavedMod(currentModId, values);
await createModFromUnsavedMod(activeModId, values);
} else {
await createModFromMod(modDefinition, values, modalData);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useDispatch, useSelector } from "react-redux";
import { actions } from "@/pageEditor/store/editor/editorSlice";
import { Button, Modal } from "react-bootstrap";
import {
selectCurrentModId,
selectActiveModId,
selectEditorModalVisibilities,
} from "@/pageEditor/store/editor/editorSelectors";
import { useOptionalModDefinition } from "@/modDefinitions/modDefinitionHooks";
Expand All @@ -32,7 +32,7 @@ const SaveAsNewModModal: React.FC = () => {
selectEditorModalVisibilities,
);

const modId = useSelector(selectCurrentModId);
const modId = useSelector(selectActiveModId);
const { data: mod, isFetching } = useOptionalModDefinition(modId);
const modName = mod?.metadata?.name ?? "this mod";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ function useAddBrick(): AddBrick {
brick,
compact([
"input" as OutputKey,
...Object.values(pipelineMap).map((x) => x.blockConfig.outputKey),
...Object.values(pipelineMap ?? {}).map(
(x) => x.blockConfig.outputKey,
),
]),
);
const newBrick = createNewConfiguredBrick(brick.id, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
import type { AnyAction, Dispatch, Middleware } from "@reduxjs/toolkit";
import { type EditorRootState } from "@/pageEditor/store/editor/pageEditorTypes";
import {
selectActiveModComponentId,
selectActiveModComponentFormState,
selectActiveModId,
selectAllDeletedModComponentIds,
selectCurrentModId,
selectExpandedModId,
selectModComponentFormStates,
} from "@/pageEditor/store/editor/editorSelectors";
import type { EmptyObject } from "type-fest";
import { uniqBy } from "lodash";
import { isInternalRegistryId } from "@/utils/registryUtils";

class InvariantViolationError extends Error {
override name = "InvariantViolationError";
Expand All @@ -48,17 +48,28 @@ class InvariantViolationError extends Error {
// XXX: in production, should we be attempting to auto-fix these invariants?
export function assertEditorInvariants(state: EditorRootState): void {
// Assert that a mod and mod component item cannot be selected at the same time
if (selectActiveModId(state) && selectActiveModComponentId(state)) {
const activeModId = selectActiveModId(state);
const activeModComponent = selectActiveModComponentFormState(state);

if (
activeModId &&
activeModComponent &&
activeModId !== activeModComponent?.modMetadata.id &&
// When saving, the activeModId and activeModComponent.modMetadata.id aren't updated at the same time.
!isInternalRegistryId(activeModId)
) {
// Should we dispatch(actions.setActiveModComponentId(null))
// Would need to change the behavior of the action to handle null
throw new InvariantViolationError(
"activeModId and activeModComponentId are both set",
"activeModComponent is not a part of the activeMod",
);
}

// Assert that the expanded mod must correspond to the selected mod or mod component
const expandedModId = selectExpandedModId(state);
if (expandedModId && selectCurrentModId(state) !== expandedModId) {
if (expandedModId && activeModId !== expandedModId) {
throw new InvariantViolationError(
"expandedModId does not match active mod/mod component",
"expandedModId does not match active mod",
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { DataPanelTabKey } from "@/pageEditor/tabs/editTab/dataPanel/dataPa
import { createSelector } from "@reduxjs/toolkit";
import type { DataPanelTabUIState } from "@/pageEditor/store/editor/uiStateTypes";
import { selectActiveBrickConfigurationUIState } from "@/pageEditor/store/editor/editorSelectors/editorPipelineSelectors";
import { selectCurrentModId } from "@/pageEditor/store/editor/editorSelectors/editorModSelectors";
import { selectActiveModId } from "@/pageEditor/store/editor/editorSelectors/editorNavigationSelectors";

export const selectIsDataPanelExpanded = ({ editor }: EditorRootState) =>
editor.isDataPanelExpanded;
Expand Down Expand Up @@ -55,9 +55,9 @@ export function selectNodeDataPanelTabState(
*/
export const selectCurrentFindInModQuery = createSelector(
({ editor }: EditorRootState) => editor.findInModQueryByModId,
selectCurrentModId,
selectActiveModId,
(findInModQueryByModId, modId) => {
assertNotNullish(modId, "Expected currentModId");
assertNotNullish(modId, "Expected activeModId");
// eslint-disable-next-line security/detect-object-injection -- registry id
return findInModQueryByModId[modId] ?? { query: "" };
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@ import { selectModInstances } from "@/store/modComponents/modInstanceSelectors";
import mapModDefinitionToModMetadata from "@/modDefinitions/util/mapModDefinitionToModMetadata";
import { normalizeModOptionsDefinition } from "@/utils/modUtils";
import {
selectActiveModComponentFormState,
selectGetModComponentFormStateByModComponentId,
selectIsModComponentDirtyById,
selectModComponentFormStates,
selectNotDeletedActivatedModComponents,
} from "@/pageEditor/store/editor/editorSelectors/editorModComponentSelectors";
import { selectActiveModId } from "@/pageEditor/store/editor/editorSelectors/editorNavigationSelectors";
import type { ModMetadata } from "@/types/modComponentTypes";
import type { UUID } from "@/types/stringTypes";
import { assertNotNullish } from "@/utils/nullishUtils";
Expand All @@ -39,19 +37,6 @@ import {
collectModVariablesDefinition,
} from "@/store/modComponents/modComponentUtils";

/**
* Select the mod id associated with the selected mod package or mod component. Should be used if the caller doesn't
* need to know if the mod item or one of its components is selected.
* @see selectActiveModId
* @see selectExpandedModId
*/
export const selectCurrentModId = createSelector(
selectActiveModId,
selectActiveModComponentFormState,
(activeModId, activeModComponentFormState) =>
activeModId ?? activeModComponentFormState?.modMetadata.id,
);

///
/// MOD METADATA
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ export const selectActiveModComponentId = ({ editor }: EditorRootState) => {
};

/**
* Select the id of the mod being edited. NOTE: is null when editing a mod component within the mod.
* Select the id of the mod being edited.
* @see selectModId
* @see selectCurrentModId
*/
export const selectActiveModId = ({ editor }: EditorRootState) =>
editor.activeModId;
Expand Down
Loading

0 comments on commit 55db8d3

Please sign in to comment.