v0.29.0: #28 Parts 2+3 — create-RFC offers + contribute-to-pending requests
Extends the v0.26.0 (#28 Part 1) read-time scanner into three buckets in one pass — active link (Part 1), pending-RFC contribute offer (Part 3), create-RFC offer (Part 2) — precedence active > pending > candidate. The backend still emits only structured segments (never HTML), so the surface stays XSS-safe by construction. Part 2 — create-RFC offers: a multi-word tag from the #27 taxonomy with no defining RFC renders, for a create-rights viewer, as an inline "+ create RFC" affordance that opens the propose modal pre-filled (?propose=<term>; ProposeModal gained initialTitle). Conservative multi-word gate; broader heuristics + the Haiku path are deferred. Part 3 — contribute-to-pending offers: a term matching a super-draft renders, for a signed-in non-owner, an "ask to contribute" affordance with the owner's display name. It opens a 3-field request form (who/why/optional use-case); submitting lands a contribution_requests row (migration 024) and one actionable §15 notification per owner (new kind contribution_request_on_pending_rfc, personal-direct). The owner's inbox shows who/why/use-case inline with Accept/Decline. Accept fires #12's owner-invite flow with the requester as invitee and echoes a notification back; decline notifies the requester. Pre-merge idea PRs are out of scope. New endpoints: GET /api/rfcs/{slug}/contribution-target, POST /api/rfcs/{slug}/contribution-requests, .../{id}/accept, .../{id}/decline. The invite issue path was refactored into one reusable api_invitations.issue_invitation(...) chokepoint shared by the manual invite endpoint and Part 3's accept. Tests: 9 new (3 scanner-bucket unit + 6 e2e). Full suite 374 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -191,7 +191,7 @@ export default function RFCDiscussionPanel({ slug, viewer }) {
|
||||
</div>
|
||||
)}
|
||||
{activeMessages.map(msg => (
|
||||
<DiscussionMessage key={msg.id} message={msg} />
|
||||
<DiscussionMessage key={msg.id} message={msg} viewer={viewer} />
|
||||
))}
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
@@ -258,7 +258,7 @@ export default function RFCDiscussionPanel({ slug, viewer }) {
|
||||
)
|
||||
}
|
||||
|
||||
function DiscussionMessage({ message }) {
|
||||
function DiscussionMessage({ message, viewer }) {
|
||||
const isSystem = message.role === 'system'
|
||||
if (isSystem) {
|
||||
return (
|
||||
@@ -281,7 +281,7 @@ function DiscussionMessage({ message }) {
|
||||
<div className="discussion-message-quote">"{message.quote}"</div>
|
||||
)}
|
||||
<div className="discussion-message-body">
|
||||
<LinkedText segments={message.text_segments} text={message.text} />
|
||||
<LinkedText segments={message.text_segments} text={message.text} viewer={viewer} canCreate={viewer?.permission_state === 'granted'} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user