feat(web): add Go campaign entries and route upgrades to Pricing #19168
Annotations
2 errors
|
Web workspace tests
Process completed with exit code 1.
|
|
Web workspace tests:
apps/web/tests/campaigns/deepseek-v4-flash.test.ts#L155
AssertionError: expected 'import { useEffect, useId, useState }…' to contain 'goCopy.cta'
- Expected
+ Received
- goCopy.cta
+ import { useEffect, useId, useState } from 'react';
+ import { createPortal } from 'react-dom';
+ import { Button, Dialog } from '@open-design/components';
+ import {
+ DEEPSEEK_V4_FLASH_CAMPAIGN as campaign,
+ formatDeepSeekV4FlashCampaignCountdown,
+ type DeepSeekV4FlashCampaignAudience,
+ } from '../campaigns/deepseek-v4-flash';
+ import { GO_PLAN_CAMPAIGN, GO_PLAN_PRICING_URL } from '../campaigns/go-plan';
+ import { useAnalytics } from '../analytics/provider';
+ import {
+ trackDeepSeekCampaignModalClick,
+ trackDeepSeekCampaignModalSurfaceView,
+ } from '../analytics/events';
+ import { useI18n } from '../i18n';
+ import { Icon } from './Icon';
+ import styles from './DeepSeekV4FlashCampaign.module.css';
+
+ interface Props {
+ /**
+ * paid = an active personal/team subscription; unpaid = no active
+ * subscription (including users who previously recharged their wallet).
+ */
+ audience: DeepSeekV4FlashCampaignAudience;
+ /**
+ * Whether the home view is the ACTIVE entry view. EntryShell keeps HomeView
+ * permanently mounted behind `display:none` while this dialog portals to
+ * `document.body`, so without this gate the campaign would escape the home
+ * view and interrupt projects/tasks/plugins/... routes. The requirement is
+ * explicit: the modal shows on #/home only.
+ */
+ active?: boolean;
+ /**
+ * Performs the REAL workbench switch for the paid 立即使用 CTA (产品拍板
+ * D5): agent to `amr`, model to the campaign model. EntryShell provides the
+ * same onAgentChange/onAgentModelChange pair the InlineModelSwitcher
+ * persists through.
+ */
+ onUseCampaignModel?: (agentId: string, modelId: string) => void;
+ /**
+ * Telemetry opt-in (config.telemetry.metrics). Gates the AMR analytics
+ * mirror of the recorded entry AND the od_device_id on the plans URL —
+ * the same treatment the workbench badge and the model-switcher upgrade
+ * already apply to this campaign's other touchpoints.
+ */
+ metricsConsent?: boolean;
+ /** config.installationId — the preferred consent-gated AMR join key. */
+ installationId?: string | null;
+ }
+
+ function hasSeenCampaign(campaignId: string): boolean {
+ if (typeof window === 'undefined') return true;
+ try {
+ return window.localStorage.getItem(`open-design:campaign-seen:${campaignId}`) === '1';
+ } catch {
+ // Fail closed: when the store is unreadable (private mode, disabled
+ // localStorage) `markCampaignSeen` cannot persist either, so answering
+ // "unseen" would re-open the modal on every mount. The campaign promise
+ // is one appearance per window — suppress instead of spamming.
+ return true;
+ }
+ }
+
+ function markCampaignSeen(campaignId: string): void {
+ try {
+ window.localStorage.setItem(`open-design:campaign-seen:${campaignId}`, '1');
+ } catch {
+ // Campaign frequency control is advisory; storage failures must not block Home.
+ }
+ }
+
+ /**
+ * Visual confirmation of the switch `onUseCampaignModel` already performed:
+ * pulse the composer's model chip. Deliberately no `chip.click()` — the model
+ * is switched for real, so opening the picker would only ask the user to redo
+ * a choice that has already been made.
+ */
+ function highlightModelSwitcher(): void {
+ const chip = document.querySelector<HTMLButtonElement>(
+ '[data-testid="inline-model-switcher-chip"]',
+ );
+ if (!chip) return;
+ chip.setAttribute('data-campaign-highlight', 'true');
+ window.setTimeout(() => chip.removeAttribute('data-campaign-highlight'), 1_500);
+ }
+
+ export function DeepSeekV4FlashCampaign({
+ audience,
+ active = true,
+ onUseCampaignModel,
+ metricsConsent = false,
+ installationId = null,
+ }: Props) {
+ const { locale, t } = useI18n();
+ const isZh = locale === 'zh-CN' || locale === 'zh-TW';
+ const analytics = useAnalytics();
+ const [modalOpen, setMod
|
background
wait
wait-all
cancel
parallel
Loading