From e9f9dfcb4e3901bde6a7ed285114704f86f6e4c6 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 10:36:48 -0400 Subject: [PATCH 01/13] WIP: group mod component reference --- .../ephemeralForm/formTransformer.ts | 5 +- .../temporaryInfo/DisplayTemporaryInfo.ts | 9 ++- .../temporaryInfo/EphemeralPanel.tsx | 10 +--- src/contentScript/contentScriptPlatform.ts | 4 +- src/contentScript/ephemeralForm.ts | 14 ++--- src/contentScript/ephemeralPanel.ts | 12 ++-- src/contentScript/lifecycle.ts | 4 +- src/contentScript/messenger/registration.ts | 2 +- .../pageEditor/runRendererBrick.ts | 24 ++++---- src/contentScript/sidebarController.tsx | 60 +++++++++++-------- .../tabs/effect/useDocumentPreviewRunBlock.ts | 6 +- src/platform/forms/formController.ts | 24 +++----- src/platform/panels/panelController.ts | 10 +++- src/platform/platformBase.ts | 9 +-- src/platform/platformProtocol.ts | 6 +- src/sidebar/Tabs.tsx | 20 +++---- src/sidebar/TemporaryPanelTabPane.tsx | 11 ++-- .../modLauncher/ActiveSidebarModsList.tsx | 2 +- src/sidebar/sidebarSelectors.ts | 4 +- src/store/sidebar/eventKeyUtils.test.ts | 29 ++++----- src/store/sidebar/eventKeyUtils.tsx | 2 +- src/store/sidebar/sidebarSlice.test.ts | 12 ++-- src/store/sidebar/sidebarSlice.ts | 29 +++++---- src/store/sidebar/thunks/addFormPanel.ts | 7 ++- src/store/sidebar/thunks/addTemporaryPanel.ts | 2 +- .../factories/modComponentFactories.ts | 8 +++ .../factories/sidebarEntryFactories.ts | 14 ++--- src/types/modComponentTypes.ts | 12 ++-- src/types/sidebarTypes.ts | 40 ++++--------- 29 files changed, 186 insertions(+), 205 deletions(-) diff --git a/src/bricks/transformers/ephemeralForm/formTransformer.ts b/src/bricks/transformers/ephemeralForm/formTransformer.ts index 894b3cfa42..9a2ccf494b 100644 --- a/src/bricks/transformers/ephemeralForm/formTransformer.ts +++ b/src/bricks/transformers/ephemeralForm/formTransformer.ts @@ -141,8 +141,9 @@ export class FormTransformer extends TransformerABC { try { return await platform.form(formDefinition, controller, { - componentId: logger.context.extensionId, - modId: logger.context.blueprintId, + extensionId: logger.context.extensionId, + blueprintId: logger.context.blueprintId, + extensionPointId: logger.context.extensionPointId, }); } finally { controller.abort(); diff --git a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts index d0015a87b4..e236ad84e8 100644 --- a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts +++ b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts @@ -102,7 +102,7 @@ class DisplayTemporaryInfo extends TransformerABC { }>, { logger: { - context: { extensionId, blueprintId }, + context: { extensionId, blueprintId, extensionPointId }, }, root = document, platform, @@ -123,8 +123,11 @@ class DisplayTemporaryInfo extends TransformerABC { const panelEntryMetadata: TemporaryPanelEntryMetadata = { heading: title, - extensionId, - blueprintId, + componentRef: { + extensionId, + blueprintId, + extensionPointId, + }, }; const getPayload = async () => { diff --git a/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx b/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx index 113d051dfe..a132f84123 100644 --- a/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx +++ b/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx @@ -174,10 +174,7 @@ const EphemeralPanel: React.FC = () => { { resolveTemporaryPanel(target, panelNonce, action); }} @@ -219,10 +216,7 @@ const EphemeralPanel: React.FC = () => { { resolveTemporaryPanel(target, panelNonce, action); }} diff --git a/src/contentScript/contentScriptPlatform.ts b/src/contentScript/contentScriptPlatform.ts index 0aad9e539d..f7535dd145 100644 --- a/src/contentScript/contentScriptPlatform.ts +++ b/src/contentScript/contentScriptPlatform.ts @@ -286,8 +286,8 @@ class ContentScriptPlatform extends PlatformBase { override get panels(): PlatformProtocol["panels"] { return { isContainerVisible: async () => sidebarController.isSidePanelOpen(), - unregisterExtensionPoint: sidebarController.removeExtensionPoint, - removeComponents: sidebarController.removeExtensions, + unregisterExtensionPoint: sidebarController.removeStarterBrick, + removeComponents: sidebarController.removeModComponents, reservePanels: sidebarController.reservePanels, updateHeading: sidebarController.updateHeading, upsertPanel: sidebarController.upsertPanel, diff --git a/src/contentScript/ephemeralForm.ts b/src/contentScript/ephemeralForm.ts index 8a65e48c4c..f55ae2eb1e 100644 --- a/src/contentScript/ephemeralForm.ts +++ b/src/contentScript/ephemeralForm.ts @@ -26,12 +26,11 @@ import { import { uuidv4 } from "@/types/helpers"; import { isLoadedInIframe } from "@/utils/iframeUtils"; import { BusinessError } from "@/errors/businessErrors"; -import type { UUID } from "@/types/stringTypes"; -import type { RegistryId } from "@/types/registryTypes"; import { getThisFrame } from "webext-messenger"; import { expectContext } from "@/utils/expectContext"; import { showModal } from "@/contentScript/modalDom"; import type { Target } from "@/types/messengerTypes"; +import type { ModComponentRef } from "@/types/modComponentTypes"; // The modes for createFrameSource are different from the location argument for FormTransformer. The mode for the frame // just determines the layout container of the form @@ -55,10 +54,7 @@ export async function createFrameSource( export async function ephemeralForm( definition: FormDefinition, controller: AbortController, - { - componentId: extensionId, - modId: blueprintId, - }: { componentId: UUID; modId: RegistryId }, + componentRef: ModComponentRef, ): Promise { expectContext("contentScript"); @@ -75,10 +71,9 @@ export async function ephemeralForm( // Pre-registering the form also allows the sidebar to know a form will be shown in computing the default // tab to show during sidebar initialization. const formPromise = registerForm({ - extensionId, + componentRef, nonce: formNonce, definition, - blueprintId, }); if (definition.location === "sidebar") { @@ -86,10 +81,9 @@ export async function ephemeralForm( await showSidebar(); await showSidebarForm({ - extensionId, - blueprintId, nonce: formNonce, form: definition, + componentRef, }); // Two-way binding between sidebar and form. Listen for the user (or an action) closing the sidebar diff --git a/src/contentScript/ephemeralPanel.ts b/src/contentScript/ephemeralPanel.ts index e3825e9af1..9d82b2868f 100644 --- a/src/contentScript/ephemeralPanel.ts +++ b/src/contentScript/ephemeralPanel.ts @@ -114,7 +114,7 @@ export async function ephemeralPanel({ registerEmptyTemporaryPanel({ nonce, location, - extensionId: panelEntryMetadata.extensionId, + extensionId: panelEntryMetadata.componentRef.extensionId, }); await showSidebar(); @@ -125,7 +125,7 @@ export async function ephemeralPanel({ nonce, payload: { key: uuidv4(), - extensionId: panelEntryMetadata.extensionId, + extensionId: panelEntryMetadata.componentRef.extensionId, loadingMessage: "Loading", }, }); @@ -140,13 +140,15 @@ export async function ephemeralPanel({ } else { // Popover/modal location // Clear existing to remove stale modals/popovers - await cancelTemporaryPanelsForExtension(panelEntryMetadata.extensionId); + await cancelTemporaryPanelsForExtension( + panelEntryMetadata.componentRef.extensionId, + ); // Register empty panel for "loading" state registerEmptyTemporaryPanel({ nonce, location, - extensionId: panelEntryMetadata.extensionId, + extensionId: panelEntryMetadata.componentRef.extensionId, }); // Create a source URL for content that will be loaded in the panel iframe @@ -233,7 +235,7 @@ export async function ephemeralPanel({ nonce, location, entry, - extensionId: entry.extensionId, + extensionId: entry.componentRef.extensionId, onRegister: onReady, }); return panelAction ?? {}; diff --git a/src/contentScript/lifecycle.ts b/src/contentScript/lifecycle.ts index 5a3158c995..6f274739b7 100644 --- a/src/contentScript/lifecycle.ts +++ b/src/contentScript/lifecycle.ts @@ -318,7 +318,7 @@ export function removeDraftModComponents( _runningStarterBricks.delete(starterBrick); _draftModComponentStarterBrickMap.delete(modComponentId); - sidebar.removeExtensions([modComponentId]); + sidebar.removeModComponents([modComponentId]); } else { console.debug( `No draft mod component exists for uuid: ${modComponentId}`, @@ -333,7 +333,7 @@ export function removeDraftModComponents( try { starterBrick.uninstall({ global: true }); _runningStarterBricks.delete(starterBrick); - sidebar.removeExtensionPoint(starterBrick.id); + sidebar.removeStarterBrick(starterBrick.id); } catch (error) { reportError(error); } diff --git a/src/contentScript/messenger/registration.ts b/src/contentScript/messenger/registration.ts index d3bd387b95..31a74126fa 100644 --- a/src/contentScript/messenger/registration.ts +++ b/src/contentScript/messenger/registration.ts @@ -21,7 +21,7 @@ import { showSidebarInTopFrame, sidebarWasLoaded, updateSidebar, - removeExtensions as removeSidebars, + removeModComponents as removeSidebars, getReservedPanelEntries, } from "@/contentScript/sidebarController"; import { handleMenuAction } from "@/contentScript/contextMenus"; diff --git a/src/contentScript/pageEditor/runRendererBrick.ts b/src/contentScript/pageEditor/runRendererBrick.ts index 946f3ceb6f..848d796aa6 100644 --- a/src/contentScript/pageEditor/runRendererBrick.ts +++ b/src/contentScript/pageEditor/runRendererBrick.ts @@ -24,11 +24,11 @@ import { HeadlessModeError } from "@/bricks/errors"; import { showTemporarySidebarPanel } from "@/contentScript/sidebarController"; import { waitForTemporaryPanel } from "@/platform/panels/panelController"; import { type UUID } from "@/types/stringTypes"; -import { type RegistryId } from "@/types/registryTypes"; import { createFrameSource } from "@/contentScript/ephemeralPanel"; import { showModal } from "@/contentScript/modalDom"; import { runBrickPreview } from "@/contentScript/pageEditor/runBrickPreview"; import { type RunBrickArgs } from "@/contentScript/pageEditor/types"; +import { type ModComponentRef } from "@/types/modComponentTypes"; type Location = "modal" | "panel"; @@ -47,15 +47,13 @@ type Location = "modal" | "panel"; * @see useDocumentPreviewRunBlock */ export async function runRendererBrick({ - modComponentId, - modId, + componentRef, runId, title, args, location, }: { - modComponentId: UUID; - modId: RegistryId | null; + componentRef: ModComponentRef; runId: UUID; title: string; args: RunBrickArgs; @@ -65,7 +63,7 @@ export async function runRendererBrick({ let payload: PanelPayload; try { - await runBrickPreview({ ...args, modId }); + await runBrickPreview({ ...args, modId: componentRef.blueprintId }); // We're expecting a HeadlessModeError (or other error) to be thrown in the line above // noinspection ExceptionCaughtLocallyJS throw new NoRendererError(); @@ -76,23 +74,22 @@ export async function runRendererBrick({ blockId: error.blockId, args: error.args, ctxt: error.ctxt, - extensionId: modComponentId, + extensionId: componentRef.extensionId, runId, }; } else { payload = { key: nonce, error: serializeError(error), - extensionId: modComponentId, + extensionId: componentRef.extensionId, runId, }; } if (location === "panel") { await showTemporarySidebarPanel({ - // Pass extension id so previous run is cancelled - extensionId: modComponentId, - blueprintId: modId, + // Pass component ref id so previous run is cancelled + componentRef, nonce, heading: title, payload, @@ -107,10 +104,9 @@ export async function runRendererBrick({ await waitForTemporaryPanel({ nonce, location, - extensionId: modComponentId, + extensionId: componentRef.extensionId, entry: { - extensionId: modComponentId, - blueprintId: modId, + componentRef, nonce, heading: title, payload, diff --git a/src/contentScript/sidebarController.tsx b/src/contentScript/sidebarController.tsx index 08d5adcf49..c5be277dae 100644 --- a/src/contentScript/sidebarController.tsx +++ b/src/contentScript/sidebarController.tsx @@ -354,41 +354,47 @@ export async function hideTemporarySidebarPanel(nonce: UUID): Promise { } /** - * Remove all panels associated with given extensionIds. - * @param extensionIds the extension UUIDs to remove + * Remove all panels associated with given modComponentIds. + * @param modComponentIds the mod component UUIDs to remove */ -export function removeExtensions(extensionIds: UUID[]): void { +export function removeModComponents(modComponentIds: UUID[]): void { expectContext("contentScript"); - console.debug("sidebarController:removeExtensions", { extensionIds }); + console.debug("sidebarController:removeExtensions", { modComponentIds }); // Avoid unnecessary messaging. More importantly, renderPanelsIfVisible should not be called from iframes. Iframes - // might call removeExtensions as part of cleanup - if (extensionIds.length === 0) { + // might call removeModComponents as part of cleanup + if (modComponentIds.length === 0) { return; } // `panels` is const, so replace the contents const current = panels.splice(0); - panels.push(...current.filter((x) => !extensionIds.includes(x.extensionId))); + panels.push( + ...current.filter( + (x) => !modComponentIds.includes(x.componentRef.extensionId), + ), + ); void renderPanelsIfVisible(); } /** * Remove all panels associated with the given extensionPointId. - * @param extensionPointId the extension point id (internal or external) + * @param starterBrickId the extension point id (internal or external) * @param preserveExtensionIds array of extension ids to keep in the panel. Used to avoid flickering if updating * the extensionPoint for a sidebar extension from the Page Editor */ -export function removeExtensionPoint( - extensionPointId: RegistryId, +export function removeStarterBrick( + starterBrickId: RegistryId, { preserveExtensionIds = [] }: { preserveExtensionIds?: UUID[] } = {}, ): void { expectContext("contentScript"); - console.debug("sidebarController:removeExtensionPoint %s", extensionPointId, { + console.debug("sidebarController:removeStarterBrick %s", starterBrickId, { preserveExtensionIds, - panels: panels.filter((x) => x.extensionPointId === extensionPointId), + panels: panels.filter( + (x) => x.componentRef.extensionPointId === starterBrickId, + ), }); // `panels` is const, so replace the contents @@ -396,8 +402,8 @@ export function removeExtensionPoint( panels.push( ...current.filter( (x) => - x.extensionPointId !== extensionPointId || - preserveExtensionIds.includes(x.extensionId), + x.componentRef.extensionPointId !== starterBrickId || + preserveExtensionIds.includes(x.componentRef.extensionId), ), ); @@ -412,14 +418,16 @@ export function reservePanels(refs: ModComponentRef[]): void { return; } - const current = new Set(panels.map((x) => x.extensionId)); + const current = new Set(panels.map((x) => x.componentRef.extensionId)); for (const { extensionId, extensionPointId, blueprintId } of refs) { if (!current.has(extensionId)) { const entry: PanelEntry = { type: "panel", - extensionId, - extensionPointId, - blueprintId, + componentRef: { + extensionId, + extensionPointId, + blueprintId, + }, heading: "", payload: null, }; @@ -440,14 +448,14 @@ export function reservePanels(refs: ModComponentRef[]): void { } export function updateHeading(extensionId: UUID, heading: string): void { - const entry = panels.find((x) => x.extensionId === extensionId); + const entry = panels.find((x) => x.componentRef.extensionId === extensionId); if (entry) { entry.heading = heading; console.debug( "updateHeading: update heading for panel %s for %s", extensionId, - entry.extensionPointId, + entry.componentRef.extensionPointId, { ...entry }, ); void renderPanelsIfVisible(); @@ -464,7 +472,9 @@ export function upsertPanel( heading: string, payload: PanelPayload, ): void { - const entry = panels.find((panel) => panel.extensionId === extensionId); + const entry = panels.find( + (panel) => panel.componentRef.extensionId === extensionId, + ); if (entry) { entry.payload = payload; entry.heading = heading; @@ -490,9 +500,11 @@ export function upsertPanel( ); panels.push({ type: "panel", - extensionId, - extensionPointId, - blueprintId, + componentRef: { + extensionId, + extensionPointId, + blueprintId, + }, heading, payload, }); diff --git a/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts b/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts index 40d5f5aba4..d2d76ee079 100644 --- a/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts +++ b/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts @@ -191,10 +191,12 @@ export default function useDocumentPreviewRunBlock( try { await runRendererBrick(inspectedTab, { - modComponentId, - modId: mod?.id, runId: traceRecord.runId, title, + componentRef: { + extensionId: modComponentId, + blueprintId: mod?.id, + }, args: { apiVersion, blockConfig: { diff --git a/src/platform/forms/formController.ts b/src/platform/forms/formController.ts index a0f7ee7970..3e0251ed33 100644 --- a/src/platform/forms/formController.ts +++ b/src/platform/forms/formController.ts @@ -20,17 +20,15 @@ import { type UUID } from "@/types/stringTypes"; import pDefer, { type DeferredPromise } from "p-defer"; import { CancelError } from "@/errors/businessErrors"; import { type FormPanelEntry } from "@/types/sidebarTypes"; -import { type RegistryId } from "@/types/registryTypes"; -import { type Nullishable } from "@/utils/nullishUtils"; +import { type ModComponentRef } from "@/types/modComponentTypes"; export type RegisteredForm = { /** * The Mod Component that created the form. Only 1 form can be registered per Mod Component. */ - extensionId: UUID; + componentRef: ModComponentRef; definition: FormDefinition; registration: DeferredPromise; - blueprintId: Nullishable; }; /** @@ -47,29 +45,25 @@ export function getFormPanelSidebarEntries(): FormPanelEntry[] { .map(([nonce, form]) => ({ type: "form", nonce, - extensionId: form.extensionId, - blueprintId: form.blueprintId ?? undefined, + componentRef: form.componentRef, form: form.definition, })); } /** * Register a form with the content script that resolves the form is either submitted or cancelled - * @param extensionId the id of the extension that created the form + * @param componentRef the mod component that created the form * @param nonce the form nonce * @param definition the form definition - * @param blueprintId the blueprint that contains the form */ export async function registerForm({ - extensionId, nonce, definition, - blueprintId, + componentRef, }: { - extensionId: UUID; + componentRef: ModComponentRef; nonce: UUID; definition: FormDefinition; - blueprintId: Nullishable; }): Promise { const registration = pDefer(); @@ -79,7 +73,8 @@ export async function registerForm({ } const preexistingForms = [...forms.entries()].filter( - ([_, registeredForm]) => registeredForm.extensionId === extensionId, + ([_, registeredForm]) => + registeredForm.componentRef.extensionId === componentRef.extensionId, ); if (preexistingForms.length > 0) { @@ -88,10 +83,9 @@ export async function registerForm({ } forms.set(nonce, { - extensionId, + componentRef, definition, registration, - blueprintId, }); return registration.promise; diff --git a/src/platform/panels/panelController.ts b/src/platform/panels/panelController.ts index d328261a93..b030fc39ab 100644 --- a/src/platform/panels/panelController.ts +++ b/src/platform/panels/panelController.ts @@ -105,7 +105,11 @@ export function updatePanelDefinition( } // Panel entry may be undefined if the panel was registered with registerEmptyTemporaryPanel - if (panel.entry && panel.entry.extensionId !== panelDefinition.extensionId) { + if ( + panel.entry && + panel.entry.componentRef.extensionId !== + panelDefinition.componentRef.extensionId + ) { throw new Error("extensionId mismatch"); } @@ -200,7 +204,9 @@ export async function waitForTemporaryPanel({ function removePanelEntry(panelNonce: UUID): void { const panel = panels.get(panelNonce); if (panel?.entry) { - extensionNonces.get(panel.entry.extensionId)?.delete(panelNonce); + extensionNonces + .get(panel.entry.componentRef.extensionId) + ?.delete(panelNonce); } panels.delete(panelNonce); diff --git a/src/platform/platformBase.ts b/src/platform/platformBase.ts index 0ee64b802c..4dc3f6c6d2 100644 --- a/src/platform/platformBase.ts +++ b/src/platform/platformBase.ts @@ -19,9 +19,8 @@ import { type PlatformCapability, PlatformCapabilityNotAvailableError, } from "@/platform/capabilities"; -import type { RegistryId, SemVerString } from "@/types/registryTypes"; +import type { SemVerString } from "@/types/registryTypes"; import type { FormDefinition } from "@/platform/forms/formTypes"; -import type { UUID } from "@/types/stringTypes"; import type { Nullishable } from "@/utils/nullishUtils"; import type { SanitizedIntegrationConfig } from "@/integrations/integrationTypes"; import type { NetworkRequestConfig } from "@/types/networkTypes"; @@ -42,6 +41,7 @@ import type { ClipboardProtocol } from "@/platform/platformTypes/clipboardProtoc import type { PlatformProtocol } from "@/platform/platformProtocol"; import type { PanelProtocol } from "@/platform/platformTypes/panelProtocol"; import type { QuickBarProtocol } from "@/platform/platformTypes/quickBarProtocol"; +import type { ModComponentRef } from "@/types/modComponentTypes"; /** * Base protocol with no capabilities implemented. @@ -72,10 +72,7 @@ export class PlatformBase implements PlatformProtocol { async form( _definition: FormDefinition, _controller: AbortController, - _context: { - componentId: UUID; - modId?: RegistryId; - }, + _context: ModComponentRef, ): Promise { throw new PlatformCapabilityNotAvailableError(this.platformName, "form"); } diff --git a/src/platform/platformProtocol.ts b/src/platform/platformProtocol.ts index 174b08626e..61fea60b90 100644 --- a/src/platform/platformProtocol.ts +++ b/src/platform/platformProtocol.ts @@ -22,8 +22,7 @@ import type { NetworkRequestConfig } from "@/types/networkTypes"; import type { RemoteResponse } from "@/types/contract"; import type { Nullishable } from "@/utils/nullishUtils"; import type { FormDefinition } from "@/platform/forms/formTypes"; -import type { UUID } from "@/types/stringTypes"; -import type { RegistryId, SemVerString } from "@/types/registryTypes"; +import type { SemVerString } from "@/types/registryTypes"; import type { JavaScriptPayload } from "@/sandbox/messenger/api"; import type { Logger } from "@/types/loggerTypes"; import type { AudioProtocol } from "@/platform/platformTypes/audioProtocol"; @@ -38,6 +37,7 @@ import type { SnippetShortcutMenuProtocol } from "@/platform/platformTypes/snipp import type { TextSelectionMenuProtocol } from "@/platform/platformTypes/textSelectionMenuProtocol"; import type { PanelProtocol } from "@/platform/platformTypes/panelProtocol"; import type { QuickBarProtocol } from "@/platform/platformTypes/quickBarProtocol"; +import { ModComponentRef } from "@/types/modComponentTypes"; /** * A protocol for the platform/environment running the mods. @@ -90,7 +90,7 @@ export interface PlatformProtocol { form: ( definition: FormDefinition, controller: AbortController, - context: { componentId: UUID; modId?: RegistryId }, + componentRef: ModComponentRef, ) => Promise; /** diff --git a/src/sidebar/Tabs.tsx b/src/sidebar/Tabs.tsx index 0f8a8cdcac..65fbcf6626 100644 --- a/src/sidebar/Tabs.tsx +++ b/src/sidebar/Tabs.tsx @@ -209,7 +209,7 @@ const Tabs: React.FC = () => { > {panels.map((panel) => ( @@ -224,7 +224,7 @@ const Tabs: React.FC = () => { {forms.map((form) => ( @@ -309,15 +309,15 @@ const Tabs: React.FC = () => { // un-submitted form state/scroll position unmountOnExit={false} className={cx("full-height flex-grow", styles.paneOverrides)} - key={panel.extensionId} + key={panel.componentRef.extensionId} eventKey={eventKeyForEntry(panel)} > { reportEvent(Events.VIEW_ERROR, { panelType: panel.type, - extensionId: panel.extensionId, - blueprintId: panel.blueprintId, + extensionId: panel.componentRef.extensionId, + blueprintId: panel.componentRef.blueprintId, }); }} > @@ -335,11 +335,7 @@ const Tabs: React.FC = () => { isRootPanel payload={panel.payload} onAction={permanentSidebarPanelAction} - context={{ - extensionId: panel.extensionId, - extensionPointId: panel.extensionPointId, - blueprintId: panel.blueprintId, - }} + context={panel.componentRef} /> @@ -355,8 +351,8 @@ const Tabs: React.FC = () => { onError={() => { reportEvent(Events.VIEW_ERROR, { panelType: form.type, - extensionId: form.extensionId, - blueprintId: form.blueprintId, + extensionId: form.componentRef.extensionId, + blueprintId: form.componentRef.blueprintId, }); }} > diff --git a/src/sidebar/TemporaryPanelTabPane.tsx b/src/sidebar/TemporaryPanelTabPane.tsx index f04f64d7a7..af64ad9278 100644 --- a/src/sidebar/TemporaryPanelTabPane.tsx +++ b/src/sidebar/TemporaryPanelTabPane.tsx @@ -50,7 +50,7 @@ export const TemporaryPanelTabPane: React.FC<{ }, [dispatch, panel.nonce], ); - const { type, extensionId, blueprintId, payload } = panel; + const { type, componentRef, payload } = panel; return ( { reportEvent(Events.VIEW_ERROR, { panelType: type, - extensionId, - blueprintId, + extensionId: componentRef.extensionId, + blueprintId: componentRef.blueprintId, }); }} > @@ -74,10 +74,7 @@ export const TemporaryPanelTabPane: React.FC<{ diff --git a/src/sidebar/modLauncher/ActiveSidebarModsList.tsx b/src/sidebar/modLauncher/ActiveSidebarModsList.tsx index 51674a3add..b68d971316 100644 --- a/src/sidebar/modLauncher/ActiveSidebarModsList.tsx +++ b/src/sidebar/modLauncher/ActiveSidebarModsList.tsx @@ -124,7 +124,7 @@ export const ActiveSidebarModsList: React.FunctionComponent = () => { tableInstance.prepareRow(row); return ( ); diff --git a/src/sidebar/sidebarSelectors.ts b/src/sidebar/sidebarSelectors.ts index e48f3afb65..30774b08ef 100644 --- a/src/sidebar/sidebarSelectors.ts +++ b/src/sidebar/sidebarSelectors.ts @@ -75,7 +75,7 @@ const extensionForEventKeySelector = createSelector( } return extensions.find( - (extension) => extension.id === sidebarEntry.extensionId, + (extension) => extension.id === sidebarEntry.componentRef.extensionId, ); }, ); @@ -100,7 +100,7 @@ export const selectExtensionFromEventKey = } return extensions.find( - (extension) => extension.id === sidebarEntry.extensionId, + (extension) => extension.id === sidebarEntry.componentRef.extensionId, ); }; diff --git a/src/store/sidebar/eventKeyUtils.test.ts b/src/store/sidebar/eventKeyUtils.test.ts index 7ffdf1ac46..fdba5ae33e 100644 --- a/src/store/sidebar/eventKeyUtils.test.ts +++ b/src/store/sidebar/eventKeyUtils.test.ts @@ -20,13 +20,7 @@ import { eventKeyForEntry, } from "@/store/sidebar/eventKeyUtils"; import { uuidv4, validateRegistryId } from "@/types/helpers"; -import { - type SidebarState, - type SidebarEntries, - type PanelEntry, - type TemporaryPanelEntry, -} from "@/types/sidebarTypes"; - +import { type SidebarEntries, type SidebarState } from "@/types/sidebarTypes"; import { sidebarEntryFactory } from "@/testUtils/factories/sidebarEntryFactories"; import { MOD_LAUNCHER } from "@/store/sidebar/constants"; @@ -48,9 +42,9 @@ describe("defaultEventKey", () => { it("prefers latest form", () => { const args = { - forms: [{ nonce: uuidv4() }, { nonce: uuidv4() }], - temporaryPanels: [{ nonce: uuidv4() }], - panels: [{ extensionId: uuidv4() }], + forms: [sidebarEntryFactory("form"), sidebarEntryFactory("form")], + temporaryPanels: [sidebarEntryFactory("temporaryPanel")], + panels: [sidebarEntryFactory("panel")], } as SidebarEntries; expect(defaultEventKey(args, {})).toBe(eventKeyForEntry(args.forms[1])); @@ -61,10 +55,10 @@ describe("defaultEventKey", () => { const args: SidebarEntries = { forms: [], temporaryPanels: [ - { nonce: uuidv4() }, - { nonce: uuidv4() }, - ] as TemporaryPanelEntry[], - panels: [{ extensionId: uuidv4() }] as PanelEntry[], + sidebarEntryFactory("temporaryPanel"), + sidebarEntryFactory("temporaryPanel"), + ], + panels: [sidebarEntryFactory("panel")], staticPanels: [], modActivationPanel: null, } as SidebarEntries; @@ -80,10 +74,7 @@ describe("defaultEventKey", () => { const entries = { forms: [], temporaryPanels: [], - panels: [ - { extensionId: uuidv4() }, - { extensionId: uuidv4() }, - ] as PanelEntry[], + panels: [sidebarEntryFactory("panel"), sidebarEntryFactory("panel")], staticPanels: [], modActivationPanel: null, } as SidebarEntries; @@ -159,7 +150,7 @@ describe("eventKeyForEntry", () => { it("uses recipeId for activateRecipe", () => { const recipeId = validateRegistryId("@test/test-recipe"); const entry = sidebarEntryFactory("activateMods", { recipeId }); - // Main part is a an object hash of the mod ids + // Main part is an object hash of the mod ids expect(eventKeyForEntry(entry)).toStartWith("activate-"); }); diff --git a/src/store/sidebar/eventKeyUtils.tsx b/src/store/sidebar/eventKeyUtils.tsx index d6579546ad..49a1647413 100644 --- a/src/store/sidebar/eventKeyUtils.tsx +++ b/src/store/sidebar/eventKeyUtils.tsx @@ -44,7 +44,7 @@ function eventKeyForEntry(entry: Nullishable): string | null { } if (isPanelEntry(entry)) { - return getEventKeyForPanel(entry.extensionId); + return getEventKeyForPanel(entry.componentRef.extensionId); } if (isStaticPanelEntry(entry)) { diff --git a/src/store/sidebar/sidebarSlice.test.ts b/src/store/sidebar/sidebarSlice.test.ts index 07cf7adafc..279495e14b 100644 --- a/src/store/sidebar/sidebarSlice.test.ts +++ b/src/store/sidebar/sidebarSlice.test.ts @@ -101,7 +101,7 @@ describe("sidebarSlice.addTemporaryPanel", () => { const existingPanel = sidebarEntryFactory("temporaryPanel"); const otherExistingPanel = sidebarEntryFactory("temporaryPanel"); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: existingPanel.extensionId, + extensionId: existingPanel.componentRef.extensionId, }); const initialState: SidebarState = { @@ -198,7 +198,7 @@ describe("removeTemporaryPanel", () => { extensionId: uuidv4(), }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: originalPanel.extensionId, + extensionId: originalPanel.componentRef.extensionId, }); const initialState: SidebarState = { @@ -532,15 +532,17 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { it("sets activeKey to the active key of any panel with the same extensionId as the removedEntry if it exists", () => { const modId = validateRegistryId("test/123"); const originalPanel = sidebarEntryFactory("panel", { - extensionId: uuidv4(), - blueprintId: modId, + componentRef: { + extensionId: uuidv4(), + blueprintId: modId, + }, }); const otherExistingPanel = sidebarEntryFactory("form", { extensionId: uuidv4(), blueprintId: modId, }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: originalPanel.extensionId, + extensionId: originalPanel.componentRef.extensionId, blueprintId: modId, }); diff --git a/src/store/sidebar/sidebarSlice.ts b/src/store/sidebar/sidebarSlice.ts index 268547ae7f..10f8f19152 100644 --- a/src/store/sidebar/sidebarSlice.ts +++ b/src/store/sidebar/sidebarSlice.ts @@ -72,21 +72,21 @@ function findNextActiveKey( if (extensionId) { // Prefer form to panel -- however, it would be unusual to target an ephemeral form when reshowing the sidebar const extensionForm = state.forms.find( - (x) => x.extensionId === extensionId, + (x) => x.componentRef.extensionId === extensionId, ); if (extensionForm) { return eventKeyForEntry(extensionForm); } const extensionTemporaryPanel = state.temporaryPanels.find( - (x) => x.extensionId === extensionId, + (x) => x.componentRef.extensionId === extensionId, ); if (extensionTemporaryPanel) { return eventKeyForEntry(extensionTemporaryPanel); } const extensionPanel = state.panels.find( - (x) => x.extensionId === extensionId, + (x) => x.componentRef.extensionId === extensionId, ); if (extensionPanel) { return eventKeyForEntry(extensionPanel); @@ -96,7 +96,10 @@ function findNextActiveKey( // Try matching on panel heading if (panelHeading) { const extensionPanel = state.panels - .filter((x) => blueprintId == null || x.blueprintId === blueprintId) + .filter( + (x) => + blueprintId == null || x.componentRef.blueprintId === blueprintId, + ) .find((x) => x.heading === panelHeading); if (extensionPanel) { return eventKeyForEntry(extensionPanel); @@ -106,7 +109,7 @@ function findNextActiveKey( // Try matching on blueprint if (blueprintId) { const blueprintPanel = state.panels.find( - (x) => x.blueprintId === blueprintId, + (x) => x.componentRef.blueprintId === blueprintId, ); if (blueprintPanel) { return eventKeyForEntry(blueprintPanel); @@ -130,20 +133,20 @@ export function fixActiveTabOnRemove( const panels = [...state.forms, ...state.panels, ...state.temporaryPanels]; const matchingExtension = panels.find( - ({ extensionId }) => + ({ componentRef }) => "extensionId" in removedEntry && - extensionId === removedEntry.extensionId, + componentRef.extensionId === removedEntry.extensionId, ); if (matchingExtension) { state.activeKey = eventKeyForEntry(matchingExtension); } else { const matchingMod = panels.find( - ({ blueprintId }) => + ({ componentRef }) => "blueprintId" in removedEntry && // Need to check for removedEntry.blueprintId to avoid switching between ModComponentBases that don't have blueprint ids - blueprintId === removedEntry.blueprintId && - blueprintId, + componentRef.blueprintId === removedEntry.blueprintId && + componentRef.blueprintId, ); if (matchingMod) { @@ -297,14 +300,16 @@ const sidebarSlice = createSlice({ (oldPanel) => (oldPanel.isUnavailable || oldPanel.isConnecting) && !action.payload.panels.some( - (newPanel) => newPanel.extensionId === oldPanel.extensionId, + (newPanel) => + newPanel.componentRef.extensionId === + oldPanel.componentRef.extensionId, ), ); // For now, pick an arbitrary order that's stable. There's no guarantees on which order panels are registered state.panels = sortBy( [...oldPanels, ...castDraft(action.payload.panels)], - (panel) => panel.extensionId, + (panel) => panel.componentRef.extensionId, ); // Try fulfilling the pendingActivePanel request diff --git a/src/store/sidebar/thunks/addFormPanel.ts b/src/store/sidebar/thunks/addFormPanel.ts index 7019330afd..95d10fece2 100644 --- a/src/store/sidebar/thunks/addFormPanel.ts +++ b/src/store/sidebar/thunks/addFormPanel.ts @@ -41,13 +41,14 @@ const addFormPanel = createAsyncThunk< return; } - const [thisExtensionForms, otherForms] = partition( + const [thisModComponentForms, otherForms] = partition( forms, - ({ extensionId }) => extensionId === form.extensionId, + ({ componentRef }) => + componentRef.extensionId === form.componentRef.extensionId, ); // The UUID must be fetched synchronously to ensure the `form` Proxy element doesn't expire - await cancelPreexistingForms(thisExtensionForms.map((form) => form.nonce)); + await cancelPreexistingForms(thisModComponentForms.map((form) => form.nonce)); return { forms: [ diff --git a/src/store/sidebar/thunks/addTemporaryPanel.ts b/src/store/sidebar/thunks/addTemporaryPanel.ts index 99055a5dfe..a931bb933d 100644 --- a/src/store/sidebar/thunks/addTemporaryPanel.ts +++ b/src/store/sidebar/thunks/addTemporaryPanel.ts @@ -40,7 +40,7 @@ const addTemporaryPanel = createAsyncThunk< const [existingExtensionTemporaryPanels, otherTemporaryPanels] = partition( temporaryPanels, - (x) => x.extensionId === panel.extensionId, + (x) => x.componentRef.extensionId === panel.componentRef.extensionId, ); // Cancel all panels for the extension, except if there's a placeholder that was added in setInitialPanels diff --git a/src/testUtils/factories/modComponentFactories.ts b/src/testUtils/factories/modComponentFactories.ts index 7ba26254d2..d8ece2f8c0 100644 --- a/src/testUtils/factories/modComponentFactories.ts +++ b/src/testUtils/factories/modComponentFactories.ts @@ -19,9 +19,11 @@ import { type Config, define, extend } from "cooky-cutter"; import { type ActivatedModComponent, type ModComponentBase, + type ModComponentRef, type ModMetadata, } from "@/types/modComponentTypes"; import { + registryIdFactory, timestampFactory, uuidSequence, } from "@/testUtils/factories/stringFactories"; @@ -33,6 +35,12 @@ import { metadataFactory } from "@/testUtils/factories/metadataFactory"; import { type StandaloneModDefinition } from "@/types/contract"; import { type Metadata, DefinitionKinds } from "@/types/registryTypes"; +export const modComponentRefFactory = define({ + extensionId: uuidSequence, + blueprintId: registryIdFactory, + extensionPointId: registryIdFactory, +}); + export const modMetadataFactory = extend( metadataFactory, { diff --git a/src/testUtils/factories/sidebarEntryFactories.ts b/src/testUtils/factories/sidebarEntryFactories.ts index 9d3e425594..37ebc1cb16 100644 --- a/src/testUtils/factories/sidebarEntryFactories.ts +++ b/src/testUtils/factories/sidebarEntryFactories.ts @@ -28,6 +28,7 @@ import { import { validateRegistryId } from "@/types/helpers"; import { uuidSequence } from "@/testUtils/factories/stringFactories"; import { type FormDefinition } from "@/platform/forms/formTypes"; +import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactories"; const activateModPanelEntryFactory = define({ type: "activateMods", @@ -55,29 +56,22 @@ const formDefinitionFactory = define({ }); export const formEntryFactory = define({ type: "form", - extensionId: uuidSequence, - blueprintId: (n: number) => - validateRegistryId(`@test/form-panel-recipe-test-${n}`), + componentRef: modComponentRefFactory, nonce: uuidSequence, form: formDefinitionFactory, }); const temporaryPanelEntryFactory = define({ type: "temporaryPanel", - extensionId: uuidSequence, - blueprintId: null, + componentRef: modComponentRefFactory, heading: (n: number) => `Temporary Panel Test ${n}`, payload: null, nonce: uuidSequence, }); const panelEntryFactory = define({ type: "panel", - extensionId: uuidSequence, - blueprintId: (n: number) => - validateRegistryId(`@test/panel-recipe-test-${n}`), + componentRef: modComponentRefFactory, heading: (n: number) => `Panel Test ${n}`, payload: null, - extensionPointId: (n: number) => - validateRegistryId(`@test/panel-extension-point-test-${n}`), }); export function sidebarEntryFactory( type: "panel", diff --git a/src/types/modComponentTypes.ts b/src/types/modComponentTypes.ts index dfe812feed..e2b2a8b807 100644 --- a/src/types/modComponentTypes.ts +++ b/src/types/modComponentTypes.ts @@ -275,7 +275,9 @@ export type HydratedModComponent = }; /** - * A reference to an ModComponentBase. + * A reference to a ModComponentBase. + * + * @see ModComponentBase */ export type ModComponentRef = { /** @@ -284,12 +286,12 @@ export type ModComponentRef = { extensionId: UUID; /** - * Registry id of the StarterBrick. + * Mod the ModComponent is from, or nullish for a standalone ModComponent. */ - extensionPointId: RegistryId; + blueprintId: Nullishable; /** - * Mod the ModComponent is from. + * Registry id of the StarterBrick. */ - blueprintId: Nullishable; + extensionPointId: RegistryId; }; diff --git a/src/types/sidebarTypes.ts b/src/types/sidebarTypes.ts index 144cbe8ab0..6fb66f4537 100644 --- a/src/types/sidebarTypes.ts +++ b/src/types/sidebarTypes.ts @@ -28,7 +28,8 @@ import { type ModComponentState } from "@/store/extensionsTypes"; import { isObject } from "@/utils/objectUtils"; import { type RunMetadata } from "@/types/runtimeTypes"; import type { ModActivationConfig } from "@/types/modTypes"; -import { type Nullishable } from "@/utils/nullishUtils"; +import type { Nullishable } from "@/utils/nullishUtils"; +import type { ModComponentRef } from "@/types/modComponentTypes"; /** * Entry types supported by the sidebar. @@ -138,19 +139,13 @@ type BasePanelEntry = { */ export type BaseModComponentPanelEntry = BasePanelEntry & { /** - * The id of the ModComponent that added the panel - */ - extensionId: UUID; - /** - * The blueprint associated with the ModComponent that added the panel. + * Reference to the ModComponent that added the panel. * - * Used to: - * - Give preference to blueprint side panels when using the "Show Sidebar" brick. - * - Pass to the panel for actions that require the blueprint id, e.g., Get Page State, Set Page State, etc. - * - * @since 1.6.5 + * The mod is used to: + * - Give preference to mod side panels when using the "Show Sidebar" brick. + * - Pass to the panel for actions that require the mod id, e.g., Get Page State, Set Page State, etc. */ - blueprintId: Nullishable; + componentRef: ModComponentRef; /** * Heading for tab name in the sidebar */ @@ -171,7 +166,7 @@ export type BaseModComponentPanelEntry = BasePanelEntry & { export function isBaseModComponentPanelEntry( panel: unknown, ): panel is BaseModComponentPanelEntry { - return (panel as BaseModComponentPanelEntry)?.extensionId != null; + return (panel as BaseModComponentPanelEntry)?.componentRef != null; } /** @@ -180,11 +175,6 @@ export function isBaseModComponentPanelEntry( */ export type PanelEntry = BaseModComponentPanelEntry & { type: "panel"; - /** - * The sidebar extension point - * @see SidebarStarterBrickABC - */ - extensionPointId: RegistryId; }; export function isPanelEntry(panel: unknown): panel is PanelEntry { @@ -219,20 +209,14 @@ export function isTemporaryPanelEntry( */ export type FormPanelEntry = BasePanelEntry & { type: "form"; - /** - * The extension that created the form - */ - extensionId: UUID; - /** - * The blueprint of the extension panel to show - * - * @since 1.7.33 - */ - blueprintId?: RegistryId; /** * Unique identifier for the form instance. Used to correlate form submission/cancellation. */ nonce: UUID; + /** + * The mod component that created the form. + */ + componentRef: ModComponentRef; /** * The form schema and configuration */ From b56aec9bbda3c481087f71297cc3bdf2bdd11dd8 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 10:59:56 -0400 Subject: [PATCH 02/13] Eliminate compiler warnings --- .../ephemeralForm/formTransformer.ts | 17 +++++++----- .../temporaryInfo/DisplayTemporaryInfo.ts | 3 ++- .../temporaryInfo/EphemeralPanel.tsx | 4 +-- src/contentScript/ephemeralForm.ts | 6 ++--- src/contentScript/ephemeralPanel.ts | 10 +++---- .../pageEditor/runRendererBrick.ts | 16 ++++++------ src/contentScript/sidebarController.tsx | 22 +++++++++------- .../modComponentFormStateAdapter.ts | 5 ++-- .../tabs/effect/useDocumentPreviewRunBlock.ts | 21 ++++++++++++--- src/platform/forms/formController.ts | 13 +++++----- src/platform/panels/panelController.ts | 6 ++--- src/sidebar/Tabs.tsx | 16 ++++++------ src/sidebar/TemporaryPanelTabPane.tsx | 8 +++--- .../modLauncher/ActiveSidebarModsList.tsx | 2 +- src/sidebar/sidebarSelectors.ts | 4 +-- src/store/sidebar/eventKeyUtils.tsx | 2 +- src/store/sidebar/sidebarSlice.test.ts | 6 ++--- src/store/sidebar/sidebarSlice.ts | 26 +++++++++---------- src/store/sidebar/thunks/addFormPanel.ts | 4 +-- src/store/sidebar/thunks/addTemporaryPanel.ts | 2 +- .../factories/sidebarEntryFactories.ts | 6 ++--- src/types/modComponentTypes.ts | 5 ++-- src/types/sidebarTypes.ts | 8 +++--- 23 files changed, 118 insertions(+), 94 deletions(-) diff --git a/src/bricks/transformers/ephemeralForm/formTransformer.ts b/src/bricks/transformers/ephemeralForm/formTransformer.ts index 9a2ccf494b..034714436a 100644 --- a/src/bricks/transformers/ephemeralForm/formTransformer.ts +++ b/src/bricks/transformers/ephemeralForm/formTransformer.ts @@ -23,6 +23,7 @@ import { type BrickConfig } from "@/bricks/types"; import { type FormDefinition } from "@/platform/forms/formTypes"; import { isExpression } from "@/utils/expressionUtils"; import type { PlatformCapability } from "@/platform/capabilities"; +import { assertNotNullish } from "@/utils/nullishUtils"; export const TEMPORARY_FORM_SCHEMA: Schema = { type: "object", @@ -135,15 +136,19 @@ export class FormTransformer extends TransformerABC { controller.abort(); }); - if (logger.context.extensionId == null) { - throw new Error(`${this.name} must be run in a mod context`); - } + const { extensionId, blueprintId, extensionPointId } = logger.context; + + assertNotNullish(extensionId, `${this.name} must be run in a mod context`); + assertNotNullish( + extensionPointId, + `${this.name} must be run in a starter brick context`, + ); try { return await platform.form(formDefinition, controller, { - extensionId: logger.context.extensionId, - blueprintId: logger.context.blueprintId, - extensionPointId: logger.context.extensionPointId, + extensionId, + blueprintId, + extensionPointId, }); } finally { controller.abort(); diff --git a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts index e236ad84e8..57b4d37769 100644 --- a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts +++ b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts @@ -114,6 +114,7 @@ class DisplayTemporaryInfo extends TransformerABC { const target = isRootAware ? root : document; assumeNotNullish_UNSAFE(extensionId); + assumeNotNullish_UNSAFE(extensionPointId); // XXX: blueprintId can actually be nullish if not running on the context of a mod. But assume it's non-nullish // for passing to the panel for now. The panel can gracefully handle nullish blueprintId. assumeNotNullish_UNSAFE(blueprintId); @@ -123,7 +124,7 @@ class DisplayTemporaryInfo extends TransformerABC { const panelEntryMetadata: TemporaryPanelEntryMetadata = { heading: title, - componentRef: { + modComponentRef: { extensionId, blueprintId, extensionPointId, diff --git a/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx b/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx index a132f84123..c8b26f8d40 100644 --- a/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx +++ b/src/bricks/transformers/temporaryInfo/EphemeralPanel.tsx @@ -174,7 +174,7 @@ const EphemeralPanel: React.FC = () => { { resolveTemporaryPanel(target, panelNonce, action); }} @@ -216,7 +216,7 @@ const EphemeralPanel: React.FC = () => { { resolveTemporaryPanel(target, panelNonce, action); }} diff --git a/src/contentScript/ephemeralForm.ts b/src/contentScript/ephemeralForm.ts index f55ae2eb1e..1c73b5dc7d 100644 --- a/src/contentScript/ephemeralForm.ts +++ b/src/contentScript/ephemeralForm.ts @@ -54,7 +54,7 @@ export async function createFrameSource( export async function ephemeralForm( definition: FormDefinition, controller: AbortController, - componentRef: ModComponentRef, + modComponentRef: ModComponentRef, ): Promise { expectContext("contentScript"); @@ -71,8 +71,8 @@ export async function ephemeralForm( // Pre-registering the form also allows the sidebar to know a form will be shown in computing the default // tab to show during sidebar initialization. const formPromise = registerForm({ - componentRef, nonce: formNonce, + modComponentRef, definition, }); @@ -83,7 +83,7 @@ export async function ephemeralForm( await showSidebarForm({ nonce: formNonce, form: definition, - componentRef, + modComponentRef, }); // Two-way binding between sidebar and form. Listen for the user (or an action) closing the sidebar diff --git a/src/contentScript/ephemeralPanel.ts b/src/contentScript/ephemeralPanel.ts index 9d82b2868f..d9947b4539 100644 --- a/src/contentScript/ephemeralPanel.ts +++ b/src/contentScript/ephemeralPanel.ts @@ -114,7 +114,7 @@ export async function ephemeralPanel({ registerEmptyTemporaryPanel({ nonce, location, - extensionId: panelEntryMetadata.componentRef.extensionId, + extensionId: panelEntryMetadata.modComponentRef.extensionId, }); await showSidebar(); @@ -125,7 +125,7 @@ export async function ephemeralPanel({ nonce, payload: { key: uuidv4(), - extensionId: panelEntryMetadata.componentRef.extensionId, + extensionId: panelEntryMetadata.modComponentRef.extensionId, loadingMessage: "Loading", }, }); @@ -141,14 +141,14 @@ export async function ephemeralPanel({ // Popover/modal location // Clear existing to remove stale modals/popovers await cancelTemporaryPanelsForExtension( - panelEntryMetadata.componentRef.extensionId, + panelEntryMetadata.modComponentRef.extensionId, ); // Register empty panel for "loading" state registerEmptyTemporaryPanel({ nonce, location, - extensionId: panelEntryMetadata.componentRef.extensionId, + extensionId: panelEntryMetadata.modComponentRef.extensionId, }); // Create a source URL for content that will be loaded in the panel iframe @@ -235,7 +235,7 @@ export async function ephemeralPanel({ nonce, location, entry, - extensionId: entry.componentRef.extensionId, + extensionId: entry.modComponentRef.extensionId, onRegister: onReady, }); return panelAction ?? {}; diff --git a/src/contentScript/pageEditor/runRendererBrick.ts b/src/contentScript/pageEditor/runRendererBrick.ts index 848d796aa6..13522a59ac 100644 --- a/src/contentScript/pageEditor/runRendererBrick.ts +++ b/src/contentScript/pageEditor/runRendererBrick.ts @@ -47,13 +47,13 @@ type Location = "modal" | "panel"; * @see useDocumentPreviewRunBlock */ export async function runRendererBrick({ - componentRef, + modComponentRef, runId, title, args, location, }: { - componentRef: ModComponentRef; + modComponentRef: ModComponentRef; runId: UUID; title: string; args: RunBrickArgs; @@ -63,7 +63,7 @@ export async function runRendererBrick({ let payload: PanelPayload; try { - await runBrickPreview({ ...args, modId: componentRef.blueprintId }); + await runBrickPreview({ ...args, modId: modComponentRef.blueprintId }); // We're expecting a HeadlessModeError (or other error) to be thrown in the line above // noinspection ExceptionCaughtLocallyJS throw new NoRendererError(); @@ -74,14 +74,14 @@ export async function runRendererBrick({ blockId: error.blockId, args: error.args, ctxt: error.ctxt, - extensionId: componentRef.extensionId, + extensionId: modComponentRef.extensionId, runId, }; } else { payload = { key: nonce, error: serializeError(error), - extensionId: componentRef.extensionId, + extensionId: modComponentRef.extensionId, runId, }; } @@ -89,7 +89,7 @@ export async function runRendererBrick({ if (location === "panel") { await showTemporarySidebarPanel({ // Pass component ref id so previous run is cancelled - componentRef, + modComponentRef, nonce, heading: title, payload, @@ -104,9 +104,9 @@ export async function runRendererBrick({ await waitForTemporaryPanel({ nonce, location, - extensionId: componentRef.extensionId, + extensionId: modComponentRef.extensionId, entry: { - componentRef, + modComponentRef, nonce, heading: title, payload, diff --git a/src/contentScript/sidebarController.tsx b/src/contentScript/sidebarController.tsx index c5be277dae..01715fe551 100644 --- a/src/contentScript/sidebarController.tsx +++ b/src/contentScript/sidebarController.tsx @@ -372,7 +372,7 @@ export function removeModComponents(modComponentIds: UUID[]): void { const current = panels.splice(0); panels.push( ...current.filter( - (x) => !modComponentIds.includes(x.componentRef.extensionId), + (x) => !modComponentIds.includes(x.modComponentRef.extensionId), ), ); void renderPanelsIfVisible(); @@ -393,7 +393,7 @@ export function removeStarterBrick( console.debug("sidebarController:removeStarterBrick %s", starterBrickId, { preserveExtensionIds, panels: panels.filter( - (x) => x.componentRef.extensionPointId === starterBrickId, + (x) => x.modComponentRef.extensionPointId === starterBrickId, ), }); @@ -402,8 +402,8 @@ export function removeStarterBrick( panels.push( ...current.filter( (x) => - x.componentRef.extensionPointId !== starterBrickId || - preserveExtensionIds.includes(x.componentRef.extensionId), + x.modComponentRef.extensionPointId !== starterBrickId || + preserveExtensionIds.includes(x.modComponentRef.extensionId), ), ); @@ -418,12 +418,12 @@ export function reservePanels(refs: ModComponentRef[]): void { return; } - const current = new Set(panels.map((x) => x.componentRef.extensionId)); + const current = new Set(panels.map((x) => x.modComponentRef.extensionId)); for (const { extensionId, extensionPointId, blueprintId } of refs) { if (!current.has(extensionId)) { const entry: PanelEntry = { type: "panel", - componentRef: { + modComponentRef: { extensionId, extensionPointId, blueprintId, @@ -448,14 +448,16 @@ export function reservePanels(refs: ModComponentRef[]): void { } export function updateHeading(extensionId: UUID, heading: string): void { - const entry = panels.find((x) => x.componentRef.extensionId === extensionId); + const entry = panels.find( + (x) => x.modComponentRef.extensionId === extensionId, + ); if (entry) { entry.heading = heading; console.debug( "updateHeading: update heading for panel %s for %s", extensionId, - entry.componentRef.extensionPointId, + entry.modComponentRef.extensionPointId, { ...entry }, ); void renderPanelsIfVisible(); @@ -473,7 +475,7 @@ export function upsertPanel( payload: PanelPayload, ): void { const entry = panels.find( - (panel) => panel.componentRef.extensionId === extensionId, + (panel) => panel.modComponentRef.extensionId === extensionId, ); if (entry) { entry.payload = payload; @@ -500,7 +502,7 @@ export function upsertPanel( ); panels.push({ type: "panel", - componentRef: { + modComponentRef: { extensionId, extensionPointId, blueprintId, diff --git a/src/pageEditor/starterBricks/modComponentFormStateAdapter.ts b/src/pageEditor/starterBricks/modComponentFormStateAdapter.ts index 5ff23991b6..ae80516690 100644 --- a/src/pageEditor/starterBricks/modComponentFormStateAdapter.ts +++ b/src/pageEditor/starterBricks/modComponentFormStateAdapter.ts @@ -20,7 +20,7 @@ import { type IconProp } from "@fortawesome/fontawesome-svg-core"; import { type Metadata } from "@/types/registryTypes"; import { type StarterBrickDefinitionLike } from "@/starterBricks/types"; import { type StarterBrickType } from "@/types/starterBrickTypes"; -import type { DraftModComponent } from "@/contentScript/pageEditor/types"; +import { type DraftModComponent } from "@/contentScript/pageEditor/types"; import { type ModComponentBase } from "@/types/modComponentTypes"; import { type Target } from "@/types/messengerTypes"; import { type BaseFormState } from "@/pageEditor/store/editor/baseFormStateTypes"; @@ -120,5 +120,6 @@ export interface ModComponentFormStateAdapter< */ readonly selectModComponent: ( modComponentFormState: TState, - ) => ModComponentBase; + ) => // XXX: refine type to enforce starter brick reference is the registry id and not an inner definition reference + ModComponentBase; } diff --git a/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts b/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts index d2d76ee079..103b772556 100644 --- a/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts +++ b/src/pageEditor/tabs/effect/useDocumentPreviewRunBlock.ts @@ -37,6 +37,9 @@ import { isExpression } from "@/utils/expressionUtils"; import makeIntegrationsContextFromDependencies from "@/integrations/util/makeIntegrationsContextFromDependencies"; import useAsyncState from "@/hooks/useAsyncState"; import { inspectedTab } from "@/pageEditor/context/connection"; +import { ADAPTERS } from "@/pageEditor/starterBricks/adapter"; +import { validateRegistryId } from "@/types/helpers"; +import { assertNotNullish } from "@/utils/nullishUtils"; type Location = "modal" | "panel"; @@ -103,13 +106,16 @@ export default function useDocumentPreviewRunBlock( ): BlockPreviewRunBlock { const [state, dispatch] = useReducer(previewSlice.reducer, initialState); + const formState = useSelector(selectActiveModComponentFormState); + const { + type, uuid: modComponentId, - modMetadata: mod, + modMetadata, apiVersion, integrationDependencies, starterBrick, - } = useSelector(selectActiveModComponentFormState); + } = formState; const { blockConfig: brickConfig } = useSelector( selectActiveModComponentNodeInfo(brickInstanceId), @@ -173,6 +179,12 @@ export default function useDocumentPreviewRunBlock( dispatch(previewSlice.actions.startPreview()); + const adapter = ADAPTERS.get(type); + const starterBrickId = validateRegistryId( + adapter.selectModComponent(formState).extensionPointId, + ); + assertNotNullish(starterBrickId, "Expected starter brick id"); + // If the block is configured to inherit the root element, and the // starter brick is a trigger, try to get the root element from the // starter brick. @@ -193,9 +205,10 @@ export default function useDocumentPreviewRunBlock( await runRendererBrick(inspectedTab, { runId: traceRecord.runId, title, - componentRef: { + modComponentRef: { extensionId: modComponentId, - blueprintId: mod?.id, + blueprintId: modMetadata?.id, + extensionPointId: starterBrickId, }, args: { apiVersion, diff --git a/src/platform/forms/formController.ts b/src/platform/forms/formController.ts index 3e0251ed33..18ac85b25a 100644 --- a/src/platform/forms/formController.ts +++ b/src/platform/forms/formController.ts @@ -26,7 +26,7 @@ export type RegisteredForm = { /** * The Mod Component that created the form. Only 1 form can be registered per Mod Component. */ - componentRef: ModComponentRef; + modComponentRef: ModComponentRef; definition: FormDefinition; registration: DeferredPromise; }; @@ -45,7 +45,7 @@ export function getFormPanelSidebarEntries(): FormPanelEntry[] { .map(([nonce, form]) => ({ type: "form", nonce, - componentRef: form.componentRef, + modComponentRef: form.modComponentRef, form: form.definition, })); } @@ -59,9 +59,9 @@ export function getFormPanelSidebarEntries(): FormPanelEntry[] { export async function registerForm({ nonce, definition, - componentRef, + modComponentRef, }: { - componentRef: ModComponentRef; + modComponentRef: ModComponentRef; nonce: UUID; definition: FormDefinition; }): Promise { @@ -74,7 +74,8 @@ export async function registerForm({ const preexistingForms = [...forms.entries()].filter( ([_, registeredForm]) => - registeredForm.componentRef.extensionId === componentRef.extensionId, + registeredForm.modComponentRef.extensionId === + modComponentRef.extensionId, ); if (preexistingForms.length > 0) { @@ -83,7 +84,7 @@ export async function registerForm({ } forms.set(nonce, { - componentRef, + modComponentRef, definition, registration, }); diff --git a/src/platform/panels/panelController.ts b/src/platform/panels/panelController.ts index b030fc39ab..e4f03df7e6 100644 --- a/src/platform/panels/panelController.ts +++ b/src/platform/panels/panelController.ts @@ -107,8 +107,8 @@ export function updatePanelDefinition( // Panel entry may be undefined if the panel was registered with registerEmptyTemporaryPanel if ( panel.entry && - panel.entry.componentRef.extensionId !== - panelDefinition.componentRef.extensionId + panel.entry.modComponentRef.extensionId !== + panelDefinition.modComponentRef.extensionId ) { throw new Error("extensionId mismatch"); } @@ -205,7 +205,7 @@ function removePanelEntry(panelNonce: UUID): void { const panel = panels.get(panelNonce); if (panel?.entry) { extensionNonces - .get(panel.entry.componentRef.extensionId) + .get(panel.entry.modComponentRef.extensionId) ?.delete(panelNonce); } diff --git a/src/sidebar/Tabs.tsx b/src/sidebar/Tabs.tsx index 65fbcf6626..b1eeaa28ff 100644 --- a/src/sidebar/Tabs.tsx +++ b/src/sidebar/Tabs.tsx @@ -209,7 +209,7 @@ const Tabs: React.FC = () => { > {panels.map((panel) => ( @@ -224,7 +224,7 @@ const Tabs: React.FC = () => { {forms.map((form) => ( @@ -309,15 +309,15 @@ const Tabs: React.FC = () => { // un-submitted form state/scroll position unmountOnExit={false} className={cx("full-height flex-grow", styles.paneOverrides)} - key={panel.componentRef.extensionId} + key={panel.modComponentRef.extensionId} eventKey={eventKeyForEntry(panel)} > { reportEvent(Events.VIEW_ERROR, { panelType: panel.type, - extensionId: panel.componentRef.extensionId, - blueprintId: panel.componentRef.blueprintId, + extensionId: panel.modComponentRef.extensionId, + blueprintId: panel.modComponentRef.blueprintId, }); }} > @@ -335,7 +335,7 @@ const Tabs: React.FC = () => { isRootPanel payload={panel.payload} onAction={permanentSidebarPanelAction} - context={panel.componentRef} + context={panel.modComponentRef} /> @@ -351,8 +351,8 @@ const Tabs: React.FC = () => { onError={() => { reportEvent(Events.VIEW_ERROR, { panelType: form.type, - extensionId: form.componentRef.extensionId, - blueprintId: form.componentRef.blueprintId, + extensionId: form.modComponentRef.extensionId, + blueprintId: form.modComponentRef.blueprintId, }); }} > diff --git a/src/sidebar/TemporaryPanelTabPane.tsx b/src/sidebar/TemporaryPanelTabPane.tsx index af64ad9278..02ffee73b2 100644 --- a/src/sidebar/TemporaryPanelTabPane.tsx +++ b/src/sidebar/TemporaryPanelTabPane.tsx @@ -50,7 +50,7 @@ export const TemporaryPanelTabPane: React.FC<{ }, [dispatch, panel.nonce], ); - const { type, componentRef, payload } = panel; + const { type, modComponentRef, payload } = panel; return ( { reportEvent(Events.VIEW_ERROR, { panelType: type, - extensionId: componentRef.extensionId, - blueprintId: componentRef.blueprintId, + extensionId: modComponentRef.extensionId, + blueprintId: modComponentRef.blueprintId, }); }} > @@ -74,7 +74,7 @@ export const TemporaryPanelTabPane: React.FC<{ diff --git a/src/sidebar/modLauncher/ActiveSidebarModsList.tsx b/src/sidebar/modLauncher/ActiveSidebarModsList.tsx index b68d971316..86c1b792a6 100644 --- a/src/sidebar/modLauncher/ActiveSidebarModsList.tsx +++ b/src/sidebar/modLauncher/ActiveSidebarModsList.tsx @@ -124,7 +124,7 @@ export const ActiveSidebarModsList: React.FunctionComponent = () => { tableInstance.prepareRow(row); return ( ); diff --git a/src/sidebar/sidebarSelectors.ts b/src/sidebar/sidebarSelectors.ts index 30774b08ef..50ebb0b406 100644 --- a/src/sidebar/sidebarSelectors.ts +++ b/src/sidebar/sidebarSelectors.ts @@ -75,7 +75,7 @@ const extensionForEventKeySelector = createSelector( } return extensions.find( - (extension) => extension.id === sidebarEntry.componentRef.extensionId, + (extension) => extension.id === sidebarEntry.modComponentRef.extensionId, ); }, ); @@ -100,7 +100,7 @@ export const selectExtensionFromEventKey = } return extensions.find( - (extension) => extension.id === sidebarEntry.componentRef.extensionId, + (extension) => extension.id === sidebarEntry.modComponentRef.extensionId, ); }; diff --git a/src/store/sidebar/eventKeyUtils.tsx b/src/store/sidebar/eventKeyUtils.tsx index 49a1647413..93f5f9c976 100644 --- a/src/store/sidebar/eventKeyUtils.tsx +++ b/src/store/sidebar/eventKeyUtils.tsx @@ -44,7 +44,7 @@ function eventKeyForEntry(entry: Nullishable): string | null { } if (isPanelEntry(entry)) { - return getEventKeyForPanel(entry.componentRef.extensionId); + return getEventKeyForPanel(entry.modComponentRef.extensionId); } if (isStaticPanelEntry(entry)) { diff --git a/src/store/sidebar/sidebarSlice.test.ts b/src/store/sidebar/sidebarSlice.test.ts index 279495e14b..9324551bdd 100644 --- a/src/store/sidebar/sidebarSlice.test.ts +++ b/src/store/sidebar/sidebarSlice.test.ts @@ -101,7 +101,7 @@ describe("sidebarSlice.addTemporaryPanel", () => { const existingPanel = sidebarEntryFactory("temporaryPanel"); const otherExistingPanel = sidebarEntryFactory("temporaryPanel"); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: existingPanel.componentRef.extensionId, + extensionId: existingPanel.modComponentRef.extensionId, }); const initialState: SidebarState = { @@ -198,7 +198,7 @@ describe("removeTemporaryPanel", () => { extensionId: uuidv4(), }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: originalPanel.componentRef.extensionId, + extensionId: originalPanel.modComponentRef.extensionId, }); const initialState: SidebarState = { @@ -542,7 +542,7 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { blueprintId: modId, }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: originalPanel.componentRef.extensionId, + extensionId: originalPanel.modComponentRef.extensionId, blueprintId: modId, }); diff --git a/src/store/sidebar/sidebarSlice.ts b/src/store/sidebar/sidebarSlice.ts index 10f8f19152..a83308005a 100644 --- a/src/store/sidebar/sidebarSlice.ts +++ b/src/store/sidebar/sidebarSlice.ts @@ -72,21 +72,21 @@ function findNextActiveKey( if (extensionId) { // Prefer form to panel -- however, it would be unusual to target an ephemeral form when reshowing the sidebar const extensionForm = state.forms.find( - (x) => x.componentRef.extensionId === extensionId, + (x) => x.modComponentRef.extensionId === extensionId, ); if (extensionForm) { return eventKeyForEntry(extensionForm); } const extensionTemporaryPanel = state.temporaryPanels.find( - (x) => x.componentRef.extensionId === extensionId, + (x) => x.modComponentRef.extensionId === extensionId, ); if (extensionTemporaryPanel) { return eventKeyForEntry(extensionTemporaryPanel); } const extensionPanel = state.panels.find( - (x) => x.componentRef.extensionId === extensionId, + (x) => x.modComponentRef.extensionId === extensionId, ); if (extensionPanel) { return eventKeyForEntry(extensionPanel); @@ -98,7 +98,7 @@ function findNextActiveKey( const extensionPanel = state.panels .filter( (x) => - blueprintId == null || x.componentRef.blueprintId === blueprintId, + blueprintId == null || x.modComponentRef.blueprintId === blueprintId, ) .find((x) => x.heading === panelHeading); if (extensionPanel) { @@ -109,7 +109,7 @@ function findNextActiveKey( // Try matching on blueprint if (blueprintId) { const blueprintPanel = state.panels.find( - (x) => x.componentRef.blueprintId === blueprintId, + (x) => x.modComponentRef.blueprintId === blueprintId, ); if (blueprintPanel) { return eventKeyForEntry(blueprintPanel); @@ -133,20 +133,20 @@ export function fixActiveTabOnRemove( const panels = [...state.forms, ...state.panels, ...state.temporaryPanels]; const matchingExtension = panels.find( - ({ componentRef }) => + ({ modComponentRef: { extensionId } }) => "extensionId" in removedEntry && - componentRef.extensionId === removedEntry.extensionId, + extensionId === removedEntry.extensionId, ); if (matchingExtension) { state.activeKey = eventKeyForEntry(matchingExtension); } else { const matchingMod = panels.find( - ({ componentRef }) => + ({ modComponentRef: { blueprintId } }) => "blueprintId" in removedEntry && // Need to check for removedEntry.blueprintId to avoid switching between ModComponentBases that don't have blueprint ids - componentRef.blueprintId === removedEntry.blueprintId && - componentRef.blueprintId, + blueprintId === removedEntry.blueprintId && + blueprintId, ); if (matchingMod) { @@ -301,15 +301,15 @@ const sidebarSlice = createSlice({ (oldPanel.isUnavailable || oldPanel.isConnecting) && !action.payload.panels.some( (newPanel) => - newPanel.componentRef.extensionId === - oldPanel.componentRef.extensionId, + newPanel.modComponentRef.extensionId === + oldPanel.modComponentRef.extensionId, ), ); // For now, pick an arbitrary order that's stable. There's no guarantees on which order panels are registered state.panels = sortBy( [...oldPanels, ...castDraft(action.payload.panels)], - (panel) => panel.componentRef.extensionId, + (panel) => panel.modComponentRef.extensionId, ); // Try fulfilling the pendingActivePanel request diff --git a/src/store/sidebar/thunks/addFormPanel.ts b/src/store/sidebar/thunks/addFormPanel.ts index 95d10fece2..f9b744a452 100644 --- a/src/store/sidebar/thunks/addFormPanel.ts +++ b/src/store/sidebar/thunks/addFormPanel.ts @@ -43,8 +43,8 @@ const addFormPanel = createAsyncThunk< const [thisModComponentForms, otherForms] = partition( forms, - ({ componentRef }) => - componentRef.extensionId === form.componentRef.extensionId, + ({ modComponentRef }) => + modComponentRef.extensionId === form.modComponentRef.extensionId, ); // The UUID must be fetched synchronously to ensure the `form` Proxy element doesn't expire diff --git a/src/store/sidebar/thunks/addTemporaryPanel.ts b/src/store/sidebar/thunks/addTemporaryPanel.ts index a931bb933d..92807a71f7 100644 --- a/src/store/sidebar/thunks/addTemporaryPanel.ts +++ b/src/store/sidebar/thunks/addTemporaryPanel.ts @@ -40,7 +40,7 @@ const addTemporaryPanel = createAsyncThunk< const [existingExtensionTemporaryPanels, otherTemporaryPanels] = partition( temporaryPanels, - (x) => x.componentRef.extensionId === panel.componentRef.extensionId, + (x) => x.modComponentRef.extensionId === panel.modComponentRef.extensionId, ); // Cancel all panels for the extension, except if there's a placeholder that was added in setInitialPanels diff --git a/src/testUtils/factories/sidebarEntryFactories.ts b/src/testUtils/factories/sidebarEntryFactories.ts index 37ebc1cb16..0bec9eaac8 100644 --- a/src/testUtils/factories/sidebarEntryFactories.ts +++ b/src/testUtils/factories/sidebarEntryFactories.ts @@ -56,20 +56,20 @@ const formDefinitionFactory = define({ }); export const formEntryFactory = define({ type: "form", - componentRef: modComponentRefFactory, + modComponentRef: modComponentRefFactory, nonce: uuidSequence, form: formDefinitionFactory, }); const temporaryPanelEntryFactory = define({ type: "temporaryPanel", - componentRef: modComponentRefFactory, + modComponentRef: modComponentRefFactory, heading: (n: number) => `Temporary Panel Test ${n}`, payload: null, nonce: uuidSequence, }); const panelEntryFactory = define({ type: "panel", - componentRef: modComponentRefFactory, + modComponentRef: modComponentRefFactory, heading: (n: number) => `Panel Test ${n}`, payload: null, }); diff --git a/src/types/modComponentTypes.ts b/src/types/modComponentTypes.ts index e2b2a8b807..3aef40526b 100644 --- a/src/types/modComponentTypes.ts +++ b/src/types/modComponentTypes.ts @@ -275,8 +275,7 @@ export type HydratedModComponent = }; /** - * A reference to a ModComponentBase. - * + * A reference to a ModComponentBase, including the associated mod and starter brick. * @see ModComponentBase */ export type ModComponentRef = { @@ -291,7 +290,7 @@ export type ModComponentRef = { blueprintId: Nullishable; /** - * Registry id of the StarterBrick. + * Registry id of the mod component's StarterBrick. */ extensionPointId: RegistryId; }; diff --git a/src/types/sidebarTypes.ts b/src/types/sidebarTypes.ts index 6fb66f4537..7db3b67ba2 100644 --- a/src/types/sidebarTypes.ts +++ b/src/types/sidebarTypes.ts @@ -144,8 +144,10 @@ export type BaseModComponentPanelEntry = BasePanelEntry & { * The mod is used to: * - Give preference to mod side panels when using the "Show Sidebar" brick. * - Pass to the panel for actions that require the mod id, e.g., Get Page State, Set Page State, etc. + * + * @since 2.0.5 refactored to use a single property for the mod component id and the containing mod id */ - componentRef: ModComponentRef; + modComponentRef: ModComponentRef; /** * Heading for tab name in the sidebar */ @@ -166,7 +168,7 @@ export type BaseModComponentPanelEntry = BasePanelEntry & { export function isBaseModComponentPanelEntry( panel: unknown, ): panel is BaseModComponentPanelEntry { - return (panel as BaseModComponentPanelEntry)?.componentRef != null; + return (panel as BaseModComponentPanelEntry)?.modComponentRef != null; } /** @@ -216,7 +218,7 @@ export type FormPanelEntry = BasePanelEntry & { /** * The mod component that created the form. */ - componentRef: ModComponentRef; + modComponentRef: ModComponentRef; /** * The form schema and configuration */ From 09b2a67db76e5bcb1a28838bf9b33101521a82fc Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 11:47:38 -0400 Subject: [PATCH 03/13] Fix broken tests --- .../DisplayTemporaryInfo.test.ts | 23 ++--- .../sidebar/sidebarStarterBrick.test.ts | 4 +- src/store/sidebar/eventKeyUtils.test.ts | 24 +++-- src/store/sidebar/sidebarSlice.test.ts | 94 ++++++++++--------- src/store/sidebar/sidebarSlice.ts | 30 +++--- .../factories/sidebarEntryFactories.ts | 1 - src/types/modComponentTypes.ts | 3 +- 7 files changed, 98 insertions(+), 81 deletions(-) diff --git a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts index 7b1a7880b5..a3e4726223 100644 --- a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts +++ b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts @@ -48,7 +48,6 @@ import { uuidv4 } from "@/types/helpers"; import ConsoleLogger from "@/utils/ConsoleLogger"; import { tick } from "@/starterBricks/starterBrickTestUtils"; import pDefer from "p-defer"; -import { registryIdFactory } from "@/testUtils/factories/stringFactories"; import { type RendererErrorPayload } from "@/types/rendererTypes"; import { MergeStrategies, @@ -60,6 +59,7 @@ import { unary } from "lodash"; import { toExpression } from "@/utils/expressionUtils"; import { showModal } from "@/contentScript/modalDom"; import { isLoadedInIframe } from "@/utils/iframeUtils"; +import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactories"; jest.mock("@/contentScript/modalDom"); jest.mock("@/contentScript/sidebarController"); @@ -92,8 +92,7 @@ describe("DisplayTemporaryInfo", () => { }); test("it returns run payload for sidebar panel", async () => { - const extensionId = uuidv4(); - const blueprintId = registryIdFactory(); + const modComponentRef = modComponentRefFactory(); const config = getExampleBrickConfig(renderer.id); const pipeline = { @@ -106,13 +105,12 @@ describe("DisplayTemporaryInfo", () => { await reducePipeline(pipeline, simpleInput({}), { ...testOptions("v3"), - logger: new ConsoleLogger({ extensionId, blueprintId }), + logger: new ConsoleLogger(modComponentRef), }); // Show function will be called with a "loading" payload expect(showTemporarySidebarPanel).toHaveBeenCalledExactlyOnceWith({ - blueprintId, - extensionId, + modComponentRef, nonce: expect.toBeString(), heading: expect.toBeString(), payload: expect.objectContaining({ @@ -122,8 +120,7 @@ describe("DisplayTemporaryInfo", () => { // Panel will be updated when the real payload is ready expect(updatePanelDefinition).toHaveBeenCalledExactlyOnceWith({ - blueprintId, - extensionId, + modComponentRef, nonce: expect.toBeString(), heading: expect.toBeString(), payload: expect.objectContaining({ @@ -179,13 +176,11 @@ describe("DisplayTemporaryInfo", () => { }, }; - const extensionId = uuidv4(); + const modComponentRef = modComponentRefFactory(); const options = { ...testOptions("v3"), - logger: new ConsoleLogger({ - extensionId, - }), + logger: new ConsoleLogger(modComponentRef), }; await reducePipeline(pipeline, simpleInput({}), options); @@ -195,10 +190,10 @@ describe("DisplayTemporaryInfo", () => { expect(waitForTemporaryPanel).toHaveBeenCalledWith({ nonce: expect.toBeString(), - extensionId, + extensionId: modComponentRef.extensionId, location: "modal", entry: expect.objectContaining({ - extensionId, + modComponentRef, heading: "Test Temp Panel", nonce: expect.toBeString(), payload: expect.toBeObject(), diff --git a/src/starterBricks/sidebar/sidebarStarterBrick.test.ts b/src/starterBricks/sidebar/sidebarStarterBrick.test.ts index 3b391bac71..b438e25be1 100644 --- a/src/starterBricks/sidebar/sidebarStarterBrick.test.ts +++ b/src/starterBricks/sidebar/sidebarStarterBrick.test.ts @@ -112,7 +112,9 @@ describe("sidebarExtension", () => { forms: [], panels: [ expect.objectContaining({ - extensionPointId: starterBrick.id, + modComponentRef: expect.objectContaining({ + extensionPointId: starterBrick.id, + }), }), ], temporaryPanels: [], diff --git a/src/store/sidebar/eventKeyUtils.test.ts b/src/store/sidebar/eventKeyUtils.test.ts index fdba5ae33e..7cc9dc6526 100644 --- a/src/store/sidebar/eventKeyUtils.test.ts +++ b/src/store/sidebar/eventKeyUtils.test.ts @@ -23,6 +23,7 @@ import { uuidv4, validateRegistryId } from "@/types/helpers"; import { type SidebarEntries, type SidebarState } from "@/types/sidebarTypes"; import { sidebarEntryFactory } from "@/testUtils/factories/sidebarEntryFactories"; import { MOD_LAUNCHER } from "@/store/sidebar/constants"; +import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactories"; describe("defaultEventKey", () => { it("returns null no content", () => { @@ -147,9 +148,11 @@ describe("eventKeyForEntry", () => { expect(eventKeyForEntry(value)).toBeNull(); }); - it("uses recipeId for activateRecipe", () => { - const recipeId = validateRegistryId("@test/test-recipe"); - const entry = sidebarEntryFactory("activateMods", { recipeId }); + it("uses modId for activateRecipe", () => { + const modId = validateRegistryId("@test/test-recipe"); + const entry = sidebarEntryFactory("activateMods", { + modComponentRef: modComponentRefFactory({ blueprintId: modId }), + }); // Main part is an object hash of the mod ids expect(eventKeyForEntry(entry)).toStartWith("activate-"); }); @@ -158,8 +161,10 @@ describe("eventKeyForEntry", () => { const extensionId = uuidv4(); const extensionPointId = validateRegistryId("@test/test-starter-brick"); const entry = sidebarEntryFactory("panel", { - extensionId, - extensionPointId, + modComponentRef: modComponentRefFactory({ + extensionId, + extensionPointId, + }), }); expect(eventKeyForEntry(entry)).toBe(`panel-${extensionId}`); }); @@ -168,12 +173,17 @@ describe("eventKeyForEntry", () => { const extensionId = uuidv4(); const nonce = uuidv4(); - const formEntry = sidebarEntryFactory("form", { extensionId, nonce }); + const formEntry = sidebarEntryFactory("form", { + nonce, + modComponentRef: modComponentRefFactory({ + extensionId, + }), + }); expect(eventKeyForEntry(formEntry)).toBe(`form-${nonce}`); const temporaryPanelEntry = sidebarEntryFactory("temporaryPanel", { - extensionId, nonce, + modComponentRef: modComponentRefFactory({ extensionId }), }); expect(eventKeyForEntry(temporaryPanelEntry)).toBe( `temporaryPanel-${nonce}`, diff --git a/src/store/sidebar/sidebarSlice.test.ts b/src/store/sidebar/sidebarSlice.test.ts index 9324551bdd..820e35382d 100644 --- a/src/store/sidebar/sidebarSlice.test.ts +++ b/src/store/sidebar/sidebarSlice.test.ts @@ -16,7 +16,7 @@ */ import sidebarSlice, { - fixActiveTabOnRemove, + fixActiveTabOnRemoveInPlace, } from "@/store/sidebar/sidebarSlice"; import { eventKeyForEntry } from "@/store/sidebar/eventKeyUtils"; import { @@ -34,6 +34,7 @@ import { configureStore } from "@reduxjs/toolkit"; import addFormPanel from "@/store/sidebar/thunks/addFormPanel"; import addTemporaryPanel from "@/store/sidebar/thunks/addTemporaryPanel"; import removeTemporaryPanel from "@/store/sidebar/thunks/removeTemporaryPanel"; +import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactories"; jest.mock("@/sidebar/messenger/api"); jest.mock("@/contentScript/messenger/api"); @@ -101,7 +102,9 @@ describe("sidebarSlice.addTemporaryPanel", () => { const existingPanel = sidebarEntryFactory("temporaryPanel"); const otherExistingPanel = sidebarEntryFactory("temporaryPanel"); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: existingPanel.modComponentRef.extensionId, + modComponentRef: modComponentRefFactory({ + extensionId: existingPanel.modComponentRef.extensionId, + }), }); const initialState: SidebarState = { @@ -190,15 +193,13 @@ describe("removeTemporaryPanel", () => { ); }); - it("sets activeKey to a panel with the same extensionId if it exists", async () => { - const originalPanel = sidebarEntryFactory("panel", { - extensionId: uuidv4(), - }); - const otherExistingPanel = sidebarEntryFactory("form", { - extensionId: uuidv4(), - }); + it("sets activeKey to a panel with the same mod component id if it exists", async () => { + const originalPanel = sidebarEntryFactory("panel"); + const otherExistingPanel = sidebarEntryFactory("form"); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: originalPanel.modComponentRef.extensionId, + modComponentRef: modComponentRefFactory({ + extensionId: originalPanel.modComponentRef.extensionId, + }), }); const initialState: SidebarState = { @@ -210,7 +211,6 @@ describe("removeTemporaryPanel", () => { const store = configureStore({ reducer: { sidebar: sidebarSlice.reducer }, - preloadedState: { sidebar: initialState }, }); @@ -532,18 +532,20 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { it("sets activeKey to the active key of any panel with the same extensionId as the removedEntry if it exists", () => { const modId = validateRegistryId("test/123"); const originalPanel = sidebarEntryFactory("panel", { - componentRef: { - extensionId: uuidv4(), + modComponentRef: modComponentRefFactory({ blueprintId: modId, - }, + }), }); const otherExistingPanel = sidebarEntryFactory("form", { - extensionId: uuidv4(), - blueprintId: modId, + modComponentRef: modComponentRefFactory({ + blueprintId: modId, + }), }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: originalPanel.modComponentRef.extensionId, - blueprintId: modId, + modComponentRef: modComponentRefFactory({ + extensionId: originalPanel.modComponentRef.extensionId, + blueprintId: modId, + }), }); const state = { @@ -556,7 +558,7 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error -- Flaky error // @ts-ignore-error "Type instantiation is excessively deep and possibly infinite" - fixActiveTabOnRemove(state, newPanel); + fixActiveTabOnRemoveInPlace(state, newPanel); expect(state).toStrictEqual({ ...state, @@ -564,19 +566,19 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { }); }); - it("sets activeKey to the active key of any panel with the same modId as the removedEntry if it exists and there is no matching extensionId", () => { + it("sets activeKey to the active key of any panel with the same modId as the removedEntry if it exists and there is no matching component id", () => { const modId = validateRegistryId("test/123"); - const firstPanel = sidebarEntryFactory("panel", { - extensionId: uuidv4(), - }); + const firstPanel = sidebarEntryFactory("panel"); const matchingPanel = sidebarEntryFactory("panel", { - extensionId: uuidv4(), - blueprintId: modId, + modComponentRef: modComponentRefFactory({ + blueprintId: modId, + }), }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: uuidv4(), - blueprintId: modId, + modComponentRef: modComponentRefFactory({ + blueprintId: modId, + }), }); const state = { @@ -586,7 +588,7 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { temporaryPanels: [], } as SidebarState; - fixActiveTabOnRemove(state, newPanel); + fixActiveTabOnRemoveInPlace(state, newPanel); expect(state).toStrictEqual({ ...state, @@ -598,18 +600,26 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { const extensionId = uuidv4(); const originalPanel = sidebarEntryFactory("panel", { - extensionId, + modComponentRef: modComponentRefFactory({ + extensionId, + }), }); const firstFormPanel = sidebarEntryFactory("form", { - extensionId, + modComponentRef: modComponentRefFactory({ + extensionId, + }), }); const nullModId = sidebarEntryFactory("form", { - extensionId: uuidv4(), - blueprintId: null, + modComponentRef: modComponentRefFactory({ + extensionId, + blueprintId: null, + }), }); const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId, - blueprintId: null, + modComponentRef: modComponentRefFactory({ + extensionId, + blueprintId: null, + }), }); const state = { @@ -620,7 +630,7 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { temporaryPanels: [], } as SidebarState; - fixActiveTabOnRemove(state, newPanel); + fixActiveTabOnRemoveInPlace(state, newPanel); expect(state).toStrictEqual({ ...state, @@ -629,15 +639,9 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { }); it("sets activeKey to the defaultEventKey if no panel with the same extensionId as the removedEntry exists", () => { - const originalPanel = sidebarEntryFactory("panel", { - extensionId: uuidv4(), - }); - const otherExistingPanel = sidebarEntryFactory("form", { - extensionId: uuidv4(), - }); - const newPanel = sidebarEntryFactory("temporaryPanel", { - extensionId: uuidv4(), - }); + const originalPanel = sidebarEntryFactory("panel"); + const otherExistingPanel = sidebarEntryFactory("form"); + const newPanel = sidebarEntryFactory("temporaryPanel"); const state = { ...sidebarSlice.getInitialState(), @@ -647,7 +651,7 @@ describe("sidebarSlice.fixActiveTabOnRemove", () => { temporaryPanels: [], } as SidebarState; - fixActiveTabOnRemove(state, newPanel); + fixActiveTabOnRemoveInPlace(state, newPanel); expect(state).toStrictEqual({ ...state, diff --git a/src/store/sidebar/sidebarSlice.ts b/src/store/sidebar/sidebarSlice.ts index a83308005a..8b8b429d35 100644 --- a/src/store/sidebar/sidebarSlice.ts +++ b/src/store/sidebar/sidebarSlice.ts @@ -124,28 +124,34 @@ function findNextActiveKey( return null; } -export function fixActiveTabOnRemove( +/** + * Updates activeKey in place based on a removed entry. Mutates the state object. + */ +export function fixActiveTabOnRemoveInPlace( state: SidebarState, removedEntry: Nullishable, -) { +): void { // Only update the active panel if the panel needs to change if (removedEntry && state.activeKey === eventKeyForEntry(removedEntry)) { const panels = [...state.forms, ...state.panels, ...state.temporaryPanels]; const matchingExtension = panels.find( ({ modComponentRef: { extensionId } }) => - "extensionId" in removedEntry && - extensionId === removedEntry.extensionId, + "modComponentRef" in removedEntry && + extensionId === removedEntry.modComponentRef.extensionId, ); if (matchingExtension) { state.activeKey = eventKeyForEntry(matchingExtension); } else { + // No mod component match, try finding another panel for the mod + const matchingMod = panels.find( ({ modComponentRef: { blueprintId } }) => - "blueprintId" in removedEntry && - // Need to check for removedEntry.blueprintId to avoid switching between ModComponentBases that don't have blueprint ids - blueprintId === removedEntry.blueprintId && + "modComponentRef" in removedEntry && + // Need to check for removedEntry.blueprintId to avoid switching between ModComponentBases that don't have + // an associate mod + blueprintId === removedEntry.modComponentRef.blueprintId && blueprintId, ); @@ -219,7 +225,7 @@ const sidebarSlice = createSlice({ const entry = remove(state.forms, (form) => form.nonce === nonce)[0]; - fixActiveTabOnRemove(state, entry); + fixActiveTabOnRemoveInPlace(state, entry); }, invalidatePanels(state) { for (const panel of state.panels) { @@ -345,7 +351,7 @@ const sidebarSlice = createSlice({ closedTabs[eventKeyForEntry(MOD_LAUNCHER)] = false; } - fixActiveTabOnRemove(state, entry); + fixActiveTabOnRemoveInPlace(state, entry); }, closeTab(state, action: PayloadAction) { state.closedTabs[action.payload] = true; @@ -381,7 +387,7 @@ const sidebarSlice = createSlice({ const { removedEntry, forms } = action.payload; state.forms = castDraft(forms); - fixActiveTabOnRemove(state, removedEntry); + fixActiveTabOnRemoveInPlace(state, removedEntry); } }) .addCase(addTemporaryPanel.fulfilled, (state, action) => { @@ -396,7 +402,7 @@ const sidebarSlice = createSlice({ const { removedEntry, temporaryPanels } = action.payload; state.temporaryPanels = castDraft(temporaryPanels); - fixActiveTabOnRemove(state, removedEntry); + fixActiveTabOnRemoveInPlace(state, removedEntry); } }) .addCase(resolveTemporaryPanel.fulfilled, (state, action) => { @@ -404,7 +410,7 @@ const sidebarSlice = createSlice({ const { resolvedEntry, temporaryPanels } = action.payload; state.temporaryPanels = castDraft(temporaryPanels); - fixActiveTabOnRemove(state, resolvedEntry); + fixActiveTabOnRemoveInPlace(state, resolvedEntry); } }); }, diff --git a/src/testUtils/factories/sidebarEntryFactories.ts b/src/testUtils/factories/sidebarEntryFactories.ts index 0bec9eaac8..7aa6cc1f5f 100644 --- a/src/testUtils/factories/sidebarEntryFactories.ts +++ b/src/testUtils/factories/sidebarEntryFactories.ts @@ -89,7 +89,6 @@ export function sidebarEntryFactory( type: "activateMods", override?: FactoryConfig, ): ModActivationPanelEntry; - export function sidebarEntryFactory( type: "staticPanel", override?: FactoryConfig, diff --git a/src/types/modComponentTypes.ts b/src/types/modComponentTypes.ts index 3aef40526b..f5744846b7 100644 --- a/src/types/modComponentTypes.ts +++ b/src/types/modComponentTypes.ts @@ -275,7 +275,8 @@ export type HydratedModComponent = }; /** - * A reference to a ModComponentBase, including the associated mod and starter brick. + * A reference to a ModComponentBase, including the associated mod and starter brick. Prefer using the mod component's + * UUID directly if information about the mod and/or starter brick are not required. * @see ModComponentBase */ export type ModComponentRef = { From 876a1c243404c19086aea90012ea3d982cc5462b Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 11:53:47 -0400 Subject: [PATCH 04/13] Make sidebarEntryFactory type safe --- .../activateMod/ActivateModPanel.test.tsx | 2 +- src/store/sidebar/eventKeyUtils.test.ts | 4 ++-- .../factories/sidebarEntryFactories.ts | 22 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sidebar/activateMod/ActivateModPanel.test.tsx b/src/sidebar/activateMod/ActivateModPanel.test.tsx index 792388d412..32e79609c1 100644 --- a/src/sidebar/activateMod/ActivateModPanel.test.tsx +++ b/src/sidebar/activateMod/ActivateModPanel.test.tsx @@ -162,7 +162,7 @@ function setupMocksAndRender( appApiMock.onGet().reply(200, []); const entry = sidebarEntryFactory("activateMods", { - modIds: [modDefinition.metadata.id], + mods: [{ modId: modDefinition.metadata.id, initialOptions: {} }], heading: "Activate Mod", }); diff --git a/src/store/sidebar/eventKeyUtils.test.ts b/src/store/sidebar/eventKeyUtils.test.ts index 7cc9dc6526..2141492539 100644 --- a/src/store/sidebar/eventKeyUtils.test.ts +++ b/src/store/sidebar/eventKeyUtils.test.ts @@ -148,10 +148,10 @@ describe("eventKeyForEntry", () => { expect(eventKeyForEntry(value)).toBeNull(); }); - it("uses modId for activateRecipe", () => { + it("uses modId for activateMods", () => { const modId = validateRegistryId("@test/test-recipe"); const entry = sidebarEntryFactory("activateMods", { - modComponentRef: modComponentRefFactory({ blueprintId: modId }), + mods: [{ modId, initialOptions: {} }], }); // Main part is an object hash of the mod ids expect(eventKeyForEntry(entry)).toStartWith("activate-"); diff --git a/src/testUtils/factories/sidebarEntryFactories.ts b/src/testUtils/factories/sidebarEntryFactories.ts index 7aa6cc1f5f..db2705f307 100644 --- a/src/testUtils/factories/sidebarEntryFactories.ts +++ b/src/testUtils/factories/sidebarEntryFactories.ts @@ -73,27 +73,27 @@ const panelEntryFactory = define({ heading: (n: number) => `Panel Test ${n}`, payload: null, }); -export function sidebarEntryFactory( +export function sidebarEntryFactory( type: "panel", - override?: FactoryConfig, + override?: FactoryConfig, ): PanelEntry; -export function sidebarEntryFactory( +export function sidebarEntryFactory( type: "temporaryPanel", - override?: FactoryConfig, + override?: FactoryConfig, ): TemporaryPanelEntry; -export function sidebarEntryFactory( +export function sidebarEntryFactory( type: "form", - override?: FactoryConfig, + override?: FactoryConfig, ): FormPanelEntry; -export function sidebarEntryFactory( +export function sidebarEntryFactory( type: "activateMods", - override?: FactoryConfig, + override?: FactoryConfig, ): ModActivationPanelEntry; -export function sidebarEntryFactory( +export function sidebarEntryFactory( type: "staticPanel", - override?: FactoryConfig, + override?: FactoryConfig, ): StaticPanelEntry; -export function sidebarEntryFactory( +export function sidebarEntryFactory( type: EntryType, override?: FactoryConfig, ): SidebarEntry { From 6fd9e0d3d8af5865dbf097afb072e793ed118b2d Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 12:00:23 -0400 Subject: [PATCH 05/13] Remove lint --- src/platform/platformProtocol.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/platformProtocol.ts b/src/platform/platformProtocol.ts index 61fea60b90..3f8ee83390 100644 --- a/src/platform/platformProtocol.ts +++ b/src/platform/platformProtocol.ts @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import { type PlatformCapability } from "@/platform/capabilities"; +import type { PlatformCapability } from "@/platform/capabilities"; import type { ElementReference } from "@/types/runtimeTypes"; import type { SanitizedIntegrationConfig } from "@/integrations/integrationTypes"; import type { NetworkRequestConfig } from "@/types/networkTypes"; @@ -37,7 +37,7 @@ import type { SnippetShortcutMenuProtocol } from "@/platform/platformTypes/snipp import type { TextSelectionMenuProtocol } from "@/platform/platformTypes/textSelectionMenuProtocol"; import type { PanelProtocol } from "@/platform/platformTypes/panelProtocol"; import type { QuickBarProtocol } from "@/platform/platformTypes/quickBarProtocol"; -import { ModComponentRef } from "@/types/modComponentTypes"; +import type { ModComponentRef } from "@/types/modComponentTypes"; /** * A protocol for the platform/environment running the mods. From 642f6955c16e77141cbaaebe46ae813e8c7aeff1 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 12:32:55 -0400 Subject: [PATCH 06/13] Fix broken test --- src/testUtils/factories/runtimeFactories.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/testUtils/factories/runtimeFactories.ts b/src/testUtils/factories/runtimeFactories.ts index f8a3d66c0d..5349d705fe 100644 --- a/src/testUtils/factories/runtimeFactories.ts +++ b/src/testUtils/factories/runtimeFactories.ts @@ -18,8 +18,8 @@ import { type BrickOptions, type RunMetadata } from "@/types/runtimeTypes"; import { define, derive } from "cooky-cutter"; import ConsoleLogger from "@/utils/ConsoleLogger"; -import { uuidSequence } from "@/testUtils/factories/stringFactories"; import contentScriptPlatform from "@/contentScript/contentScriptPlatform"; +import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactories"; /** * Factory for BrickOptions to pass to Brick.run method. @@ -32,11 +32,8 @@ export const brickOptionsFactory = define({ ctxt() { return {}; }, - platform: () => contentScriptPlatform, - logger: (i: number) => - new ConsoleLogger({ - extensionId: uuidSequence(i), - }), + platform: (_i: number) => contentScriptPlatform, + logger: (_i: number) => new ConsoleLogger(modComponentRefFactory()), root: (_i: number) => document, runPipeline: (_i: number) => jest.fn().mockRejectedValue(new Error("runPipeline mock not implemented")), From 793180590f81274ca5c148b801448b55ab831314 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 12:58:30 -0400 Subject: [PATCH 07/13] Fix broken type --- src/testUtils/factories/runtimeFactories.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/testUtils/factories/runtimeFactories.ts b/src/testUtils/factories/runtimeFactories.ts index 5349d705fe..cab6f92a12 100644 --- a/src/testUtils/factories/runtimeFactories.ts +++ b/src/testUtils/factories/runtimeFactories.ts @@ -33,7 +33,14 @@ export const brickOptionsFactory = define({ return {}; }, platform: (_i: number) => contentScriptPlatform, - logger: (_i: number) => new ConsoleLogger(modComponentRefFactory()), + logger(_i: number) { + const { blueprintId, ...rest } = modComponentRefFactory(); + // MessageContext expects undefined instead of null for blueprintId + return new ConsoleLogger({ + ...rest, + blueprintId: blueprintId ?? undefined, + }); + }, root: (_i: number) => document, runPipeline: (_i: number) => jest.fn().mockRejectedValue(new Error("runPipeline mock not implemented")), From 7454e5dc91203fb1d7137f8f7042cfbf7e8c8f04 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 13:21:26 -0400 Subject: [PATCH 08/13] Method cleanup --- .../temporaryInfo/DisplayTemporaryInfo.ts | 18 +++---------- src/contentScript/sidebarController.tsx | 10 +++---- src/store/sidebar/sidebarSlice.ts | 3 +-- src/utils/modUtils.ts | 27 +++++++++++++++++++ 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts index 57b4d37769..0655cac227 100644 --- a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts +++ b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.ts @@ -27,11 +27,11 @@ import { type JsonObject } from "type-fest"; import { TransformerABC } from "@/types/bricks/transformerTypes"; import { type Schema } from "@/types/schemaTypes"; import { type Location } from "@/types/starterBrickTypes"; -import { assumeNotNullish_UNSAFE } from "@/utils/nullishUtils"; import type { RefreshTrigger, TemporaryPanelEntryMetadata, } from "@/platform/panels/panelTypes"; +import { mapMessageContextToModComponentRef } from "@/utils/modUtils"; class DisplayTemporaryInfo extends TransformerABC { static BRICK_ID = validateRegistryId("@pixiebrix/display"); @@ -101,9 +101,7 @@ class DisplayTemporaryInfo extends TransformerABC { isRootAware: boolean; }>, { - logger: { - context: { extensionId, blueprintId, extensionPointId }, - }, + logger: { context }, root = document, platform, runRendererPipeline, @@ -113,22 +111,14 @@ class DisplayTemporaryInfo extends TransformerABC { expectContext("contentScript"); const target = isRootAware ? root : document; - assumeNotNullish_UNSAFE(extensionId); - assumeNotNullish_UNSAFE(extensionPointId); - // XXX: blueprintId can actually be nullish if not running on the context of a mod. But assume it's non-nullish - // for passing to the panel for now. The panel can gracefully handle nullish blueprintId. - assumeNotNullish_UNSAFE(blueprintId); // Counter for tracking branch execution let counter = 0; const panelEntryMetadata: TemporaryPanelEntryMetadata = { heading: title, - modComponentRef: { - extensionId, - blueprintId, - extensionPointId, - }, + // Throws if there's no mod component or starter brick in the context + modComponentRef: mapMessageContextToModComponentRef(context), }; const getPayload = async () => { diff --git a/src/contentScript/sidebarController.tsx b/src/contentScript/sidebarController.tsx index 01715fe551..e05d3ab8a4 100644 --- a/src/contentScript/sidebarController.tsx +++ b/src/contentScript/sidebarController.tsx @@ -470,10 +470,12 @@ export function updateHeading(extensionId: UUID, heading: string): void { } export function upsertPanel( - { extensionId, extensionPointId, blueprintId }: ModComponentRef, + modComponentRef: ModComponentRef, heading: string, payload: PanelPayload, ): void { + const { extensionId, extensionPointId, blueprintId } = modComponentRef; + const entry = panels.find( (panel) => panel.modComponentRef.extensionId === extensionId, ); @@ -502,11 +504,7 @@ export function upsertPanel( ); panels.push({ type: "panel", - modComponentRef: { - extensionId, - extensionPointId, - blueprintId, - }, + modComponentRef, heading, payload, }); diff --git a/src/store/sidebar/sidebarSlice.ts b/src/store/sidebar/sidebarSlice.ts index 8b8b429d35..f0d97c4879 100644 --- a/src/store/sidebar/sidebarSlice.ts +++ b/src/store/sidebar/sidebarSlice.ts @@ -149,9 +149,8 @@ export function fixActiveTabOnRemoveInPlace( const matchingMod = panels.find( ({ modComponentRef: { blueprintId } }) => "modComponentRef" in removedEntry && - // Need to check for removedEntry.blueprintId to avoid switching between ModComponentBases that don't have - // an associate mod blueprintId === removedEntry.modComponentRef.blueprintId && + // Require blueprintId to avoid switching between panels of standalone mod components blueprintId, ); diff --git a/src/utils/modUtils.ts b/src/utils/modUtils.ts index a9ad27afba..3f98a7f99a 100644 --- a/src/utils/modUtils.ts +++ b/src/utils/modUtils.ts @@ -34,6 +34,7 @@ import { type ModComponentBase, type HydratedModComponent, type SerializedModComponent, + type ModComponentRef, } from "@/types/modComponentTypes"; import { DefinitionKinds, type RegistryId } from "@/types/registryTypes"; import { type UUID } from "@/types/stringTypes"; @@ -54,6 +55,32 @@ import { import { produce } from "immer"; import { isStarterBrickDefinitionLike } from "@/starterBricks/types"; import { normalizeStarterBrickDefinitionProp } from "@/starterBricks/starterBrickUtils"; +import { type MessageContext } from "@/types/loggerTypes"; + +/** + * Returns the ModComponentRef for a given Logger MessageContext. Only call from running bricks with an associated + * mod component and starter brick in the context. + * + * @throws TypeError if the extensionId or extensionPointId is missing + */ +export function mapMessageContextToModComponentRef( + context: MessageContext, +): ModComponentRef { + assertNotNullish( + context.extensionId, + "extensionId is required for ModComponentRef", + ); + assertNotNullish( + context.extensionPointId, + "extensionPointId is required for ModComponentRef", + ); + + return { + extensionId: context.extensionId, + blueprintId: context.blueprintId, + extensionPointId: context.extensionPointId, + }; +} /** * Returns true if the mod is an UnavailableMod, i.e., a mod the user no longer has access to. From 596c5d9fddf9b8ffdcf6f8b184a7cf7855381ebf Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 6 Jul 2024 13:35:08 -0400 Subject: [PATCH 09/13] Update test options --- .../DisplayTemporaryInfo.test.ts | 64 +++++++------------ 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts index a3e4726223..ad20e78513 100644 --- a/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts +++ b/src/bricks/transformers/temporaryInfo/DisplayTemporaryInfo.test.ts @@ -64,12 +64,18 @@ import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactor jest.mock("@/contentScript/modalDom"); jest.mock("@/contentScript/sidebarController"); jest.mock("@/platform/panels/panelController"); - jest.mock("@/utils/iframeUtils"); const displayTemporaryInfoBlock = new DisplayTemporaryInfo(); const renderer = new DocumentRenderer(); +function reduceOptionsFactory() { + return { + ...testOptions("v3"), + logger: new ConsoleLogger(modComponentRefFactory()), + }; +} + describe("DisplayTemporaryInfo", () => { beforeEach(() => { jest.mocked(isLoadedInIframe).mockReturnValue(false); @@ -157,7 +163,7 @@ describe("DisplayTemporaryInfo", () => { payload = entry.payload; }); - await reducePipeline(pipeline, simpleInput({}), testOptions("v3")); + await reducePipeline(pipeline, simpleInput({}), reduceOptionsFactory()); expect(isRendererErrorPayload(payload)).toBe(true); const error = payload as RendererErrorPayload; @@ -202,6 +208,7 @@ describe("DisplayTemporaryInfo", () => { }); test("it errors from frame", async () => { + const modComponentRef = modComponentRefFactory(); jest.mocked(isLoadedInIframe).mockReturnValue(true); const config = getExampleBrickConfig(renderer.id); @@ -215,13 +222,9 @@ describe("DisplayTemporaryInfo", () => { }, }; - const extensionId = uuidv4(); - const options = { ...testOptions("v3"), - logger: new ConsoleLogger({ - extensionId, - }), + logger: new ConsoleLogger(modComponentRef), }; await expect( @@ -241,17 +244,8 @@ describe("DisplayTemporaryInfo", () => { }, }; - const extensionId = uuidv4(); - - const options = { - ...testOptions("v3"), - logger: new ConsoleLogger({ - extensionId, - }), - }; - await expect( - reducePipeline(pipeline, simpleInput({}), options), + reducePipeline(pipeline, simpleInput({}), reduceOptionsFactory()), ).rejects.toThrow("Target must be an element for popover"); }); @@ -269,17 +263,13 @@ describe("DisplayTemporaryInfo", () => { }, }; - const extensionId = uuidv4(); const root = document.querySelector("#target"); - const options = { - ...testOptions("v3"), - logger: new ConsoleLogger({ - extensionId, - }), - }; - - await reducePipeline(pipeline, { ...simpleInput({}), root }, options); + await reducePipeline( + pipeline, + { ...simpleInput({}), root }, + reduceOptionsFactory(), + ); expect(showModal).not.toHaveBeenCalled(); expect(showTemporarySidebarPanel).not.toHaveBeenCalled(); @@ -309,16 +299,7 @@ describe("DisplayTemporaryInfo", () => { }, }; - const extensionId = uuidv4(); - - const options = { - ...testOptions("v3"), - logger: new ConsoleLogger({ - extensionId, - }), - }; - - void reducePipeline(pipeline, simpleInput({}), options); + void reducePipeline(pipeline, simpleInput({}), reduceOptionsFactory()); await tick(); @@ -333,7 +314,7 @@ describe("DisplayTemporaryInfo", () => { deferredPromise.resolve(); }); - test("body receives updated mod variable on re-render", async () => { + test("body receives updated public mod variable on re-render", async () => { document.body.innerHTML = '
'; const deferredPromise = pDefer(); @@ -360,9 +341,12 @@ describe("DisplayTemporaryInfo", () => { const options = { ...testOptions("v3"), - logger: new ConsoleLogger({ - extensionId, - }), + logger: new ConsoleLogger( + modComponentRefFactory({ + extensionId, + blueprintId: null, + }), + ), }; void reducePipeline(pipeline, simpleInput({}), options); From 0bc7de4f3b0a74cf7a73eb52c5b1872461c60328 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sun, 7 Jul 2024 08:20:43 -0400 Subject: [PATCH 10/13] Add method for selecting event data from modComponentRef --- .../ephemeralForm/formTransformer.ts | 21 ++++++---------- src/sidebar/Tabs.tsx | 7 +++--- src/sidebar/TemporaryPanelTabPane.tsx | 4 +-- src/telemetry/deployments.ts | 3 ++- src/telemetry/telemetryHelpers.test.ts | 25 ++++++++++++++++++- src/telemetry/telemetryHelpers.ts | 14 +++++++++++ 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/bricks/transformers/ephemeralForm/formTransformer.ts b/src/bricks/transformers/ephemeralForm/formTransformer.ts index 034714436a..ee56a0686d 100644 --- a/src/bricks/transformers/ephemeralForm/formTransformer.ts +++ b/src/bricks/transformers/ephemeralForm/formTransformer.ts @@ -23,7 +23,7 @@ import { type BrickConfig } from "@/bricks/types"; import { type FormDefinition } from "@/platform/forms/formTypes"; import { isExpression } from "@/utils/expressionUtils"; import type { PlatformCapability } from "@/platform/capabilities"; -import { assertNotNullish } from "@/utils/nullishUtils"; +import { mapMessageContextToModComponentRef } from "@/utils/modUtils"; export const TEMPORARY_FORM_SCHEMA: Schema = { type: "object", @@ -136,20 +136,13 @@ export class FormTransformer extends TransformerABC { controller.abort(); }); - const { extensionId, blueprintId, extensionPointId } = logger.context; - - assertNotNullish(extensionId, `${this.name} must be run in a mod context`); - assertNotNullish( - extensionPointId, - `${this.name} must be run in a starter brick context`, - ); - try { - return await platform.form(formDefinition, controller, { - extensionId, - blueprintId, - extensionPointId, - }); + // `mapMessageContextToModComponentRef` throws if there's no mod component or starter brick in the context + return await platform.form( + formDefinition, + controller, + mapMessageContextToModComponentRef(logger.context), + ); } finally { controller.abort(); } diff --git a/src/sidebar/Tabs.tsx b/src/sidebar/Tabs.tsx index b1eeaa28ff..86283ab61f 100644 --- a/src/sidebar/Tabs.tsx +++ b/src/sidebar/Tabs.tsx @@ -64,6 +64,7 @@ import useOnMountOnly from "@/hooks/useOnMountOnly"; import UnavailableOverlay from "@/sidebar/UnavailableOverlay"; import removeFormPanel from "@/store/sidebar/thunks/removeFormPanel"; import ConnectingOverlay from "@/sidebar/ConnectingOverlay"; +import { mapModComponentRefToEventData } from "@/telemetry/telemetryHelpers"; const ActivateModPanel = lazy( async () => @@ -315,9 +316,8 @@ const Tabs: React.FC = () => { { reportEvent(Events.VIEW_ERROR, { + ...mapModComponentRefToEventData(panel.modComponentRef), panelType: panel.type, - extensionId: panel.modComponentRef.extensionId, - blueprintId: panel.modComponentRef.blueprintId, }); }} > @@ -350,9 +350,8 @@ const Tabs: React.FC = () => { { reportEvent(Events.VIEW_ERROR, { + ...mapModComponentRefToEventData(form.modComponentRef), panelType: form.type, - extensionId: form.modComponentRef.extensionId, - blueprintId: form.modComponentRef.blueprintId, }); }} > diff --git a/src/sidebar/TemporaryPanelTabPane.tsx b/src/sidebar/TemporaryPanelTabPane.tsx index 02ffee73b2..7055bd135b 100644 --- a/src/sidebar/TemporaryPanelTabPane.tsx +++ b/src/sidebar/TemporaryPanelTabPane.tsx @@ -31,6 +31,7 @@ import resolveTemporaryPanel from "@/store/sidebar/thunks/resolveTemporaryPanel" import { type AsyncDispatch } from "@/sidebar/store"; import UnavailableOverlay from "@/sidebar/UnavailableOverlay"; import removeTemporaryPanel from "@/store/sidebar/thunks/removeTemporaryPanel"; +import { mapModComponentRefToEventData } from "@/telemetry/telemetryHelpers"; // Need to memoize this to make sure it doesn't rerender unless its entry actually changes // This was part of the fix for issue: https://github.com/pixiebrix/pixiebrix-extension/issues/5646 @@ -60,9 +61,8 @@ export const TemporaryPanelTabPane: React.FC<{ { reportEvent(Events.VIEW_ERROR, { + ...mapModComponentRefToEventData(modComponentRef), panelType: type, - extensionId: modComponentRef.extensionId, - blueprintId: modComponentRef.blueprintId, }); }} > diff --git a/src/telemetry/deployments.ts b/src/telemetry/deployments.ts index eb05edadd7..b3a374cb52 100644 --- a/src/telemetry/deployments.ts +++ b/src/telemetry/deployments.ts @@ -4,7 +4,8 @@ import { type MessageContext } from "@/types/loggerTypes"; import { isRegistryId } from "@/types/helpers"; /** - * Select data to report to the team admins for the deployment + * Select data to report to the team admins for the deployment. + * @see mapModComponentRefToEventData */ export function selectEventData( modComponent: Nullishable, diff --git a/src/telemetry/telemetryHelpers.test.ts b/src/telemetry/telemetryHelpers.test.ts index 001d02d47f..5ac861df51 100644 --- a/src/telemetry/telemetryHelpers.test.ts +++ b/src/telemetry/telemetryHelpers.test.ts @@ -15,7 +15,12 @@ * along with this program. If not, see . */ -import { cleanDatadogVersionName } from "@/telemetry/telemetryHelpers"; +import { + cleanDatadogVersionName, + mapModComponentRefToEventData, +} from "@/telemetry/telemetryHelpers"; +import { modComponentRefFactory } from "@/testUtils/factories/modComponentFactories"; +import { mapMessageContextToModComponentRef } from "@/utils/modUtils"; // Disable automatic __mocks__ resolution #6799 jest.mock("@/telemetry/telemetryHelpers", () => @@ -35,3 +40,21 @@ describe("cleanDatadogVersionName", () => { ); }); }); + +describe("mapModComponentRefToEventData", () => { + it("maps fields", () => { + const value = modComponentRefFactory(); + expect(mapModComponentRefToEventData(value)).toStrictEqual({ + extensionId: value.extensionId, + blueprintId: value.blueprintId, + extensionPointId: value.extensionPointId, + }); + }); + + it("round trips mod component reference", () => { + const value = modComponentRefFactory(); + expect( + mapMessageContextToModComponentRef(mapModComponentRefToEventData(value)), + ).toStrictEqual(value); + }); +}); diff --git a/src/telemetry/telemetryHelpers.ts b/src/telemetry/telemetryHelpers.ts index dca63053a8..178318ae18 100644 --- a/src/telemetry/telemetryHelpers.ts +++ b/src/telemetry/telemetryHelpers.ts @@ -20,6 +20,8 @@ import { uuidv4 } from "@/types/helpers"; import type { UUID } from "@/types/stringTypes"; import { once } from "lodash"; import { StorageItem } from "webext-storage"; +import type { ModComponentRef } from "@/types/modComponentTypes"; +import type { MessageContext } from "@/types/loggerTypes"; /** * The Person model for application error telemetry. @@ -97,3 +99,15 @@ export async function mapAppUserToTelemetryUser( organizationId: telemetryOrganizationId ?? organizationId, }; } + +/** + * Returns the event data for a ModComponentRef. + * @param modComponentRef + * @see selectEventData + */ +export function mapModComponentRefToEventData( + modComponentRef: ModComponentRef, +): MessageContext { + // Fields are currently named the same. In the future, the fields might temporarily diverge. + return { ...modComponentRef }; +} From a570ffe42624893fa722efa461573cb5528d8f79 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sun, 7 Jul 2024 09:05:40 -0400 Subject: [PATCH 11/13] Fix lint/type error --- src/telemetry/telemetryHelpers.test.ts | 11 +++++++++++ src/telemetry/telemetryHelpers.ts | 8 ++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/telemetry/telemetryHelpers.test.ts b/src/telemetry/telemetryHelpers.test.ts index 5ac861df51..dc9e6917aa 100644 --- a/src/telemetry/telemetryHelpers.test.ts +++ b/src/telemetry/telemetryHelpers.test.ts @@ -51,6 +51,17 @@ describe("mapModComponentRefToEventData", () => { }); }); + it("replaces null with undefined", () => { + const value = modComponentRefFactory({ + blueprintId: null, + }); + expect(mapModComponentRefToEventData(value)).toStrictEqual({ + extensionId: value.extensionId, + blueprintId: undefined, + extensionPointId: value.extensionPointId, + }); + }); + it("round trips mod component reference", () => { const value = modComponentRefFactory(); expect( diff --git a/src/telemetry/telemetryHelpers.ts b/src/telemetry/telemetryHelpers.ts index 178318ae18..20d5f254b4 100644 --- a/src/telemetry/telemetryHelpers.ts +++ b/src/telemetry/telemetryHelpers.ts @@ -102,12 +102,16 @@ export async function mapAppUserToTelemetryUser( /** * Returns the event data for a ModComponentRef. - * @param modComponentRef * @see selectEventData */ export function mapModComponentRefToEventData( modComponentRef: ModComponentRef, ): MessageContext { // Fields are currently named the same. In the future, the fields might temporarily diverge. - return { ...modComponentRef }; + return { + extensionId: modComponentRef.extensionId, + extensionPointId: modComponentRef.extensionPointId, + // MessageContext expects undefined instead of null/undefined + blueprintId: modComponentRef.blueprintId ?? undefined, + }; } From 00cbd3294d18ddd4f4bce168846f7e3923c48afb Mon Sep 17 00:00:00 2001 From: Graham Langford <30706330+grahamlangford@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:10:41 -0500 Subject: [PATCH 12/13] Apply suggestions from code review --- src/platform/platformProtocol.ts | 2 +- src/sidebar/sidebarSelectors.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/platformProtocol.ts b/src/platform/platformProtocol.ts index 3f8ee83390..75240dc813 100644 --- a/src/platform/platformProtocol.ts +++ b/src/platform/platformProtocol.ts @@ -90,7 +90,7 @@ export interface PlatformProtocol { form: ( definition: FormDefinition, controller: AbortController, - componentRef: ModComponentRef, + modComponentRef: ModComponentRef, ) => Promise; /** diff --git a/src/sidebar/sidebarSelectors.ts b/src/sidebar/sidebarSelectors.ts index 50ebb0b406..427668e957 100644 --- a/src/sidebar/sidebarSelectors.ts +++ b/src/sidebar/sidebarSelectors.ts @@ -75,7 +75,7 @@ const extensionForEventKeySelector = createSelector( } return extensions.find( - (extension) => extension.id === sidebarEntry.modComponentRef.extensionId, + (modComponent) => modComponent.id === sidebarEntry.modComponentRef.extensionId, ); }, ); @@ -100,7 +100,7 @@ export const selectExtensionFromEventKey = } return extensions.find( - (extension) => extension.id === sidebarEntry.modComponentRef.extensionId, + (modComponent) => modComponent.id === sidebarEntry.modComponentRef.extensionId, ); }; From 6b8c8a34742780b5366ea316c16152155ebfa144 Mon Sep 17 00:00:00 2001 From: Graham Langford Date: Mon, 8 Jul 2024 10:18:13 -0500 Subject: [PATCH 13/13] prettier fix --- src/sidebar/sidebarSelectors.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sidebar/sidebarSelectors.ts b/src/sidebar/sidebarSelectors.ts index 427668e957..4def35c70b 100644 --- a/src/sidebar/sidebarSelectors.ts +++ b/src/sidebar/sidebarSelectors.ts @@ -75,7 +75,8 @@ const extensionForEventKeySelector = createSelector( } return extensions.find( - (modComponent) => modComponent.id === sidebarEntry.modComponentRef.extensionId, + (modComponent) => + modComponent.id === sidebarEntry.modComponentRef.extensionId, ); }, ); @@ -100,7 +101,8 @@ export const selectExtensionFromEventKey = } return extensions.find( - (modComponent) => modComponent.id === sidebarEntry.modComponentRef.extensionId, + (modComponent) => + modComponent.id === sidebarEntry.modComponentRef.extensionId, ); };