Skip to content

Fixed displayed gift link being incorrect on first modal open - #29019

Merged
jonatansberg merged 2 commits into
mainfrom
codex/fix-gift-link-modal-duplicate-ensure
Jul 8, 2026
Merged

Fixed displayed gift link being incorrect on first modal open#29019
jonatansberg merged 2 commits into
mainfrom
codex/fix-gift-link-modal-duplicate-ensure

Conversation

@kevinansfield

@kevinansfield kevinansfield commented Jul 1, 2026

Copy link
Copy Markdown
Member

ref https://linear.app/ghost/issue/BER-3762/

Summary

  • Guarded the gift-link modal ensure effect by resource/post target so StrictMode effect replay and local re-renders do not issue duplicate PUT requests.
  • Kept the existing cancelled callback guard so closed/stale requests do not update modal UI or show stale errors.
  • Added focused regression coverage for StrictMode replay, close/reopen, and changing post ids while open.

Why

Opening the modal could fire two same-moment ensure requests. The API's last-write-wins tradeoff could then leave the modal displaying a token that was no longer the active gift link.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 48313ae6-ed44-4b8d-9f3a-26d13e29abdf

📥 Commits

Reviewing files that changed from the base of the PR and between 4f1792d and a666596.

📒 Files selected for processing (2)
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
  • apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx

Walkthrough

GiftLinkModal now caches the active ensure request for a {resource, postId} pair while the modal is open, clears that cache on close, and reuses it on subsequent renders for the same pair. The test suite adds mocked hooks and verifies the ensure request behavior under StrictMode, after close/reopen, and when either postId or resource changes.

Possibly related PRs

  • TryGhost/Ghost#28897: Introduces the GiftLinkModal flow and useEnsureGiftLink usage that this PR refines.

Suggested labels: ok to merge for me

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main fix: the gift link shown on first modal open was incorrect.
Description check ✅ Passed The description accurately summarizes the gift-link modal fix and regression coverage added in this change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-gift-link-modal-duplicate-ensure

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit a666596

Command Status Duration Result
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗
nx run-many -t test:unit -p @tryghost/posts,@tr... ✅ Succeeded 2m 16s View ↗
nx run @tryghost/admin:build ✅ Succeeded 55s View ↗
nx run-many -t lint -p @tryghost/posts,@tryghos... ✅ Succeeded 35s View ↗
nx run ghost:build:assets ✅ Succeeded 2s View ↗
nx run ghost:build:tsc ✅ Succeeded 6s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-08 12:30:37 UTC

@kevinansfield kevinansfield changed the title [codex] Fixed duplicate gift link modal upserts Fixed displayed gift link being incorrect on first modal open Jul 1, 2026
@kevinansfield
kevinansfield force-pushed the codex/fix-gift-link-modal-duplicate-ensure branch from c6b42f0 to 5c55cc1 Compare July 1, 2026 17:34
@kevinansfield
kevinansfield marked this pull request as ready for review July 1, 2026 17:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c55cc1c2f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx (1)

44-82: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Gate the async callbacks on the current key (apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx:44-82)

