@mx-cartographer/experiences 9.7.89 → 9.7.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/core/types/Insights.d.ts +4 -0
  3. package/dist/core/types/Insights.es.js.map +1 -1
  4. package/dist/insights-v2/actionBlocks/SubscriptionStatusModalBlock.es.js +80 -70
  5. package/dist/insights-v2/actionBlocks/SubscriptionStatusModalBlock.es.js.map +1 -1
  6. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js +43 -42
  7. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js.map +1 -1
  8. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/layout.d.ts +2 -0
  9. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/layout.es.js +4 -3
  10. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/layout.es.js.map +1 -1
  11. package/dist/insights-v2/components/CompactInsightCard.es.js +176 -127
  12. package/dist/insights-v2/components/CompactInsightCard.es.js.map +1 -1
  13. package/dist/insights-v2/components/TransactionListCard.d.ts +1 -1
  14. package/dist/insights-v2/components/TransactionListCard.es.js +30 -26
  15. package/dist/insights-v2/components/TransactionListCard.es.js.map +1 -1
  16. package/dist/insights-v2/components/shared/StatusSnackbar.d.ts +5 -1
  17. package/dist/insights-v2/components/shared/StatusSnackbar.es.js +31 -11
  18. package/dist/insights-v2/components/shared/StatusSnackbar.es.js.map +1 -1
  19. package/dist/insights-v2/components/shared/elevation.d.ts +7 -0
  20. package/dist/insights-v2/components/shared/elevation.es.js +9 -0
  21. package/dist/insights-v2/components/shared/elevation.es.js.map +1 -0
  22. package/dist/insights-v2/ctaActions/ctaConfig.es.js +16 -8
  23. package/dist/insights-v2/ctaActions/ctaConfig.es.js.map +1 -1
  24. package/dist/insights-v2/ctaActions/index.d.ts +1 -1
  25. package/dist/insights-v2/ctaActions/runCtaAction.d.ts +6 -0
  26. package/dist/insights-v2/ctaActions/runCtaAction.es.js +1 -0
  27. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
  28. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"runCtaAction.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/runCtaAction.ts"],"sourcesContent":["import { sendBeatCta } from '../../common/utils/PostMessage'\nimport {\n ANALYTICS_EVENTS_CONFIG,\n type AnalyticsEventConfigEntry,\n} from '../analytics/analyticsEventsConfig'\nimport { resolveAnalyticsEvent } from '../analytics/resolveAnalyticsEvent'\nimport { hasDrawerBlock } from '../mappings/hasDrawerBlock'\nimport { ctaActions } from './ctaConfig'\nimport { resolveCtaMetadata } from './ctaPostMessages'\nimport { ctaHandlers, type CtaHandlerName } from './ctaHandlers'\nimport { HOST_ACTIONS } from './hostActions'\nimport type { CtaPostMessageMetadata } from '../../core'\nimport type {\n CtaContext,\n CtaEffect,\n CtaHandler,\n CtaHandlerContext,\n CtaState,\n CtaStateContext,\n CtaStep,\n RunHandlerOptions,\n RunInDescriptionCtaArgs,\n} from './types'\n\nconst DEFAULT_STEP: CtaStep = {}\n\n// ─── Resolution ─────────────────────────────────────────────────────────────\n\n/** The beat's own context (input a block collected for it), `{}` until something is written. */\nexport const beatContextOf = ({ beat, stores }: Pick<CtaStateContext, 'beat' | 'stores'>) =>\n stores.insightsV2Store.beatContext[beat.guid] ?? {}\n\n/** The step named by the CTA's `action_key`, or the empty default (no key / unknown key). */\nexport const resolveStep = ({ cta }: { cta: { action_key?: string | null } }): CtaStep =>\n (cta.action_key && ctaActions[cta.action_key]) || DEFAULT_STEP\n\n/** Pending always disables, as may the step's `disabledWhen`; its `presenter` may replace the button. */\nexport const resolveCtaState = (ctx: CtaStateContext): CtaState => {\n const step = resolveStep(ctx)\n const pending = ctx.stores.insightsV2Store.ctaStatus[ctx.beat.guid] === 'pending'\n const presentation = step.presenter?.(ctx)\n\n return {\n disabled: pending || (step.disabledWhen?.(ctx) ?? false),\n ...(presentation && { presentation }),\n }\n}\n\n// ─── Targets and effects ────────────────────────────────────────────────────\n\n/** Opens the beat's drawer for whichever slot triggered the current step. */\nconst openDrawerForSlot = ({\n beat,\n slot,\n stores,\n}: Pick<CtaContext, 'beat' | 'slot' | 'stores'>) => {\n stores.insightsV2Store.openCtaDrawer(\n beat.guid,\n slot === 'primary' ? 'primaryCta' : 'secondaryCta',\n )\n}\n\n/**\n * Posts this CTA's resolved message (event, op and beat-specific payload, from the backend's\n * `cta.metadata` or the template's `ctaPostMessages` entry) to the host.\n */\nconst postCtaMessage = ({ beat, stores }: CtaContext, metadata: CtaPostMessageMetadata) => {\n const { is_mobile_webview, clientConfig } = stores.appDataStore.appConfig\n const { event, action, payload } = metadata\n\n sendBeatCta(beat, {\n event: event ?? undefined,\n op: action ?? undefined,\n extras: payload ?? undefined,\n isMobileWebview: is_mobile_webview,\n webviewScheme: clientConfig?.master?.ui_message_webview_url_scheme,\n userGuid: beat.insights_payload?.user_guid ?? beat.user_guid,\n })\n}\n\n/**\n * Whether the step's target opens the drawer: `drawer` always, `default` only for a real block (a\n * missing block never implies a post message), `none` never. Omitted: `none` with `run`, else `default`.\n */\nconst opensDrawer = (step: CtaStep, cta: { block?: string | null }) => {\n const target = step.next ?? (step.run ? 'none' : 'default')\n\n return target === 'drawer' || (target === 'default' && hasDrawerBlock(cta.block))\n}\n\nconst runEffect = (effect: CtaEffect, { beat, stores }: CtaContext) => {\n switch (effect) {\n case 'refreshBeat':\n stores.emitUpdate({ staleBeatGuid: beat.guid })\n break\n }\n}\n\ntype AnalyticsCtx = Pick<CtaContext, 'beat' | 'stores' | 'hostActions'>\n\n/**\n * The one way to fire a catalogued analytics event. Takes the catalogue entry itself (e.g.\n * `ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED`), not a lookup key — the call site already\n * has the entry via property access, so there is no key to look up or mistype. Resolves the\n * payload and hands it to the host directly. Never throws: a failed fire is captured, it\n * cannot fail or disable anything.\n */\nexport const sendCtaAnalytics = (ctx: AnalyticsCtx, event: AnalyticsEventConfigEntry): void => {\n try {\n ctx.hostActions.onAnalyticsEvent?.(resolveAnalyticsEvent(event, ctx.beat))\n } catch (error) {\n ctx.stores.captureException(error, {\n store: 'ctaActions',\n template: ctx.beat.template,\n handler: 'sendCtaAnalytics',\n })\n }\n}\n\n// First primary click: fires FIRST_TIME_PRIMARY_ACTION_SELECTED and records cta_clicked_at.\nconst markCtaClicked = (ctx: AnalyticsCtx & Pick<CtaContext, 'slot'>): void => {\n const { beat, slot, hostActions, stores } = ctx\n\n if (slot !== 'primary' || beat.cta_clicked_at) return\n\n sendCtaAnalytics(ctx, ANALYTICS_EVENTS_CONFIG.FIRST_TIME_PRIMARY_ACTION_SELECTED)\n\n // Fire-and-forget; a failed write must not fail the user's action, but still report it.\n Promise.resolve()\n .then(() =>\n hostActions.onCtaAction?.({ beatGuid: beat.guid, name: HOST_ACTIONS.markCtaClicked }),\n )\n .catch((error) =>\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n handler: 'markCtaClicked',\n }),\n )\n}\n\n/**\n * Trigger 1 + the first-click bookkeeping together: every CTA press's generic analytics\n * (`Primary/Secondary Action Selected`), plus `FIRST_TIME_PRIMARY_ACTION_SELECTED` /\n * `cta_clicked_at` on the first primary press. The one call site for \"a CTA got pressed\",\n * used by the runner and by the contact-support bypass in `BaseLayout`.\n */\nexport const reportCtaClick = (\n ctx: AnalyticsCtx & Pick<CtaContext, 'slot'>,\n { skipCtaClicked = false }: { skipCtaClicked?: boolean } = {},\n): void => {\n sendCtaAnalytics(\n ctx,\n ctx.slot === 'primary'\n ? ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED\n : ANALYTICS_EVENTS_CONFIG.SECONDARY_ACTION_SELECTED,\n )\n\n if (!skipCtaClicked) markCtaClicked(ctx)\n}\n\n// ─── Shared bookkeeping ─────────────────────────────────────────────────────\n\n/** Runs a named handler, then its `onSuccess` effects. Shared by card CTAs and drawer saves. */\nconst runHandlerWithEffects = async (\n ctx: CtaContext,\n handlerName: CtaHandlerName,\n onSuccess: CtaEffect[],\n): Promise<void> => {\n // Names are typed, but the map is still indexed defensively: config can come from anywhere.\n const handler = (ctaHandlers as Record<string, CtaHandler>)[handlerName]\n\n if (!handler) throw new Error(`Unknown CTA handler \"${handlerName}\"`)\n\n await handler(ctx)\n\n for (const effect of onSuccess) runEffect(effect, ctx)\n}\n\n/** Per-beat status around `work`: `pending` -> `done` | `error` (+ captureException). */\nconst withCtaStatus = async (\n { beat, stores }: Pick<CtaContext, 'beat' | 'stores'>,\n work: () => Promise<void>,\n captureContext: Record<string, string> = {},\n): Promise<boolean> => {\n const { insightsV2Store } = stores\n\n insightsV2Store.setCtaStatus(beat.guid, 'pending')\n\n try {\n await work()\n insightsV2Store.setCtaStatus(beat.guid, 'done')\n\n return true\n } catch (error) {\n insightsV2Store.setCtaStatus(beat.guid, 'error')\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n ...captureContext,\n })\n\n return false\n }\n}\n\n/** The step's params, overridden by the caller's. */\nconst withStepParams = (step: CtaStep, ctx: CtaContext): CtaContext => {\n const stepParams = typeof step.params === 'function' ? step.params(ctx) : step.params\n\n return { ...ctx, params: { ...stepParams, ...ctx.params } }\n}\n\n// ─── Entry points ───────────────────────────────────────────────────────────\n\n// Runs the CTA's step: handler, onSuccess effects, post message, then the next target.\nexport const runCtaAction = async (ctx: CtaContext): Promise<void> => {\n const step = resolveStep(ctx)\n const stepCtx = withStepParams(step, ctx)\n\n reportCtaClick(stepCtx, { skipCtaClicked: step.skipCtaClicked })\n\n await withCtaStatus(stepCtx, async () => {\n if (step.run) await runHandlerWithEffects(stepCtx, step.run, step.onSuccess ?? [])\n\n // A step's own event, e.g. select_savings_account (Saved) / open_savings_account (Created).\n if (step.analytics) sendCtaAnalytics(stepCtx, step.analytics)\n\n const metadata = resolveCtaMetadata(stepCtx)\n\n if (metadata) postCtaMessage(stepCtx, metadata)\n\n if (opensDrawer(step, stepCtx.cta)) openDrawerForSlot(stepCtx)\n })\n}\n\n/**\n * A pinned beat's auto-open: runs the card action's (`in_description_cta`) step and opens its block,\n * with no press analytics or post message. Does nothing while the step shows a status (e.g. an\n * answered subscription), nothing or a disabled button. Never rejects: failures are reported.\n */\nexport const runInDescriptionCta = async ({\n beat,\n stores,\n hostActions = {},\n}: RunInDescriptionCtaArgs): Promise<void> => {\n const link = beat.insights_payload?.in_description_cta\n\n if (!link) return\n\n const cta = { ...link, block: link.block ?? '' }\n const step = resolveStep({ cta })\n\n try {\n const ctx = withStepParams(step, {\n beat,\n cta,\n slot: 'primary',\n stores,\n hostActions,\n params: {},\n })\n\n await step.prepare?.(ctx)\n\n // Read after the load; `disabled` also covers a press made meanwhile (pending).\n const { disabled, presentation } = resolveCtaState(ctx)\n\n if (disabled || presentation) return\n\n if (step.run) {\n const { run, onSuccess = [] } = step\n\n if (!(await withCtaStatus(ctx, () => runHandlerWithEffects(ctx, run, onSuccess)))) return\n }\n\n // Never replaces a drawer the user opened meanwhile.\n if (opensDrawer(step, cta) && !stores.insightsV2Store.actionDrawer.isDrawerOpen) {\n stores.insightsV2Store.openActionDrawer({\n guid: beat.guid,\n trigger: 'inDescriptionCta',\n cta: { block: cta.block, variant: cta.variant, action_key: cta.action_key },\n })\n }\n } catch (error) {\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n handler: 'runInDescriptionCta',\n })\n }\n}\n\n/** Placeholder CTA for handlers run from inside a drawer, where no card slot is involved. */\nconst DIRECT_CTA = { label: '', block: '' }\n\n/** Runs a handler directly (drawer Save buttons). True on success. */\nexport const runCtaHandler = (\n ctx: CtaHandlerContext,\n handlerName: CtaHandlerName,\n { onSuccess = [], analytics }: RunHandlerOptions = {},\n): Promise<boolean> => {\n const fullCtx: CtaContext = { ...ctx, cta: DIRECT_CTA, slot: 'primary' }\n\n return withCtaStatus(\n fullCtx,\n async () => {\n await runHandlerWithEffects(fullCtx, handlerName, onSuccess)\n\n // Success-time analytics for drawer Saves.\n if (analytics) sendCtaAnalytics(fullCtx, analytics)\n },\n { handler: handlerName },\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runCtaAction.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/runCtaAction.ts"],"sourcesContent":["import { sendBeatCta } from '../../common/utils/PostMessage'\nimport {\n ANALYTICS_EVENTS_CONFIG,\n type AnalyticsEventConfigEntry,\n} from '../analytics/analyticsEventsConfig'\nimport { resolveAnalyticsEvent } from '../analytics/resolveAnalyticsEvent'\nimport { hasDrawerBlock } from '../mappings/hasDrawerBlock'\nimport { ctaActions } from './ctaConfig'\nimport { resolveCtaMetadata } from './ctaPostMessages'\nimport { ctaHandlers, type CtaHandlerName } from './ctaHandlers'\nimport { HOST_ACTIONS } from './hostActions'\nimport type { CtaPostMessageMetadata } from '../../core'\nimport type {\n CtaContext,\n CtaEffect,\n CtaHandler,\n CtaHandlerContext,\n CtaState,\n CtaStateContext,\n CtaStep,\n RunHandlerOptions,\n RunInDescriptionCtaArgs,\n} from './types'\n\nconst DEFAULT_STEP: CtaStep = {}\n\n// ─── Resolution ─────────────────────────────────────────────────────────────\n\n/** The beat's own context (input a block collected for it), `{}` until something is written. */\nexport const beatContextOf = ({ beat, stores }: Pick<CtaStateContext, 'beat' | 'stores'>) =>\n stores.insightsV2Store.beatContext[beat.guid] ?? {}\n\n/** The step named by the CTA's `action_key`, or the empty default (no key / unknown key). */\nexport const resolveStep = ({ cta }: { cta: { action_key?: string | null } }): CtaStep =>\n (cta.action_key && ctaActions[cta.action_key]) || DEFAULT_STEP\n\n/** Pending always disables, as may the step's `disabledWhen`; its `presenter` may replace the button. */\nexport const resolveCtaState = (ctx: CtaStateContext): CtaState => {\n const step = resolveStep(ctx)\n const pending = ctx.stores.insightsV2Store.ctaStatus[ctx.beat.guid] === 'pending'\n const presentation = step.presenter?.(ctx)\n\n return {\n disabled: pending || (step.disabledWhen?.(ctx) ?? false),\n ...(presentation && { presentation }),\n }\n}\n\n// ─── Targets and effects ────────────────────────────────────────────────────\n\n/** Opens the beat's drawer for whichever slot triggered the current step. */\nconst openDrawerForSlot = ({\n beat,\n slot,\n stores,\n}: Pick<CtaContext, 'beat' | 'slot' | 'stores'>) => {\n stores.insightsV2Store.openCtaDrawer(\n beat.guid,\n slot === 'primary' ? 'primaryCta' : 'secondaryCta',\n )\n}\n\n/**\n * Posts this CTA's resolved message (event, op and beat-specific payload, from the backend's\n * `cta.metadata` or the template's `ctaPostMessages` entry) to the host.\n */\nexport const postCtaMessage = (\n { beat, stores }: Pick<CtaContext, 'beat' | 'stores'>,\n metadata: CtaPostMessageMetadata,\n) => {\n const { is_mobile_webview, clientConfig } = stores.appDataStore.appConfig\n const { event, action, payload } = metadata\n\n sendBeatCta(beat, {\n event: event ?? undefined,\n op: action ?? undefined,\n extras: payload ?? undefined,\n isMobileWebview: is_mobile_webview,\n webviewScheme: clientConfig?.master?.ui_message_webview_url_scheme,\n userGuid: beat.insights_payload?.user_guid ?? beat.user_guid,\n })\n}\n\n/**\n * Whether the step's target opens the drawer: `drawer` always, `default` only for a real block (a\n * missing block never implies a post message), `none` never. Omitted: `none` with `run`, else `default`.\n */\nconst opensDrawer = (step: CtaStep, cta: { block?: string | null }) => {\n const target = step.next ?? (step.run ? 'none' : 'default')\n\n return target === 'drawer' || (target === 'default' && hasDrawerBlock(cta.block))\n}\n\nconst runEffect = (effect: CtaEffect, { beat, stores }: CtaContext) => {\n switch (effect) {\n case 'refreshBeat':\n stores.emitUpdate({ staleBeatGuid: beat.guid })\n break\n }\n}\n\ntype AnalyticsCtx = Pick<CtaContext, 'beat' | 'stores' | 'hostActions'>\n\n/**\n * The one way to fire a catalogued analytics event. Takes the catalogue entry itself (e.g.\n * `ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED`), not a lookup key — the call site already\n * has the entry via property access, so there is no key to look up or mistype. Resolves the\n * payload and hands it to the host directly. Never throws: a failed fire is captured, it\n * cannot fail or disable anything.\n */\nexport const sendCtaAnalytics = (ctx: AnalyticsCtx, event: AnalyticsEventConfigEntry): void => {\n try {\n ctx.hostActions.onAnalyticsEvent?.(resolveAnalyticsEvent(event, ctx.beat))\n } catch (error) {\n ctx.stores.captureException(error, {\n store: 'ctaActions',\n template: ctx.beat.template,\n handler: 'sendCtaAnalytics',\n })\n }\n}\n\n// First primary click: fires FIRST_TIME_PRIMARY_ACTION_SELECTED and records cta_clicked_at.\nconst markCtaClicked = (ctx: AnalyticsCtx & Pick<CtaContext, 'slot'>): void => {\n const { beat, slot, hostActions, stores } = ctx\n\n if (slot !== 'primary' || beat.cta_clicked_at) return\n\n sendCtaAnalytics(ctx, ANALYTICS_EVENTS_CONFIG.FIRST_TIME_PRIMARY_ACTION_SELECTED)\n\n // Fire-and-forget; a failed write must not fail the user's action, but still report it.\n Promise.resolve()\n .then(() =>\n hostActions.onCtaAction?.({ beatGuid: beat.guid, name: HOST_ACTIONS.markCtaClicked }),\n )\n .catch((error) =>\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n handler: 'markCtaClicked',\n }),\n )\n}\n\n/**\n * Trigger 1 + the first-click bookkeeping together: every CTA press's generic analytics\n * (`Primary/Secondary Action Selected`), plus `FIRST_TIME_PRIMARY_ACTION_SELECTED` /\n * `cta_clicked_at` on the first primary press. The one call site for \"a CTA got pressed\",\n * used by the runner and by the contact-support bypass in `BaseLayout`.\n */\nexport const reportCtaClick = (\n ctx: AnalyticsCtx & Pick<CtaContext, 'slot'>,\n { skipCtaClicked = false }: { skipCtaClicked?: boolean } = {},\n): void => {\n sendCtaAnalytics(\n ctx,\n ctx.slot === 'primary'\n ? ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED\n : ANALYTICS_EVENTS_CONFIG.SECONDARY_ACTION_SELECTED,\n )\n\n if (!skipCtaClicked) markCtaClicked(ctx)\n}\n\n// ─── Shared bookkeeping ─────────────────────────────────────────────────────\n\n/** Runs a named handler, then its `onSuccess` effects. Shared by card CTAs and drawer saves. */\nconst runHandlerWithEffects = async (\n ctx: CtaContext,\n handlerName: CtaHandlerName,\n onSuccess: CtaEffect[],\n): Promise<void> => {\n // Names are typed, but the map is still indexed defensively: config can come from anywhere.\n const handler = (ctaHandlers as Record<string, CtaHandler>)[handlerName]\n\n if (!handler) throw new Error(`Unknown CTA handler \"${handlerName}\"`)\n\n await handler(ctx)\n\n for (const effect of onSuccess) runEffect(effect, ctx)\n}\n\n/** Per-beat status around `work`: `pending` -> `done` | `error` (+ captureException). */\nconst withCtaStatus = async (\n { beat, stores }: Pick<CtaContext, 'beat' | 'stores'>,\n work: () => Promise<void>,\n captureContext: Record<string, string> = {},\n): Promise<boolean> => {\n const { insightsV2Store } = stores\n\n insightsV2Store.setCtaStatus(beat.guid, 'pending')\n\n try {\n await work()\n insightsV2Store.setCtaStatus(beat.guid, 'done')\n\n return true\n } catch (error) {\n insightsV2Store.setCtaStatus(beat.guid, 'error')\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n ...captureContext,\n })\n\n return false\n }\n}\n\n/** The step's params, overridden by the caller's. */\nconst withStepParams = (step: CtaStep, ctx: CtaContext): CtaContext => {\n const stepParams = typeof step.params === 'function' ? step.params(ctx) : step.params\n\n return { ...ctx, params: { ...stepParams, ...ctx.params } }\n}\n\n// ─── Entry points ───────────────────────────────────────────────────────────\n\n// Runs the CTA's step: handler, onSuccess effects, post message, then the next target.\nexport const runCtaAction = async (ctx: CtaContext): Promise<void> => {\n const step = resolveStep(ctx)\n const stepCtx = withStepParams(step, ctx)\n\n reportCtaClick(stepCtx, { skipCtaClicked: step.skipCtaClicked })\n\n await withCtaStatus(stepCtx, async () => {\n if (step.run) await runHandlerWithEffects(stepCtx, step.run, step.onSuccess ?? [])\n\n // A step's own event, e.g. select_savings_account (Saved) / open_savings_account (Created).\n if (step.analytics) sendCtaAnalytics(stepCtx, step.analytics)\n\n const metadata = resolveCtaMetadata(stepCtx)\n\n if (metadata) postCtaMessage(stepCtx, metadata)\n\n if (opensDrawer(step, stepCtx.cta)) openDrawerForSlot(stepCtx)\n })\n}\n\n/**\n * A pinned beat's auto-open: runs the card action's (`in_description_cta`) step and opens its block,\n * with no press analytics or post message. Does nothing while the step shows a status (e.g. an\n * answered subscription), nothing or a disabled button. Never rejects: failures are reported.\n */\nexport const runInDescriptionCta = async ({\n beat,\n stores,\n hostActions = {},\n}: RunInDescriptionCtaArgs): Promise<void> => {\n const link = beat.insights_payload?.in_description_cta\n\n if (!link) return\n\n const cta = { ...link, block: link.block ?? '' }\n const step = resolveStep({ cta })\n\n try {\n const ctx = withStepParams(step, {\n beat,\n cta,\n slot: 'primary',\n stores,\n hostActions,\n params: {},\n })\n\n await step.prepare?.(ctx)\n\n // Read after the load; `disabled` also covers a press made meanwhile (pending).\n const { disabled, presentation } = resolveCtaState(ctx)\n\n if (disabled || presentation) return\n\n if (step.run) {\n const { run, onSuccess = [] } = step\n\n if (!(await withCtaStatus(ctx, () => runHandlerWithEffects(ctx, run, onSuccess)))) return\n }\n\n // Never replaces a drawer the user opened meanwhile.\n if (opensDrawer(step, cta) && !stores.insightsV2Store.actionDrawer.isDrawerOpen) {\n stores.insightsV2Store.openActionDrawer({\n guid: beat.guid,\n trigger: 'inDescriptionCta',\n cta: { block: cta.block, variant: cta.variant, action_key: cta.action_key },\n })\n }\n } catch (error) {\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n handler: 'runInDescriptionCta',\n })\n }\n}\n\n/** Placeholder CTA for handlers run from inside a drawer, where no card slot is involved. */\nconst DIRECT_CTA = { label: '', block: '' }\n\n/** Runs a handler directly (drawer Save buttons). True on success. */\nexport const runCtaHandler = (\n ctx: CtaHandlerContext,\n handlerName: CtaHandlerName,\n { onSuccess = [], analytics }: RunHandlerOptions = {},\n): Promise<boolean> => {\n const fullCtx: CtaContext = { ...ctx, cta: DIRECT_CTA, slot: 'primary' }\n\n return withCtaStatus(\n fullCtx,\n async () => {\n await runHandlerWithEffects(fullCtx, handlerName, onSuccess)\n\n // Success-time analytics for drawer Saves.\n if (analytics) sendCtaAnalytics(fullCtx, analytics)\n },\n { handler: handlerName },\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-cartographer/experiences",
3
- "version": "9.7.89",
3
+ "version": "9.7.91",
4
4
  "description": "Library containing experience widgets",
5
5
  "author": "MX",
6
6
  "license": "MIT",