Skip to content

feat(shared): add digest upsell banners to notifications and bookmarks pages#5630

Open
capJavert wants to merge 8 commits intomainfrom
feat/digest-upsell-banners
Open

feat(shared): add digest upsell banners to notifications and bookmarks pages#5630
capJavert wants to merge 8 commits intomainfrom
feat/digest-upsell-banners

Conversation

@capJavert
Copy link
Contributor

@capJavert capJavert commented Mar 3, 2026

Show digest subscription CTA banners for non-Plus users without an active digest. Notifications page gets a full-width banner; bookmarks page gets a rounded card above the feed. Both persist dismissal via IndexedDB and log impression/click events.

Changes

Events

Did you introduce any new tracking events?

Experiment

Did you introduce any new experiments?

Manual Testing

Caution

Please make sure existing components are not breaking/affected by this PR

Preview domain

https://feat-digest-upsell-banners.preview.app.daily.dev

…s pages

Show digest subscription CTA banners for non-Plus users without an active digest.
Notifications page gets a full-width banner; bookmarks page gets a rounded card
above the feed. Both persist dismissal via IndexedDB and log impression/click events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@capJavert capJavert self-assigned this Mar 3, 2026
@vercel
Copy link

vercel bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview Mar 3, 2026 5:52pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Mar 3, 2026 5:52pm

Request Review

Unit tests cover rendering conditions (Plus, digest, dismissed, loading),
impression/click analytics, CTA subscription, and dismiss behavior.
E2E tests verify banner visibility and dismissal on notifications and bookmarks pages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace usePersistentContext (client-only IndexedDB) with useActions
(server-side, tied to user account) for banner dismissal. Both dismiss
and subscribe now complete the action so banners are shown only once.

Adds DismissDigestUpsell and DismissDigestBookmarkUpsell to ActionType.
Removes unused PersistentContextKeys and updates tests accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only show DigestUpsellBanner on notifications page when push
notifications are already subscribed, ensuring it never renders
simultaneously with the EnableNotification CTA.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use shared ActionType.DigestUpsell for both banners
- Fix notification settings overwrite with setNotificationStatusBulk
- Add isAuthReady gate to prevent flash before auth resolves
- Show bookmark banner only when feed is empty
- Center bookmark banner on desktop
- Update toast and bookmark banner copy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Log click event with action: dismiss when user closes the banner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@capJavert
Copy link
Contributor Author

@claude review

@claude
Copy link
Contributor

claude bot commented Mar 5, 2026

Claude finished @capJavert's task in 1m 27s —— View job


Code Review

  • Gather context and read changed files
  • Review component implementations
  • Review tests
  • Review hooks and utilities
  • Post final review

Overall the feature is solid and the dismissal/impression tracking pattern is well done. A few issues to address:

Critical: E2E tests check for text that doesn't exist in the components

packages/playwright/tests/digest-upsell.spec.ts looks for text that doesn't match the rendered components:

Test file text Actual component text
'Get your personalized digest' (lines 38, 77) 'Get the must-read posts delivered daily'
'Never miss the best posts' (lines 58, 93) 'Not sure what to read? Let us pick for you'

The E2E tests will silently pass (they use isVisible().catch(() => false) guards) but will never actually validate the banner content. Fix this →

Architecture: Near-complete duplication between the two banner components

DigestUpsellBanner.tsx and DigestBookmarkBanner.tsx are ~95% identical. Per repo guidelines, copy-pasted logic should be extracted. The only real differences are:

  • The wrapper div className (full-width vs rounded card)
  • The title/description text
  • The target_id used for logging

This should be a single DigestUpsellBanner component that accepts props for the visual variant and target_id:

// Proposed shared component
interface DigestUpsellBannerProps {
  targetId: TargetId;
  title: string;
  description: string;
  className?: string;
}

The same duplication applies to the two spec files — they're identical except for the component name, rendered text, and target_id assertions.

Fix this →

Minor: completeAction not awaited in onDismiss

In both banners, onEnable awaits completeAction but onDismiss does not:

// DigestUpsellBanner.tsx:93 / DigestBookmarkBanner.tsx:93
const onDismiss = () => {
  logEvent({ ... });
  completeAction(ActionType.DigestUpsell); // ← not awaited, but onEnable does await it
};

This is inconsistent and fire-and-forget on dismissal. While unlikely to cause a visible bug, it means a rapid re-render could briefly re-show the banner before the action is persisted.

Minor: No error handling in onEnable

If subscribePersonalizedDigest rejects, execution stops silently — completeAction and displayToast won't run, leaving the user without any feedback. A try/catch with an error toast would improve resilience.

Minor: PR description inaccuracy

"Both persist dismissal via IndexedDB"

Dismissal actually uses the server-side ActionType / completeAction system, not IndexedDB. Worth updating the description to avoid confusion.

Nit: DigestBookmarkBanner only renders on empty feed

BookmarkFeedLayout.tsx:257:

{!plusEntryBookmark && isEmptyFeed && <DigestBookmarkBanner />}

This is intentional but not mentioned in the PR description. Worth a comment if the "only on empty bookmarks" behaviour is deliberate product intent.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant