Skip to content

feat(pro): pre-launch — announcement banner, locked Pro button, "coming soon" Sounds; renewal reminder only in the last 60 days - #147

Merged
ShuhaoZQGG merged 8 commits into
devfrom
feat/pre-launch-teaser
Nov 14, 2026
Merged

ShuhaoZQGG merged 8 commits into
devfrom
feat/pre-launch-teaser

Conversation

@ShuhaoZQGG

@ShuhaoZQGG ShuhaoZQGG commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Closes # (no issue; requested directly before merging dev to master)

Why

Production runs with sounds on and pro off until the Pro launch. Since #126 the Sounds button always shows. For everyone in production it was locked with "Sounds are a Pro feature", with no way to buy Pro yet. We also want to tell users a new look and Pro are coming, and to be able to announce other things the same way later. The footer's Pro button should stay visible before the launch and say what's coming. The club renewal reminder was showing "Pro ends in 316 days" on a club set not to renew.

What changes

  • Sounds lock text. The button and modal still show, locked. The lock now says "Hold tight! Sounds are coming soon." whenever sounds or pro is off. "Sounds are a Pro feature. Upgrade to Pro to use them." appears only when both flags are on and the plan isn't Pro, i.e. when buying Pro would unlock it. This reverses fix(pro): make the pro flag the source of truth for Pro in the UI #127's choice that a paying user with pro off sees the Pro lock. Same rule in the web app.

  • Reusable announcement banner. One-line notices above the tabs come from a list, ANNOUNCEMENTS in packages/shared/announcements.js. To announce something, add an entry with a new id:

    • lead and text
    • tone (info / success / warning)
    • surfaces (zoom / web)
    • optional starts / ends dates
    • an optional when({ flags }) condition, so a notice can follow a release flag and be switched from PostHog without a deploy
    • an optional link

    The first entry that applies shows, and a close is remembered per announcement on that device (toastmaster_announcements_dismissed), so a new id reaches everyone again. The banner is presentational and shared (packages/ui/AnnouncementBanner.jsx); the Zoom app wires it up (apps/zoom-app/src/components/AnnouncementBanner.jsx). Events: announcement_shown, announcement_dismissed and announcement_link_opened, each with announcement_id.

  • Locked Pro button (Zoom app footer). While pro is off, the footer keeps a grey "Pro" with a lock where the Pro / Upgrade button goes. Pointing at it or clicking it opens a card: "Pro is coming soon", with sounds, club card sets on every club timer, voting for Best Table Topics and Best Evaluator, "and more", and "The timer itself stays free." A click keeps it open until a click elsewhere or Escape. Event: pro_coming_soon_opened with trigger. Once pro is on, it's the real button again.

  • Club renewal reminder. "'s Pro ends in N days" now shows for a club that will not renew (set to cancel, or cancelled) only in its last 60 days, and only when the end date is known. A failed payment is still warned about at once. clubLifecycle gains reason: 'payment_failed' | 'not_renewing'. Applies to both apps (shared clubGraceReminder).

  • First announcement: pre-launch-2026-11, "Coming soon: a fresh new look for the timer, and Pro features for your club.", shown in the Zoom app only while pro is off, so it goes away by itself at launch.

Security

None. No routes, no data leaves the device beyond PostHog events.

Production impact

After this reaches master, every production user sees the pre-launch notice (pro is 0% except one targeted account), a "coming soon" Sounds lock and the locked Pro button. Clubs set not to renew stop seeing the renewal line until their last 60 days. No flag changes needed. At launch, turning pro on removes the notice and restores the Pro lock for free users. Rollback: revert.

Testing

  • npm test: 119 files, 2306 tests pass (new: announcements.test.js 14, AnnouncementBanner.test.jsx 7, ProComingSoon.test.jsx 4; Footer.test.jsx +2; club.test.js +3)
  • npm run test:e2e: 50 pass (e2e/zoom.announcement-banner.spec.js 3, e2e/zoom.pro-coming-soon.spec.js 2; e2e/web.timer-sounds.spec.js updated, 1 new)
  • npm run test:specs: 11 specs valid
  • Oracle: n/a (not a factory issue)

Evidence (real Zoom client)