In dev StrictMode, the first effect cleanup flips cancelled to true, the replayed effect returns early on the same ref key, and the only in-flight promise drops its finally, leaving ensuring stuck. Compare against lastEnsuredLinkKeyRef.current at resolution time instead of a per-run cleanup flag so stale requests can be ignored without disabling the modal forever.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx` around lines
44 - 82, The async callbacks in gift-link-modal’s useEffect are gated by a
per-run cancelled flag, which breaks under StrictMode replay and can leave
ensuring stuck. Update the effect in GiftLinkModal to validate against
lastEnsuredLinkKeyRef.current at promise resolution time for ensureGiftLink, so
stale requests are ignored without preventing the latest open target from
clearing ensuring. Keep the existing key logic based on resource and postId, but
use that current-key check in the then/catch/finally handlers instead of relying
on cleanup-only cancellation.
🧹 Nitpick comments (1)
apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx (1)

81-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test doesn't verify the token actually gets applied after StrictMode replay.

This test only checks mockEnsureGiftLink call count/args, not that the resolved token is reflected in the UI (e.g., the copy button becoming enabled / "Generating link…" placeholder clearing). As noted in the review of gift-link-modal.tsx, the current guard can call ensureGiftLink once yet still discard its response under StrictMode replay — this test would pass even with that regression present.

Consider asserting on screen.getByTestId('copy-gift-link') (e.g., checking it becomes enabled, or that the resolved URL appears) after the waitFor, so the test actually validates the fix's goal rather than just the call count.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx` around
lines 81 - 92, The StrictMode test for GiftLinkModal only verifies
ensureGiftLink is called once, but it should also confirm the returned token is
applied to the UI after replay. In the gift-link-modal.test.tsx case around
GiftLinkModal and mockEnsureGiftLink, extend the assertion after waitFor to
check the rendered state changes (for example, the copy-gift-link control
becomes enabled or the loading placeholder disappears), so the test proves the
resolved link is actually used.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx`:
- Around line 44-82: The async callbacks in gift-link-modal’s useEffect are
gated by a per-run cancelled flag, which breaks under StrictMode replay and can
leave ensuring stuck. Update the effect in GiftLinkModal to validate against
lastEnsuredLinkKeyRef.current at promise resolution time for ensureGiftLink, so
stale requests are ignored without preventing the latest open target from
clearing ensuring. Keep the existing key logic based on resource and postId, but
use that current-key check in the then/catch/finally handlers instead of relying
on cleanup-only cancellation.

---

Nitpick comments:
In `@apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx`:
- Around line 81-92: The StrictMode test for GiftLinkModal only verifies
ensureGiftLink is called once, but it should also confirm the returned token is
applied to the UI after replay. In the gift-link-modal.test.tsx case around
GiftLinkModal and mockEnsureGiftLink, extend the assertion after waitFor to
check the rendered state changes (for example, the copy-gift-link control
becomes enabled or the loading placeholder disappears), so the test proves the
resolved link is actually used.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b1e4ddd5-a21e-499a-8d21-27ae098c0f58

📥 Commits

Reviewing files that changed from the base of the PR and between a0d69a9 and 5c55cc1.

📒 Files selected for processing (2)
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
  • apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx

@kevinansfield
kevinansfield force-pushed the codex/fix-gift-link-modal-duplicate-ensure branch from 5c55cc1 to 962ea5b Compare July 1, 2026 17:46
ref https://linear.app/ghost/issue/BER-3762/

Opening the gift link modal in React StrictMode could replay the ensure effect and issue competing upsert requests, leaving the UI with the wrong last-written token.
@kevinansfield
kevinansfield force-pushed the codex/fix-gift-link-modal-duplicate-ensure branch from 962ea5b to 4f1792d Compare July 1, 2026 17:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx`:
- Around line 38-64: The request dedup cache in gift-link-modal’s useEffect only
keys on postId, so a changed resource can incorrectly reuse a stale
ensureGiftLink request. Update the cached request shape around
ensureGiftLinkRequestRef and the reuse check in the effect to include resource
as part of the identity, so a resource change creates a fresh
ensureGiftLink({id: postId, resource}) request instead of reusing the previous
one.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0bf97b7c-115c-4093-b9db-79bb8cfcc79e

📥 Commits

Reviewing files that changed from the base of the PR and between 962ea5b and 4f1792d.

📒 Files selected for processing (2)
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
  • apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/posts/test/unit/views/post-analytics/gift-link-modal.test.tsx

Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
ref https://linear.app/ghost/issue/BER-3762/

- the ensure effect re-runs when the resource prop changes, but the cached request was keyed by post id only, so a resource change with the same id would reuse a request made for the other resource
- unreachable today because every modal host pins or remounts per resource, but the cache key now matches what the effect dependencies claim
@jonatansberg
jonatansberg enabled auto-merge (squash) July 8, 2026 12:30
@jonatansberg
jonatansberg merged commit dbd20a0 into main Jul 8, 2026
41 checks passed
@jonatansberg
jonatansberg deleted the codex/fix-gift-link-modal-duplicate-ensure branch July 8, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants