velxio/frontend/src/components/layout/NewsModal.tsx

72 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* "What's New" modal — shows the next pending product-news post when the
* user enters the editor. At most one per day, each post only ever once;
* the queue/seen logic lives in lib/newsSource.ts (OSS: localStorage over
* the local /api/news/feed proxy; pro overlay: server-backed per-user).
*
* The fetch is delayed a few seconds so it never competes with editor
* startup, and every failure path resolves to "no modal".
*/
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { getNextNews, type NewsPost } from '../../lib/newsSource';
import { NewsMarkdown } from './NewsMarkdown';
import './NewsModal.css';
const FETCH_DELAY_MS = 4000;
export function NewsModal() {
const [post, setPost] = useState<NewsPost | null>(null);
useEffect(() => {
let cancelled = false;
const timer = setTimeout(() => {
void getNextNews().then((p) => {
if (!cancelled && p) setPost(p);
});
}, FETCH_DELAY_MS);
return () => {
cancelled = true;
clearTimeout(timer);
};
}, []);
if (!post) return null;
return createPortal(
<div className="velxio-news-overlay" onClick={() => setPost(null)}>
<div
className="velxio-news-modal"
role="dialog"
aria-modal="true"
aria-label={post.title}
onClick={(e) => e.stopPropagation()}
>
<div className="velxio-news-header">
<div>
<span className="velxio-news-kicker">What's new</span>
<h2 className="velxio-news-title">{post.title}</h2>
</div>
<button
className="velxio-news-close"
aria-label="Close"
onClick={() => setPost(null)}
>
×
</button>
</div>
<div className="velxio-news-body">
<NewsMarkdown>{post.body_md}</NewsMarkdown>
</div>
<div className="velxio-news-footer">
<button className="velxio-news-ok" onClick={() => setPost(null)}>
Got it
</button>
</div>
</div>
</div>,
document.body,
);
}