From 673db2588d3bf822fdc0c50a65e8d3bba84a18ee Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Fri, 8 Nov 2024 14:25:32 -0600 Subject: [PATCH 01/18] alphebetize keys --- .../store/editor/pageEditorTypes/editorStateMigrated.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts b/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts index d0f90bb38f..33d8fcb4a1 100644 --- a/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts +++ b/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts @@ -388,12 +388,12 @@ export type EditorStateMigratedV11 = Except< */ export type EditorStateMigratedV12 = Pick< EditorStateMigratedV11, + | "deletedModComponentFormStateIdsByModId" | "dirty" - | "dirtyModVariablesDefinitionById" - | "isDimensionsWarningDismissed" | "dirtyModMetadataById" | "dirtyModOptionsArgsById" - | "modComponentFormStates" - | "deletedModComponentFormStateIdsByModId" | "dirtyModOptionsDefinitionById" + | "dirtyModVariablesDefinitionById" + | "isDimensionsWarningDismissed" + | "modComponentFormStates" >; From 2d02c7e7f0af1bcaf26daf2c003317f8f7193b3e Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Fri, 8 Nov 2024 15:11:18 -0600 Subject: [PATCH 02/18] remove need for currentModId --- .../useRegisterDraftModInstanceOnAllFrames.ts | 6 ++-- .../modListingPanel/ModListItem.tsx | 20 +++++-------- .../modListingPanel/modals/CreateModModal.tsx | 14 ++++----- .../modals/SaveAsNewModModal.tsx | 4 +-- .../store/editor/editorInvariantMiddleware.ts | 30 ++++++++++++++----- .../editorSelectors/editorModSelectors.ts | 15 ---------- .../editorNavigationSelectors.ts | 3 +- .../store/editor/editorSliceHelpers.ts | 5 ++-- src/store/editorStorage.test.ts | 1 + 9 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/pageEditor/hooks/useRegisterDraftModInstanceOnAllFrames.ts b/src/pageEditor/hooks/useRegisterDraftModInstanceOnAllFrames.ts index 21047bcb15..6ee735139b 100644 --- a/src/pageEditor/hooks/useRegisterDraftModInstanceOnAllFrames.ts +++ b/src/pageEditor/hooks/useRegisterDraftModInstanceOnAllFrames.ts @@ -31,7 +31,7 @@ import { useDispatch, useSelector } from "react-redux"; import { selectActiveModComponentFormState, selectActiveModComponentId, - selectCurrentModId, + selectActiveModId, selectEditorUpdateKey, selectGetModDraftStateForModId, } from "@/pageEditor/store/editor/editorSelectors"; @@ -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 @@ -175,7 +175,7 @@ function updateDraftModInstance() { */ function useRegisterDraftModInstanceOnAllFrames(): void { const dispatch = useDispatch(); - const modId = useSelector(selectCurrentModId); + const modId = useSelector(selectActiveModId); const editorUpdateKey = useSelector(selectEditorUpdateKey); assertNotNullish(modId, "modId is required"); diff --git a/src/pageEditor/modListingPanel/ModListItem.tsx b/src/pageEditor/modListingPanel/ModListItem.tsx index 458e2885ac..b86817efcf 100644 --- a/src/pageEditor/modListingPanel/ModListItem.tsx +++ b/src/pageEditor/modListingPanel/ModListItem.tsx @@ -29,7 +29,7 @@ import { import { useDispatch, useSelector } from "react-redux"; import cx from "classnames"; import { - selectActiveModComponentFormState, + selectActiveModComponentId, selectActiveModId, selectDirtyMetadataForModId, selectExpandedModId, @@ -47,13 +47,12 @@ 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 isModActive = activeModId === modId; + const isModComponentActive = activeModComponentId != null; const isExpanded = expandedModId === modId; // TODO: Fix this so it pulls from registry, after registry single-item-api-fetch is implemented @@ -61,10 +60,6 @@ const ModListItem: React.FC< 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..."; @@ -81,16 +76,17 @@ 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 ?? ""]: isModComponentActive, })} tabIndex={0} // Avoid using `button` because this item includes more buttons #2343 - active={isActive} + active={isModActive && !isModComponentActive} key={`mod-${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 && isModActive ? null : modId), ); }} > diff --git a/src/pageEditor/modListingPanel/modals/CreateModModal.tsx b/src/pageEditor/modListingPanel/modals/CreateModModal.tsx index 384bcb29b6..96b2175fd4 100644 --- a/src/pageEditor/modListingPanel/modals/CreateModModal.tsx +++ b/src/pageEditor/modListingPanel/modals/CreateModModal.tsx @@ -27,7 +27,7 @@ import { useDispatch, useSelector } from "react-redux"; import { getModalDataSelector, selectActiveModComponentFormState, - selectCurrentModId, + selectActiveModId, selectEditorModalVisibilities, selectModMetadataMap, } from "@/pageEditor/store/editor/editorSelectors"; @@ -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, @@ -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 = async (values, helpers) => { if (isModDefinitionFetching) { @@ -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); } diff --git a/src/pageEditor/modListingPanel/modals/SaveAsNewModModal.tsx b/src/pageEditor/modListingPanel/modals/SaveAsNewModModal.tsx index c81b1fbb38..0064773d9c 100644 --- a/src/pageEditor/modListingPanel/modals/SaveAsNewModModal.tsx +++ b/src/pageEditor/modListingPanel/modals/SaveAsNewModModal.tsx @@ -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"; @@ -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"; diff --git a/src/pageEditor/store/editor/editorInvariantMiddleware.ts b/src/pageEditor/store/editor/editorInvariantMiddleware.ts index e9c9c46779..c38db9c509 100644 --- a/src/pageEditor/store/editor/editorInvariantMiddleware.ts +++ b/src/pageEditor/store/editor/editorInvariantMiddleware.ts @@ -18,15 +18,16 @@ 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 { type AppDispatch } from "@/pageEditor/store/store"; +import { actions } from "@/pageEditor/store/editor/editorSlice"; class InvariantViolationError extends Error { override name = "InvariantViolationError"; @@ -46,19 +47,32 @@ class InvariantViolationError extends Error { * @see editorInvariantMiddleware */ // XXX: in production, should we be attempting to auto-fix these invariants? -export function assertEditorInvariants(state: EditorRootState): void { +export function assertEditorInvariants( + state: EditorRootState, + dispatch: AppDispatch, +): 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 activeModComponentId = selectActiveModComponentFormState(state); + + if (activeModComponentId && !activeModId) { + dispatch(actions.setActiveModId(activeModComponentId.modMetadata.id)); + } + + if ( + activeModComponentId && + activeModId !== activeModComponentId?.modMetadata.id + ) { 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 && selectActiveModId(state) !== expandedModId) { throw new InvariantViolationError( - "expandedModId does not match active mod/mod component", + "expandedModId does not match active mod", ); } @@ -97,7 +111,7 @@ const editorInvariantMiddleware: Middleware = const result = next(action); try { - assertEditorInvariants(storeAPI.getState()); + assertEditorInvariants(storeAPI.getState(), storeAPI.dispatch); } catch (error) { throw new Error(`Action violated invariant: ${action.type}`, { cause: error, diff --git a/src/pageEditor/store/editor/editorSelectors/editorModSelectors.ts b/src/pageEditor/store/editor/editorSelectors/editorModSelectors.ts index 3d56b4fffe..220316e771 100644 --- a/src/pageEditor/store/editor/editorSelectors/editorModSelectors.ts +++ b/src/pageEditor/store/editor/editorSelectors/editorModSelectors.ts @@ -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"; @@ -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 /// diff --git a/src/pageEditor/store/editor/editorSelectors/editorNavigationSelectors.ts b/src/pageEditor/store/editor/editorSelectors/editorNavigationSelectors.ts index b8e82070c3..5a7ea3c1a8 100644 --- a/src/pageEditor/store/editor/editorSelectors/editorNavigationSelectors.ts +++ b/src/pageEditor/store/editor/editorSelectors/editorNavigationSelectors.ts @@ -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; diff --git a/src/pageEditor/store/editor/editorSliceHelpers.ts b/src/pageEditor/store/editor/editorSliceHelpers.ts index e7e7ec6580..118b4596cb 100644 --- a/src/pageEditor/store/editor/editorSliceHelpers.ts +++ b/src/pageEditor/store/editor/editorSliceHelpers.ts @@ -171,9 +171,8 @@ export function setActiveModComponentId( ) { state.error = null; state.activeModComponentId = modComponentFormState.uuid; - state.activeModId = null; - state.expandedModId = - modComponentFormState.modMetadata.id ?? state.expandedModId; + state.activeModId = modComponentFormState.modMetadata.id; + state.expandedModId = modComponentFormState.modMetadata.id; state.selectionSeq++; ensureBrickPipelineUIState(state, modComponentFormState.uuid); diff --git a/src/store/editorStorage.test.ts b/src/store/editorStorage.test.ts index 3483bf2a98..dc669db9d1 100644 --- a/src/store/editorStorage.test.ts +++ b/src/store/editorStorage.test.ts @@ -91,6 +91,7 @@ describe("editorStorage", () => { "persist:editor", { ...targetEditorState, + activeModId: null, // When the mod's form states are added, they become active. On remove the active/expanded props are reset activeModComponentId: null, expandedModId: null, From 6897a141f856400d8ae32e7e12f6185f7d9c1c51 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Fri, 8 Nov 2024 16:01:03 -0600 Subject: [PATCH 03/18] further refactoring --- .../modListingPanel/ModSidebarListItems.tsx | 44 +++++++++++-------- .../store/editor/editorInvariantMiddleware.ts | 2 + 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/pageEditor/modListingPanel/ModSidebarListItems.tsx b/src/pageEditor/modListingPanel/ModSidebarListItems.tsx index 24146efa13..5538102997 100644 --- a/src/pageEditor/modListingPanel/ModSidebarListItems.tsx +++ b/src/pageEditor/modListingPanel/ModSidebarListItems.tsx @@ -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 ( - - {modComponents.map((modComponentSidebarItem) => ( - - ))} - - ); - }); + return ( + + {modComponents.map((modComponentSidebarItem) => ( + + ))} + + ); + }), + [ + availableActivatedModComponentIds, + availableDraftModComponentIds, + filteredSidebarItems, + ], + ); return ( <> diff --git a/src/pageEditor/store/editor/editorInvariantMiddleware.ts b/src/pageEditor/store/editor/editorInvariantMiddleware.ts index c38db9c509..3160619934 100644 --- a/src/pageEditor/store/editor/editorInvariantMiddleware.ts +++ b/src/pageEditor/store/editor/editorInvariantMiddleware.ts @@ -63,6 +63,8 @@ export function assertEditorInvariants( activeModComponentId && activeModId !== activeModComponentId?.modMetadata.id ) { + // Should we dispatch(actions.setActiveModComponentId(null)) + // Would need to change the behavior of the action to handle null throw new InvariantViolationError( "activeModComponent is not a part of the activeMod", ); From 0d47f6c226028192b9b475b2e4af2b470f6a05a3 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Mon, 11 Nov 2024 07:54:20 -0600 Subject: [PATCH 04/18] cleanup --- src/pageEditor/modListingPanel/ModListItem.tsx | 6 ++---- src/pageEditor/modListingPanel/ModSidebarListItems.tsx | 2 +- src/pageEditor/store/editor/editorInvariantMiddleware.ts | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pageEditor/modListingPanel/ModListItem.tsx b/src/pageEditor/modListingPanel/ModListItem.tsx index b86817efcf..3fb2d2a40d 100644 --- a/src/pageEditor/modListingPanel/ModListItem.tsx +++ b/src/pageEditor/modListingPanel/ModListItem.tsx @@ -81,13 +81,11 @@ const ModListItem: React.FC< })} tabIndex={0} // Avoid using `button` because this item includes more buttons #2343 active={isModActive && !isModComponentActive} - key={`mod-${modId}`} + 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 && isModActive ? null : modId), - ); + dispatch(actions.setExpandedModId(isExpanded ? null : modId)); }} > diff --git a/src/pageEditor/modListingPanel/ModSidebarListItems.tsx b/src/pageEditor/modListingPanel/ModSidebarListItems.tsx index 5538102997..957048387c 100644 --- a/src/pageEditor/modListingPanel/ModSidebarListItems.tsx +++ b/src/pageEditor/modListingPanel/ModSidebarListItems.tsx @@ -131,7 +131,7 @@ const ModSidebarListItems: React.FunctionComponent = () => { {/* Mod Component List */} - {listItems} + {listItems} ); diff --git a/src/pageEditor/store/editor/editorInvariantMiddleware.ts b/src/pageEditor/store/editor/editorInvariantMiddleware.ts index 3160619934..061b8d5667 100644 --- a/src/pageEditor/store/editor/editorInvariantMiddleware.ts +++ b/src/pageEditor/store/editor/editorInvariantMiddleware.ts @@ -72,7 +72,7 @@ export function assertEditorInvariants( // Assert that the expanded mod must correspond to the selected mod or mod component const expandedModId = selectExpandedModId(state); - if (expandedModId && selectActiveModId(state) !== expandedModId) { + if (expandedModId && activeModId !== expandedModId) { throw new InvariantViolationError( "expandedModId does not match active mod", ); From de301d1170ce4590917ab70db78f4383410123a1 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Mon, 11 Nov 2024 12:44:44 -0600 Subject: [PATCH 05/18] restore ListGroup --- .../src/pageEditor/modListingPanel/ModSidebarListItems.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/browser-extension/src/pageEditor/modListingPanel/ModSidebarListItems.tsx b/applications/browser-extension/src/pageEditor/modListingPanel/ModSidebarListItems.tsx index 957048387c..5538102997 100644 --- a/applications/browser-extension/src/pageEditor/modListingPanel/ModSidebarListItems.tsx +++ b/applications/browser-extension/src/pageEditor/modListingPanel/ModSidebarListItems.tsx @@ -131,7 +131,7 @@ const ModSidebarListItems: React.FunctionComponent = () => { {/* Mod Component List */} - {listItems} + {listItems} ); From 85468d6b580d75054932580823bbaf923a4b6be1 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Mon, 11 Nov 2024 15:50:50 -0600 Subject: [PATCH 06/18] favor migration over invariant checks --- .../store/editor/editorInvariantMiddleware.ts | 13 ++----------- .../src/pageEditor/store/editor/editorSlice.ts | 2 +- .../src/store/editorMigrations.ts | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts b/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts index 061b8d5667..8e6b33fa64 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts @@ -26,8 +26,6 @@ import { } from "@/pageEditor/store/editor/editorSelectors"; import type { EmptyObject } from "type-fest"; import { uniqBy } from "lodash"; -import { type AppDispatch } from "@/pageEditor/store/store"; -import { actions } from "@/pageEditor/store/editor/editorSlice"; class InvariantViolationError extends Error { override name = "InvariantViolationError"; @@ -47,18 +45,11 @@ class InvariantViolationError extends Error { * @see editorInvariantMiddleware */ // XXX: in production, should we be attempting to auto-fix these invariants? -export function assertEditorInvariants( - state: EditorRootState, - dispatch: AppDispatch, -): void { +export function assertEditorInvariants(state: EditorRootState): void { // Assert that a mod and mod component item cannot be selected at the same time const activeModId = selectActiveModId(state); const activeModComponentId = selectActiveModComponentFormState(state); - if (activeModComponentId && !activeModId) { - dispatch(actions.setActiveModId(activeModComponentId.modMetadata.id)); - } - if ( activeModComponentId && activeModId !== activeModComponentId?.modMetadata.id @@ -113,7 +104,7 @@ const editorInvariantMiddleware: Middleware = const result = next(action); try { - assertEditorInvariants(storeAPI.getState(), storeAPI.dispatch); + assertEditorInvariants(storeAPI.getState()); } catch (error) { throw new Error(`Action violated invariant: ${action.type}`, { cause: error, diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts index 267eaff18b..347d0fe2bb 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts @@ -1067,7 +1067,7 @@ export const persistEditorConfig: PersistConfig = { // Change the type of localStorage to our overridden version so that it can be exported // See: @/store/StorageInterface.ts storage: localStorage as StorageInterface, - version: 12, + version: 13, migrate: createMigrate(migrations, { debug: Boolean(process.env.DEBUG) }), blacklist: Object.keys(initialEphemeralState), }; diff --git a/applications/browser-extension/src/store/editorMigrations.ts b/applications/browser-extension/src/store/editorMigrations.ts index 16f48f9008..b2816dec49 100644 --- a/applications/browser-extension/src/store/editorMigrations.ts +++ b/applications/browser-extension/src/store/editorMigrations.ts @@ -85,6 +85,8 @@ export const migrations: MigrationManifest = { migrateEditorStateV10(state), 12: (state: EditorStateMigratedV11 & PersistedState) => migrateEditorStateV11(state), + 13: (state: EditorStateMigratedV11 & PersistedState) => + resetEditorStateSynced(state), }; export function migrateIntegrationDependenciesV1toV2( @@ -407,3 +409,17 @@ export function migrateEditorStateV11( _persist, }; } + +/** + * Reset the synced editor state to its initial state. + */ +// XXX: a limitation is that the function won't delete keys removed from the EditorStateSynced type. The assumption +// is size/storage of those keys will be negligible. +export function resetEditorStateSynced( + state: T, +): T & Required { + return { + ...state, + ...initialSyncedState, + }; +} From 340052cc8beeef70ad752e1b39d293ee9aed94ea Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Mon, 11 Nov 2024 15:54:20 -0600 Subject: [PATCH 07/18] 2.1.8 -> 2.2.0 --- applications/browser-extension/src/auth/featureFlags.ts | 4 ++-- .../contentScript/platform/contentScriptPlatformProtocol.ts | 2 +- .../store/editor/pageEditorTypes/editorStateMigrated.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/browser-extension/src/auth/featureFlags.ts b/applications/browser-extension/src/auth/featureFlags.ts index 059da00ee8..afec81b230 100644 --- a/applications/browser-extension/src/auth/featureFlags.ts +++ b/applications/browser-extension/src/auth/featureFlags.ts @@ -79,13 +79,13 @@ export const FeatureFlags = { /** * Show mod variables definition editor/affordances in the Page Editor - * @since 2.1.8 + * @since 2.2.0 */ PAGE_EDITOR_MOD_VARIABLES_DEFINITION: "page-editor-mod-variables-definition", /** * Show mod version history affordances in the Page Editor - * @since 2.1.8 + * @since 2.2.0 */ PAGE_EDITOR_MOD_VERSION_HISTORY: "page-editor-mod-version-history", diff --git a/applications/browser-extension/src/contentScript/platform/contentScriptPlatformProtocol.ts b/applications/browser-extension/src/contentScript/platform/contentScriptPlatformProtocol.ts index c07185b3f9..a4e564c40e 100644 --- a/applications/browser-extension/src/contentScript/platform/contentScriptPlatformProtocol.ts +++ b/applications/browser-extension/src/contentScript/platform/contentScriptPlatformProtocol.ts @@ -7,7 +7,7 @@ import type { ElementReference } from "@/types/runtimeTypes"; /** * Extended platform protocol for content scripts. - * @since 2.1.8 + * @since 2.2.0 * @see isContentScriptPlatformProtocol */ export interface ContentScriptPlatformProtocol extends PlatformProtocol { diff --git a/applications/browser-extension/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts b/applications/browser-extension/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts index 33d8fcb4a1..6dd2f59606 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/pageEditorTypes/editorStateMigrated.ts @@ -384,7 +384,7 @@ export type EditorStateMigratedV11 = Except< * @see EditorStateEphemeral * * @deprecated - Do not use versioned state types directly, exported for testing - * @since 2.1.8 + * @since 2.2.0 */ export type EditorStateMigratedV12 = Pick< EditorStateMigratedV11, From 55c28b84dbdf235a27a11f109e2e85b987328487 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 11:27:00 -0600 Subject: [PATCH 08/18] currentModId -> activeModId --- .../src/pageEditor/hooks/useEnsureFormStates.ts | 8 ++++---- .../editor/editorSelectors/editorDataPanelSelectors.ts | 6 +++--- .../src/pageEditor/store/editor/editorSlice.ts | 8 ++++---- .../tabs/editTab/dataPanel/tabs/FindTab/useFindInMod.ts | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/applications/browser-extension/src/pageEditor/hooks/useEnsureFormStates.ts b/applications/browser-extension/src/pageEditor/hooks/useEnsureFormStates.ts index 4e0153ae8d..a5979deab6 100644 --- a/applications/browser-extension/src/pageEditor/hooks/useEnsureFormStates.ts +++ b/applications/browser-extension/src/pageEditor/hooks/useEnsureFormStates.ts @@ -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"; @@ -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(() => { diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts index 07a9e0324d..1260956eb0 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts @@ -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"; export const selectIsDataPanelExpanded = ({ editor }: EditorRootState) => editor.isDataPanelExpanded; @@ -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: "" }; }, diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts index 3c3dddc910..d7a7a5bbdf 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts @@ -39,7 +39,7 @@ import { selectActiveBrickConfigurationUIState, selectActiveBrickPipelineUIState, selectActiveModComponentFormState, - selectCurrentModId, + selectActiveModId, selectGetModComponentFormStateByModComponentId, selectGetModComponentFormStatesForMod, selectModComponentFormStates, @@ -982,10 +982,10 @@ export const editorSlice = createSlice({ setDataPanelTabFindQuery(state, action: PayloadAction<{ query: string }>) { const { query } = action.payload; - const currentModId = selectCurrentModId({ editor: state }); - assertNotNullish(currentModId, "Expected currentModId"); + const activeModId = selectActiveModId({ editor: state }); + assertNotNullish(activeModId, "Expected activeModId"); - state.findInModQueryByModId[currentModId] = { query }; + state.findInModQueryByModId[activeModId] = { query }; }, /** diff --git a/applications/browser-extension/src/pageEditor/tabs/editTab/dataPanel/tabs/FindTab/useFindInMod.ts b/applications/browser-extension/src/pageEditor/tabs/editTab/dataPanel/tabs/FindTab/useFindInMod.ts index ba1b9bc81b..f1cb8f75bc 100644 --- a/applications/browser-extension/src/pageEditor/tabs/editTab/dataPanel/tabs/FindTab/useFindInMod.ts +++ b/applications/browser-extension/src/pageEditor/tabs/editTab/dataPanel/tabs/FindTab/useFindInMod.ts @@ -17,7 +17,7 @@ import { useSelector } from "react-redux"; import { - selectCurrentModId, + selectActiveModId, selectGetModComponentFormStatesForMod, } from "@/pageEditor/store/editor/editorSelectors"; import { useMemo } from "react"; @@ -40,14 +40,14 @@ function useFindInMod( // Find/search depends on all mod components having form states with instanceIds assigned useEnsureFormStates(); - const currentModId = useSelector(selectCurrentModId); - assertNotNullish(currentModId, "Expected currentModId"); + const activeModId = useSelector(selectActiveModId); + assertNotNullish(activeModId, "Expected activeModId"); const getModComponentFormStatesForMod = useSelector( selectGetModComponentFormStatesForMod, ); - const modComponentFormStates = getModComponentFormStatesForMod(currentModId); + const modComponentFormStates = getModComponentFormStatesForMod(activeModId); const fuse = useAsyncState(async () => { const items = await SearchIndexVisitor.collectItems(modComponentFormStates); From 9d033d73606c3d7b40faf370c40f15513d3112dc Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 11:56:21 -0600 Subject: [PATCH 09/18] add watch command --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2896c37b95..67371a3f6d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "build:typecheck": "npm run build:typecheck --workspaces", "dead-code": "npm run dead-code:base -- --include files,duplicates,dependencies,classMembers,binaries,enumMembers,nsTypes,exports,nsExports", "dead-code:base": "knip --config knip.mjs --tags=-knip", - "dead-code:prod": "npm run dead-code -- --production" + "dead-code:prod": "npm run dead-code -- --production", + "watch": "npm run watch --workspaces" }, "author": "Todd Schiller", "license": "AGPL-3.0", From 4079cdccdfd5890a3e72de5249c528286d703e1b Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 12:49:41 -0600 Subject: [PATCH 10/18] fix setActiveModId import --- .../store/editor/editorSelectors/editorDataPanelSelectors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts index 1260956eb0..5737c6e92f 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorDataPanelSelectors.ts @@ -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 { selectActiveModId } from "@/pageEditor/store/editor/editorSelectors"; +import { selectActiveModId } from "@/pageEditor/store/editor/editorSelectors/editorNavigationSelectors"; export const selectIsDataPanelExpanded = ({ editor }: EditorRootState) => editor.isDataPanelExpanded; From 0b5c18868c2a903683461bf21c1c9c506a5c5074 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 14:33:44 -0600 Subject: [PATCH 11/18] fix editor state invariant when saving a new mod --- .../tests/pageEditor/saveMod.spec.ts | 1 - .../modListingPanel/ModListItem.tsx | 2 +- .../store/editor/editorInvariantMiddleware.ts | 10 +- .../editorPipelineSelectors.ts | 12 +- .../tabs/modMetadata/ModMetadataEditor.tsx | 103 +++++++++--------- 5 files changed, 65 insertions(+), 63 deletions(-) diff --git a/applications/browser-extension/end-to-end-tests/tests/pageEditor/saveMod.spec.ts b/applications/browser-extension/end-to-end-tests/tests/pageEditor/saveMod.spec.ts index b4a5dc4133..318b034317 100644 --- a/applications/browser-extension/end-to-end-tests/tests/pageEditor/saveMod.spec.ts +++ b/applications/browser-extension/end-to-end-tests/tests/pageEditor/saveMod.spec.ts @@ -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("/"); diff --git a/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx b/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx index 3fb2d2a40d..cf47cce7b1 100644 --- a/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx +++ b/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx @@ -77,7 +77,7 @@ const ModListItem: React.FC< as={ListGroup.Item} className={cx(styles.root, "list-group-item-action", { // Set the alternate background if a mod component in this mod is active - [styles.modBackground ?? ""]: isModComponentActive, + [styles.modBackground ?? ""]: isModActive && isModComponentActive, })} tabIndex={0} // Avoid using `button` because this item includes more buttons #2343 active={isModActive && !isModComponentActive} diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts b/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts index 8e6b33fa64..f81845e0db 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorInvariantMiddleware.ts @@ -26,6 +26,7 @@ import { } 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"; @@ -48,11 +49,14 @@ class InvariantViolationError extends Error { export function assertEditorInvariants(state: EditorRootState): void { // Assert that a mod and mod component item cannot be selected at the same time const activeModId = selectActiveModId(state); - const activeModComponentId = selectActiveModComponentFormState(state); + const activeModComponent = selectActiveModComponentFormState(state); if ( - activeModComponentId && - activeModId !== activeModComponentId?.modMetadata.id + 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 diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorPipelineSelectors.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorPipelineSelectors.ts index 14dd456354..9f1c45b3ab 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorPipelineSelectors.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSelectors/editorPipelineSelectors.ts @@ -73,9 +73,9 @@ export const selectActiveNodeInfo = createSelector( const activeModComponentNodeInfoSelector = createSelector( selectActiveBrickPipelineUIState, (_state: EditorRootState, instanceId: UUID) => instanceId, - (uiState: BrickPipelineUIState, instanceId: UUID) => + (uiState, instanceId) => // eslint-disable-next-line security/detect-object-injection -- using a node uuid - uiState.pipelineMap[instanceId], + uiState?.pipelineMap[instanceId], ); export const selectActiveModComponentNodeInfo = @@ -103,13 +103,13 @@ export const selectActiveNodeEventData = createSelector( export const selectPipelineMap = createSelector( selectActiveBrickPipelineUIState, - (uiState: BrickPipelineUIState) => uiState?.pipelineMap, + (uiState) => uiState?.pipelineMap, ); export const selectCollapsedNodes = createSelector( selectActiveBrickPipelineUIState, - (brickPipelineUIState: BrickPipelineUIState) => - Object.entries(brickPipelineUIState.nodeUIStates) + (brickPipelineUIState) => + Object.entries(brickPipelineUIState?.nodeUIStates ?? {}) .map(([nodeId, { collapsed }]) => (collapsed ? nodeId : null)) .filter((nodeId) => nodeId != null), ); @@ -117,7 +117,7 @@ export const selectCollapsedNodes = createSelector( const parentNodeInfoSelector = createSelector( selectActiveBrickPipelineUIState, (_state: EditorRootState, nodeId: UUID) => nodeId, - (brickPipelineUIState: BrickPipelineUIState, nodeId: UUID) => { + (brickPipelineUIState, nodeId) => { if (brickPipelineUIState == null) { return null; } diff --git a/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx b/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx index 9574135993..ffd62a43c0 100644 --- a/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx +++ b/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx @@ -133,60 +133,59 @@ const ModMetadataEditor: React.VoidFunctionComponent = () => { [dispatch], ); - const renderBody: RenderBody = ({ values }) => ( - - - - - Mod Metadata - - {showOldModVersionWarning && ( - - )} -
- {isInnerDefinitionRegistryId( - (values as ModMetadataFormState).id, - ) ? ( - - ) : ( - + console.log("ModMetadataEditor", { values }) || ( + + + + + Mod Metadata + + {showOldModVersionWarning && ( + )} -
- - - -
-
-
- ); +
+ {isInnerDefinitionRegistryId(values.id) ? ( + + ) : ( + + )} +
+ + + + + + + ); return ( From b12c321e50a23972ee57043fd3c4edca5eb40470 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 14:44:36 -0600 Subject: [PATCH 12/18] fix activeModCompnent deselection during save --- .../src/pageEditor/store/editor/editorSlice.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts index d7a7a5bbdf..bb9236b2c0 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts @@ -348,16 +348,22 @@ export const editorSlice = createSlice({ /// /** - * Activate the mod with the given id. Expands the mod listing pane item if not already expanded + * Select the mod with the given id. Expands the mod listing pane item if not already expanded * @see toggleExpandedModId */ setActiveModId(state, action: PayloadAction) { const modId = action.payload; + const activeModComponent = selectActiveModComponentFormState({ + editor: state, + }); state.error = null; - state.activeModComponentId = null; state.activeModId = modId; + if (activeModComponent?.modMetadata.id !== modId) { + state.activeModComponentId = null; + } + if (state.expandedModId !== modId) { state.expandedModId = modId; } From 7411ca9835bcec094896c428f48caff41bf8a4ed Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 14:47:01 -0600 Subject: [PATCH 13/18] remove console log --- .../tabs/modMetadata/ModMetadataEditor.tsx | 101 +++++++++--------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx b/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx index ffd62a43c0..d9e8ca5889 100644 --- a/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx +++ b/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx @@ -133,59 +133,58 @@ const ModMetadataEditor: React.VoidFunctionComponent = () => { [dispatch], ); - const renderBody: RenderBody = ({ values }) => - console.log("ModMetadataEditor", { values }) || ( - - - - - Mod Metadata - - {showOldModVersionWarning && ( - ( + + + + + Mod Metadata + + {showOldModVersionWarning && ( + + )} +
+ {isInnerDefinitionRegistryId(values.id) ? ( + + ) : ( + )} -
- {isInnerDefinitionRegistryId(values.id) ? ( - - ) : ( - - )} -
- - - - - - - ); +
+ + + +
+
+
+ ); return ( From d94521cf3e6ebcfc5b4590bd16da742b0705a59b Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 14:51:46 -0600 Subject: [PATCH 14/18] fix type issues --- .../src/pageEditor/modals/addBrickModal/useAddBrick.ts | 4 +++- .../browser-extension/src/pageEditor/tabs/editTab/EditTab.tsx | 2 +- package.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/applications/browser-extension/src/pageEditor/modals/addBrickModal/useAddBrick.ts b/applications/browser-extension/src/pageEditor/modals/addBrickModal/useAddBrick.ts index edb0026085..9fbc0a99e9 100644 --- a/applications/browser-extension/src/pageEditor/modals/addBrickModal/useAddBrick.ts +++ b/applications/browser-extension/src/pageEditor/modals/addBrickModal/useAddBrick.ts @@ -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, { diff --git a/applications/browser-extension/src/pageEditor/tabs/editTab/EditTab.tsx b/applications/browser-extension/src/pageEditor/tabs/editTab/EditTab.tsx index 6cc18f6533..676d4c55d7 100644 --- a/applications/browser-extension/src/pageEditor/tabs/editTab/EditTab.tsx +++ b/applications/browser-extension/src/pageEditor/tabs/editTab/EditTab.tsx @@ -64,7 +64,7 @@ const EditTab: React.FC<{ function copyBrick(instanceId: UUID) { // eslint-disable-next-line security/detect-object-injection -- UUID - const brickToCopy = pipelineMap[instanceId]?.blockConfig; + const brickToCopy = pipelineMap?.[instanceId]?.blockConfig; if (brickToCopy) { dispatch(actions.copyBrickConfig(brickToCopy)); } diff --git a/package.json b/package.json index 67371a3f6d..62ba317511 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "dead-code": "npm run dead-code:base -- --include files,duplicates,dependencies,classMembers,binaries,enumMembers,nsTypes,exports,nsExports", "dead-code:base": "knip --config knip.mjs --tags=-knip", "dead-code:prod": "npm run dead-code -- --production", - "watch": "npm run watch --workspaces" + "watch": "npm run watch --workspaces", + "watch:typescript": "npm run watch:typescript --workspaces" }, "author": "Todd Schiller", "license": "AGPL-3.0", From 36f02ca8e221cd0a7f16ea6501a94ea8789d5839 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 15:56:48 -0600 Subject: [PATCH 15/18] fix active mod component id selection during save --- .../src/components/formBuilder/edit/ActiveField.tsx | 2 +- .../src/pageEditor/hooks/useCreateModFromUnsavedMod.ts | 8 ++++++++ .../src/pageEditor/store/editor/editorSlice.ts | 7 +------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/applications/browser-extension/src/components/formBuilder/edit/ActiveField.tsx b/applications/browser-extension/src/components/formBuilder/edit/ActiveField.tsx index 81cbf8fd73..1c66c76161 100644 --- a/applications/browser-extension/src/components/formBuilder/edit/ActiveField.tsx +++ b/applications/browser-extension/src/components/formBuilder/edit/ActiveField.tsx @@ -3,7 +3,6 @@ import { type SelectStringOption, type SetActiveField, } from "@/components/formBuilder/formBuilderTypes"; -import FieldEditor from "./fieldEditor/FieldEditor"; import { moveStringInArray, getNormalizedUiOrder, @@ -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; diff --git a/applications/browser-extension/src/pageEditor/hooks/useCreateModFromUnsavedMod.ts b/applications/browser-extension/src/pageEditor/hooks/useCreateModFromUnsavedMod.ts index f27bd8f52f..3dbe9d0a2f 100644 --- a/applications/browser-extension/src/pageEditor/hooks/useCreateModFromUnsavedMod.ts +++ b/applications/browser-extension/src/pageEditor/hooks/useCreateModFromUnsavedMod.ts @@ -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 ) { diff --git a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts index bb9236b2c0..5aee4ec903 100644 --- a/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts +++ b/applications/browser-extension/src/pageEditor/store/editor/editorSlice.ts @@ -353,16 +353,11 @@ export const editorSlice = createSlice({ */ setActiveModId(state, action: PayloadAction) { const modId = action.payload; - const activeModComponent = selectActiveModComponentFormState({ - editor: state, - }); state.error = null; state.activeModId = modId; - if (activeModComponent?.modMetadata.id !== modId) { - state.activeModComponentId = null; - } + state.activeModComponentId = null; if (state.expandedModId !== modId) { state.expandedModId = modId; From db356f3ec3bca12bce52c4dd72aae13e8ced90e7 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Tue, 12 Nov 2024 17:13:05 -0600 Subject: [PATCH 16/18] fix lint error --- .../src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx b/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx index d9e8ca5889..9574135993 100644 --- a/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx +++ b/applications/browser-extension/src/pageEditor/tabs/modMetadata/ModMetadataEditor.tsx @@ -148,7 +148,9 @@ const ModMetadataEditor: React.VoidFunctionComponent = () => { /> )}
- {isInnerDefinitionRegistryId(values.id) ? ( + {isInnerDefinitionRegistryId( + (values as ModMetadataFormState).id, + ) ? ( Date: Wed, 13 Nov 2024 10:26:05 -0600 Subject: [PATCH 17/18] remove playwright command from source package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 631eb2dc36..396ca47c98 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ ], "scripts": { "test": "nx test", - "test:e2e:debug": "PWDEBUG=console playwright test", "lint": "nx lint", "build": "nx build", "build:typecheck": "nx build:typecheck", From 3b48053f3a011d853771c3eeda77c24d3bcf6090 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Thu, 14 Nov 2024 16:36:11 -0600 Subject: [PATCH 18/18] only collapse the mod on click when it's active --- .../src/pageEditor/modListingPanel/ModListItem.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx b/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx index cf47cce7b1..44142e75fa 100644 --- a/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx +++ b/applications/browser-extension/src/pageEditor/modListingPanel/ModListItem.tsx @@ -51,8 +51,8 @@ const ModListItem: React.FC< const { id: modId, name: savedName, version: activatedVersion } = modMetadata; - const isModActive = activeModId === modId; - const isModComponentActive = activeModComponentId != null; + 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 @@ -77,15 +77,19 @@ const ModListItem: React.FC< as={ListGroup.Item} className={cx(styles.root, "list-group-item-action", { // Set the alternate background if a mod component in this mod is active - [styles.modBackground ?? ""]: isModActive && isModComponentActive, + [styles.modBackground ?? ""]: isModSelected || isModComponentSelected, })} tabIndex={0} // Avoid using `button` because this item includes more buttons #2343 - active={isModActive && !isModComponentActive} + 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 ? null : modId)); + dispatch( + actions.setExpandedModId( + isExpanded && isModSelected ? null : modId, + ), + ); }} >