Zoom 7.2.5, macOS 26.7.2, branch deployed to dev; dev restored to its branch tip afterwards.

  • announcement-banner on cb49176 (test-runs/2026-11-14-announcement-banner/run.md, local): AB-01 ✅ no notice with pro on (Pro chip shows; the new close key never written, so the flag alone hid it) · AB-02 ✅ shared banner above the tabs, two lines, styles intact · AB-03 ✅ speech to green, stop, reset with the banner up · AB-04 ✅ × hides it at once; still gone after Close all and reopen.
  • pro-coming-soon (test-runs/2026-11-14-pro-coming-soon/run.md, local): PC-01 ❌ on 5305bc9: "Send Us Feedback" wrapped to two lines. Fixed in 4d95d3d (the lock takes the sparkle's place), then PC-01 ✅ one line · PC-02 ✅ hover opens the card inside the panel, leaving closes it · PC-03 ✅ a click keeps it open; a click elsewhere closes it · PC-04 ✅ with pro on, the amber Pro button and no lock.
  • club-renewal-reminder:
    • CR-01 ✅ in Zoom (same run): the test club is set not to renew with 315 days left, and no renewal line shows with pro off or on (before: "Pro ends in 316 days").
    • CR-02 ✅ and CR-03 ✅ on 51534f9 (test-runs/2026-11-14-club-renewal-reminder/run.md, local), with the test club's record edited in dev KV (approved, backed up, restored and read back identical):
      • +29.5 days → "Claude Test Club's Pro ends in 30 days. Ask your club admin to renew."
      • past_due, period ended 2 days ago → "…ends in 5 days…" straight away.
      • × hides it for the day.
    • CR-02 and CR-03 ran on the web app as a second club device. The Zoom client only re-reads its club after 24 h, and forcing that would have reset the token the PR fix(club): keep club devices in their club past a day between refreshes #145 session is waiting to test tonight. The banner and the rule are shared code.
  • timer-sounds TS-09 on 60e1b6e (test-runs/2026-11-14-pre-launch-teaser/run.md, local): ✅ with pro off, the tooltip and modal say "Hold tight! Sounds are coming soon.", with no "Pro feature" text, no Upgrade link, disabled controls and no Pro chip.

Not verified

  • TS-01 (free plan, both flags on → Pro lock): the test device is on Pro through Claude Test Club, so Sounds are usable there. Unchanged by this PR; covered by TimerSounds.test.jsx and web.timer-sounds.spec.js.
  • TS-08 (pro on, sounds off): needs per-uid PostHog targeting; this session's PostHog key has no feature_flag:write. Text change only; a unit test covers it.
  • TS-05 and the human audio steps (TS-02/03/04/06/07): untouched by this change.
  • CR-02 / CR-03 inside the Zoom client itself (run on the web app; see Evidence).
  • timer-sounds is marked stale for TS-01/TS-08, hence needs:zoom-test.
  • club-card-sets covers club.js, so it now reads as unverified (--stale). Reviewed: card sets are untouched. It still needs a rerun before the next release (--stale --strict).
  • An announcement's link, dates and non-blue tones haven't been seen in Zoom (no entry uses them yet); unit and e2e tests cover them.
  • Found in passing, not changed here: the club grace banner shows "Pro ends in 316 days" on the test device even with pro off.

Look here first

  • packages/shared/announcements.js: the list and pickAnnouncement.
  • packages/shared/club.js (clubGraceReminder): the 60-day window.
  • apps/zoom-app/src/components/ProComingSoon.jsx: hover vs pinned.
  • apps/zoom-app/src/hooks/useSounds.js: the new lock rule ('pro' only when both flags are on).

…can be bought

Sounds stay visible and locked. The lock says "Hold tight! Sounds are coming
soon." whenever the sounds or pro flag is off, and "Sounds are a Pro
feature" only once buying Pro would unlock them. Same rule in the web app.

A dismissible banner above the tabs in the Zoom app says a new look and Pro
are coming. It shows only while the pro flag is known and off, so it goes away
by itself at launch.
@ShuhaoZQGG ShuhaoZQGG added the needs:zoom-test Factory: waiting for the real-Zoom station label Nov 14, 2026
The pre-launch banner becomes the first entry in ANNOUNCEMENTS
(packages/shared/announcements.js). To announce something, add an entry
with a new id: lead, text, tone, surfaces, optional dates, an optional
flag condition and an optional link. The Zoom app shows the first entry
that applies; a close is remembered per announcement on that device.

The banner itself is presentational and shared (packages/ui), so the web
app can render it later. Events become announcement_shown,
announcement_dismissed and announcement_link_opened, each with
announcement_id.
@ShuhaoZQGG ShuhaoZQGG changed the title feat(pro): pre-launch banner; Sounds say "coming soon" until Pro can be bought feat: reusable announcement banner (pre-launch notice first); Sounds say "coming soon" until Pro can be bought Nov 14, 2026
…er only in the last 60 days

Before the launch (pro off) the footer keeps a grey Pro button with a lock.
Pointing at it or clicking it lists what Pro brings: sounds, club card sets
on every club timer, voting for Best Table Topics and Best Evaluator, and
more. Once pro is on it is the real Pro / Upgrade button again.

The club renewal reminder now shows for a club that will not renew only in
its last 60 days, and only with a known end date. A failed payment is still
warned about at once.
@ShuhaoZQGG ShuhaoZQGG changed the title feat: reusable announcement banner (pre-launch notice first); Sounds say "coming soon" until Pro can be bought feat(pro): pre-launch — announcement banner, locked Pro button, "coming soon" Sounds; renewal reminder only in the last 60 days Nov 14, 2026
@ShuhaoZQGG
ShuhaoZQGG marked this pull request as ready for review November 14, 2026 20:56
@ShuhaoZQGG
ShuhaoZQGG merged commit 776ff3d into dev Nov 14, 2026
1 check passed
ShuhaoZQGG added a commit that referenced this pull request Nov 14, 2026
Resolves the spec-index conflict with dev (#147, #151): every row from both
sides, pro-launch kept after timer-sounds and in the run order. Footer.jsx
merged cleanly: with pro on, the Upgrade / Pro button with the launch dot;
with pro off, dev's locked 'Pro (coming soon)' button. pro-launch is marked
stale (the Sounds line and this merge came after its last run) and points to
pro-coming-soon for the pro-off footer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:zoom-test Factory: waiting for the real-Zoom station

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant