diff --git a/src/components/TopicDetail/PaidResourceDisclaimer.tsx b/src/components/TopicDetail/PaidResourceDisclaimer.tsx new file mode 100644 index 000000000..ab466f535 --- /dev/null +++ b/src/components/TopicDetail/PaidResourceDisclaimer.tsx @@ -0,0 +1,30 @@ +import { useState } from 'react'; +import { X } from 'lucide-react'; + +type PaidResourceDisclaimerProps = { + onClose: () => void; +}; + +export function PaidResourceDisclaimer(props: PaidResourceDisclaimerProps) { + const { onClose } = props; + + return ( +
+ + +

+ Note on Premium Resources +

+

+ These are optional paid resources vetted by the roadmap team. +

+

+ If you purchase a resource, we may receive a small commission at no + extra cost to you. This helps us offset the costs of running this site + and keep it free for everyone. +

+
+ ); +} diff --git a/src/components/TopicDetail/TopicDetail.tsx b/src/components/TopicDetail/TopicDetail.tsx index 8336a7adc..769eb11a1 100644 --- a/src/components/TopicDetail/TopicDetail.tsx +++ b/src/components/TopicDetail/TopicDetail.tsx @@ -22,7 +22,7 @@ import type { RoadmapContentDocument, } from '../CustomRoadmap/CustomRoadmap'; import { markdownToHtml, sanitizeMarkdown } from '../../lib/markdown'; -import { Ban, FileText, HeartHandshake, Star, X } from 'lucide-react'; +import { Ban, Coins, FileText, HeartHandshake, Star, X } from 'lucide-react'; import { getUrlParams, parseUrl } from '../../lib/browser'; import { Spinner } from '../ReactIcons/Spinner'; import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx'; @@ -32,6 +32,7 @@ import { resourceTitleFromId } from '../../lib/roadmap.ts'; import { lockBodyScroll } from '../../lib/dom.ts'; import { TopicDetailLink } from './TopicDetailLink.tsx'; import { ResourceListSeparator } from './ResourceListSeparator.tsx'; +import { PaidResourceDisclaimer } from './PaidResourceDisclaimer.tsx'; type TopicDetailProps = { resourceId?: string; @@ -71,6 +72,8 @@ async function fetchRoadmapPaidResources(roadmapId: string) { return response; } +const PAID_RESOURCE_DISCLAIMER_HIDDEN = 'paid-resource-disclaimer-hidden'; + export function TopicDetail(props: TopicDetailProps) { const { canSubmitContribution, @@ -92,6 +95,9 @@ export function TopicDetail(props: TopicDetailProps) { const [links, setLinks] = useState([]); const toast = useToast(); + const [showPaidResourceDisclaimer, setShowPaidResourceDisclaimer] = + useState(false); + const { secret } = getUrlParams() as { secret: string }; const isGuest = useMemo(() => !isLoggedIn(), []); const topicRef = useRef(null); @@ -116,6 +122,10 @@ export function TopicDetail(props: TopicDetailProps) { return; } + setShowPaidResourceDisclaimer( + localStorage.getItem(PAID_RESOURCE_DISCLAIMER_HIDDEN) !== 'true', + ); + fetchRoadmapPaidResources(defaultResourceId).then((resources) => { setPaidResources(resources); }); @@ -475,6 +485,18 @@ export function TopicDetail(props: TopicDetailProps) { ); })} + + {showPaidResourceDisclaimer && ( + { + localStorage.setItem( + PAID_RESOURCE_DISCLAIMER_HIDDEN, + 'true', + ); + setShowPaidResourceDisclaimer(false); + }} + /> + )} )}