Add memo engagement, Local Docker-Compose rig (frontend)#16
Open
jnmclarty wants to merge 2 commits into
Open
Conversation
Author
|
See BuildCanada/york_factory#38 for backend. |
xrendan
reviewed
May 11, 2026
| variant="ghost" | ||
| onClick={() => setOpenKind("critique")} | ||
| > | ||
| Critique this memo |
Member
There was a problem hiding this comment.
Can we add some substansive copy about what makes a good critique?
We won't publish ad hominens.
In that vein, they should also directly address the memo content and propose a different alternative to the problem specified or challenge the premise of the memo itself.
We will not publish anything that is self-promotional or that tries to leverage the platform for someone's own personal gain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Memo engagement: Endorse + Critique buttons
Adds an engagement section at the bottom of every memo page with Endorse and Critique buttons. Both modals route the user through a LinkedIn OIDC popup hosted by york_factory; on success the modal collects a Canadian postal code (and a critique body) and submits to the new york_factory endpoints. Counts and the 5 most recent endorsers/approved critiques render server-side; the user's own action is reflected immediately via
optimistic update + on-demand cache revalidation.
What's in this PR
MemoEngagement.tsx(new client island,src/app/memos/[slug]/) — buttons, dialog, popup-driven LinkedIn OAuth, postal-code validation, optimistic insert,sonnertoasts. Critique bodies are cropped to the first sentence with a small expand-icon that opens the full body in a secondary modal — handles long critiques without dwarfing the page.api/revalidate/route.ts(new) — Bearer-token-protected endpoint that callsrevalidateTag(). Allows york_factory to bust the per-memo cache tag immediately when an endorsement is added or a critique is approved.fetchMemonow tags its fetch withmemo:<slug>so revalidation can target a single memo.apiPosthelper added tosrc/lib/api/client.ts(JSON,cache: 'no-store', throws typedApiPostErroron non-2xx so the modal can branch on409).YFMemoDetailextended withendorsements_count,critiques_count,recent_endorsers,critiques. Mapped throughfetchMemo.timeZone: "UTC") so SSR and client render identical strings.@base-ui/react/dialogpattern fromSubscribeModal.tsx. No new dependencies.New environment variables
REVALIDATE_SECRET/api/revalidate. Must match york_factory'sNEXTJS_REVALIDATE_SECRET.NEXT_PUBLIC_YORK_FACTORY_ORIGINhttps://yorkfactory.buildcanada.compostMessagefrom the LinkedIn callback popup. Optional — defaults to the origin parsed fromYORK_FACTORY_API_URL, override only if the API is reverse-proxied under a different public origin.Existing
YORK_FACTORY_API_URLis unchanged.Required york_factory env vars (cross-repo)
This PR depends on the corresponding york_factory PR being deployed and configured with:
LINKEDIN_CLIENT_IDLINKEDIN_CLIENT_SECRETLINKEDIN_REDIRECT_URI— must point to the york_factorylinkedin/callbackrouteLINKEDIN_POSTMESSAGE_ORIGIN— must equal the production TradingPost origin (e.g.https://buildcanada.com)NEXTJS_REVALIDATE_URL— must point to this app's/api/revalidateNEXTJS_REVALIDATE_SECRET— must equalREVALIDATE_SECREThereLinkedIn app registration steps live in the york_factory PR.
Deployment
This PR can ship independently of york_factory but the engagement buttons will display zero counts until york_factory's migrations + endpoints are live. Recommended order:
REVALIDATE_SECRETandNEXT_PUBLIC_YORK_FACTORY_ORIGIN(if needed) in Vercel project env.NEXTJS_REVALIDATE_URLandNEXTJS_REVALIDATE_SECRETin york_factory (Kamal secrets), then redeploy york_factory so the revalidator picks them up.Testing
Local (against live york_factory dev)
Then in york_factory:
# .env additions LINKEDIN_CLIENT_ID=... LINKEDIN_CLIENT_SECRET=... LINKEDIN_REDIRECT_URI=http://localhost:3000/api/v1/auth/linkedin/callback LINKEDIN_POSTMESSAGE_ORIGIN=http://localhost:5050 NEXTJS_REVALIDATE_URL=http://localhost:5050/api/revalidate NEXTJS_REVALIDATE_SECRET=dev-revalidate-secret bin/rails sOpen
http://localhost:5050/memos/<any-slug>and walk through the steps below.Manual flow
M5V 3A8→ toast "Thanks for endorsing this memo.", count increments, name appears at the top of the recent list./admin/critiques) → approve the critique → within ~1s the TradingPost memo page, on next refresh, shows the critique without the pending badge. VerifyrevalidateTagfired by checking the TradingPost server logs forPOST /api/revalidatewith200 { ok: true, revalidated: ["memo:<slug>"] }.12345→ client-side error "Please enter a valid Canadian postal code (e.g. A1A 1A1).", no network call.window.postMessage({type:"linkedin-verified", verifiedTicket:"x", payload:{}}, "*")from a tab on a different origin (or simulate by editingNEXT_PUBLIC_YORK_FACTORY_ORIGIN) — the modal should ignore it.Build / lint
Should be clean. The new route lives under
src/app/api/revalidate/route.tsand is opted out of static rendering (dynamic = "force-dynamic").Cache-bust smoke test
Rollback
git revert <merge-commit>and redeploy. The york_factory engagement endpoints can stay live — they're harmless without a frontend consumer. The/api/revalidateroute disappearing means cache-bust calls from york_factory will start failing with404; logged as a warning byNextjsRevalidator, no impact on user-facing requests.Notes
generateStaticParams(). The 5-minute ISR window (revalidate: 300infetchMemo) is now made fresh on demand by tag-based revalidation when engagement changes — so users see new endorsements/critiques within seconds, not minutes.fetchMemoEngagementhelper insrc/lib/api/memos.tsis exported but currently unused; it's there for a future variant of the page that wants always-fresh counts at SSR time.Screenshots
At the bottom of the memo:
Note that critiques are cropped to the first sentence.
Expanded Critique (pop-out modal)
Endorse/Critique Modals