From 6e64027921d932d56d037ac6c7c7acbeb9b05d2d Mon Sep 17 00:00:00 2001 From: David Montero Date: Tue, 11 Aug 2026 18:37:23 +0200 Subject: [PATCH] feat(news): interaction seam for What's New (impression/open/link_click/video_play) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New lib/newsEvents.ts registry (same pattern as newsSource.ts): OSS default is a no-op sink; an extension overlay can register one to forward the signals to its own analytics. NewsAnnouncer reports the toast impression and modal opens (toast vs Help menu); NewsMarkdown grows an optional onInteract callback fired on body link clicks and YouTube preview plays — the admin authoring preview passes nothing, so authoring never counts as engagement. --- .../src/components/layout/NewsAnnouncer.tsx | 16 +++++- .../src/components/layout/NewsMarkdown.tsx | 45 +++++++++++++-- frontend/src/lib/newsEvents.ts | 56 +++++++++++++++++++ 3 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 frontend/src/lib/newsEvents.ts diff --git a/frontend/src/components/layout/NewsAnnouncer.tsx b/frontend/src/components/layout/NewsAnnouncer.tsx index 18b71ae6..49b9ae57 100644 --- a/frontend/src/components/layout/NewsAnnouncer.tsx +++ b/frontend/src/components/layout/NewsAnnouncer.tsx @@ -25,6 +25,7 @@ import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { getNextNews, type NewsPost } from '../../lib/newsSource'; +import { reportNewsEvent } from '../../lib/newsEvents'; import { NewsMarkdown } from './NewsMarkdown'; import { registerEditorCommand } from '../../lib/editorCommands'; import './NewsAnnouncer.css'; @@ -46,6 +47,7 @@ export function NewsAnnouncer() { if (cancelled || !p) return; setPost(p); setToastVisible(true); + reportNewsEvent('impression', p.id); }); }, FETCH_DELAY_MS); return () => { @@ -62,6 +64,7 @@ export function NewsAnnouncer() { return registerEditorCommand('help.whatsNew', () => { setToastVisible(false); setExpanded(true); + reportNewsEvent('open', post.id, { via: 'menu' }); }); }, [post]); @@ -101,7 +104,17 @@ export function NewsAnnouncer() {
- {post.body_md} + + reportNewsEvent( + kind === 'video' ? 'video_play' : 'link_click', + post.id, + { href }, + ) + } + > + {post.body_md} +