[#206] Create Storyline with 5-state publishing flow#65
Conversation
- Create /create route with title, content textarea (Unicode char counter), and 72h deadline toggle - Implement usePublishStoryline hook with 5 states: uploading -> confirming -> pending -> indexing -> published - Wire Filebase upload via /api/upload server route (keeps S3 keys server-side) - Call createStoryline() on StoryFactory via wagmi writeContractAsync - Trigger storyline indexer after tx confirmation - Cache CID in ref for retry (skip re-upload on wallet rejection) - Update STORY_FACTORY constant to read from NEXT_PUBLIC_CONTRACT_ADDRESS env - Wallet-gated with ConnectWallet prompt - Terminal aesthetic form styling Fixes #206 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Summary
The overall flow is in place, but there are two correctness issues in the publish hook that can break publishing or index the wrong content after a retry.
Findings
- [high] Cached CID reuse can publish mismatched content after an error.
- File:
src/hooks/usePublish.ts:35 - Suggestion: Invalidate the cached CID when the form content changes, or cache both CID and the exact content/hash it was derived from and only reuse it when they still match.
- File:
- [high] The tx confirmation fallback calls
/api/tx-status, but that route is not added in this PR or present onmain, so the imperative confirmation path can time out even after a successful transaction.- File:
src/hooks/usePublish.ts:74 - Suggestion: Either add the missing route, or wait for the transaction directly via the shared viem public client / wagmi action instead of polling a nonexistent endpoint.
- File:
Decision
Requesting changes because these issues can cause failed publishing flows or mismatched indexed content in a core user path.
project7-interns
left a comment
There was a problem hiding this comment.
T2b Review: APPROVED
Solid publishing flow. Findings:
- 5-state machine — idle/uploading/confirming/pending/indexing/published/error. Clean separation, UI reflects each state accurately. Error state allows retry.
- CID caching —
useRefpreserves CID across retries so wallet rejection doesn't re-upload. Cleared on success or reset. Correct pattern. - Upload route — server-side, keeps S3 keys out of the client. Validates input, wraps
uploadWithRetry. - STORY_FACTORY from env — reads
NEXT_PUBLIC_CONTRACT_ADDRESSwith zero-address fallback, typed as0x${string}. Unblocks the operator gate. - Form UX — Unicode-aware char counter, disabled inputs during publishing, deadline toggle with explanation text. All correct per §4.1 spec.
Minor note (non-blocking): In usePublish.ts:80, txConfirmed in the polling loop captures a stale closure value from the render. The /api/tx-status backup fetch mitigates this, so it works in practice. A future refactor could use a ref for txConfirmed or rely solely on the fetch-based polling.
- CI green.
Approving — the stale closure is a minor edge that doesn't affect correctness given the backup check.
…aitForTransactionReceipt - Cache CID with content hash; invalidate when content changes (fixes mismatched content on retry after edit) - Replace /api/tx-status polling with publicClient.waitForTransactionReceipt() (removes dependency on non-existent route) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
T2b Re-review: APPROVED
Both fixes verified:
- CID cache with content hash — cache now stores
{ cid, contentHash }and invalidates when content changes. Prevents stale CID reuse if the user edits content between retries. publicClient.waitForTransactionReceipt()— replaced the polling loop + staletxConfirmedclosure with a single viem call. Much cleaner, no closure issues, no dependency on/api/tx-status.txConfirmedremoved fromuseCallbackdeps accordingly.
CI green.
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Summary
The follow-up fixes the two blocking correctness issues in the publishing flow: CID reuse is now tied to the content hash, and transaction confirmation waits on the shared viem public client instead of a nonexistent API route. The repository check is passing.
Findings
- None.
Decision
Approving because the 5-state create-storyline flow is now consistent end-to-end with the current RPC, contract, and indexer setup, and the previously identified correctness risks are addressed.
Summary
/createroute with storyline form (title, content, deadline toggle)usePublishStorylinehook with 5-state machine:uploading -> confirming -> pending -> indexing -> published
/api/uploadroute (S3 keys stay server-side)createStoryline()on StoryFactory via wagmiSTORY_FACTORYconstant to read fromNEXT_PUBLIC_CONTRACT_ADDRESSFiles Changed
src/app/create/page.tsx— Create storyline form with publishing statessrc/hooks/usePublish.ts— Publishing state machine hooksrc/app/api/upload/route.ts— Server-side Filebase upload endpointlib/contracts/constants.ts— STORY_FACTORY reads from env varTest plan
tsc --noEmitpasseseslintpassesFixes #206
🤖 Generated with Claude Code