// LinkedText.jsx — roadmap #28 (Parts 1–3). // // Renders a backend-provided list of text/link segments (see // backend/app/rfc_links.py). References in PR descriptions and comments // arrive pre-scanned as structured segments — this component maps them // onto plain text runs, anchors, and inline affordances. It never renders // HTML from the server (no dangerouslySetInnerHTML), so the surface is // XSS-safe regardless of what a comment author typed. // // Segment types: // * `rfc` — Part 1: a link to an accepted (active) RFC. // * `rfc-pending` — Part 3: the term names a pending (super-draft) // RFC; a signed-in viewer who isn't its owner gets // an inline "ask to contribute" affordance routing // to the contribute form (App reads `?contribute=`). // * `rfc-candidate` — Part 2: a strong-candidate term with no RFC yet; // a viewer with create rights (`canCreate`) gets a // "create RFC" affordance routing to the propose // flow pre-filled (App reads `?propose=`). // // Affordances degrade to plain text when the viewer lacks the relevant // right, so the visible prose is identical for everyone — only the // offered actions differ. // // `segments` is the enriched array; `text` is the raw fallback used when // the field is absent (an older cached response, or a caller that didn't // pass segments). import { Link } from 'react-router-dom' import { entryPath, useProjectId } from '../lib/entryPaths' export default function LinkedText({ segments, text, viewer, canCreate }) { const pid = useProjectId() if (!Array.isArray(segments) || segments.length === 0) { return <>{text ?? ''} } // A signed-in, beta-granted viewer can ask to contribute; the backend // re-checks ownership/collaborator status and rejects self-requests. const canContribute = !!viewer && viewer.permission_state === 'granted' return ( <> {segments.map((seg, i) => { if (seg.type === 'rfc') { return ( {seg.label} ) } if (seg.type === 'rfc-pending') { const who = seg.owner || 'Someone' return ( {seg.label} {canContribute && ( ask to contribute )} ) } if (seg.type === 'rfc-candidate') { const term = seg.term || seg.label return ( {seg.label} {canCreate && ( + create RFC )} ) } return {seg.text} })} ) }