Skip to content

feat: session hydration#4618

Merged
miguelpeixe merged 34 commits intotrunkfrom
feat/session-hydration
Apr 6, 2026
Merged

feat: session hydration#4618
miguelpeixe merged 34 commits intotrunkfrom
feat/session-hydration

Conversation

@miguelpeixe
Copy link
Copy Markdown
Member

@miguelpeixe miguelpeixe commented Apr 1, 2026

All Submissions:

Changes proposed in this Pull Request:

This PR introduces a mechanism to bridge the gap between authentication and the next page load. After login, a short-lived transient binds the newspack-cid cookie to the authenticated user ID, and a new REST endpoint (/reader/session) returns a wp_rest nonce for subsequent authenticated API calls.

This session hydration strategy is used by the reader data store and the newsletter sign-up modal to offer premium newsletters to the signed-in user.

The data store will no longer require a page refresh to sync pending data items, and the newsletter modal refresh endpoint will rely on authentication, simplifying its logic.

How it works:

  1. In the backend: any authentication (via Woo checkout registration or auth/registration modal), bind the session's newspack-cid to the $user_id for 2 minutes.
  2. In the frontend: detect the auth cookie and request the /reader/session endpoint to fetch a wp_rest nonce
  3. Once hydrated (nonce fetched), a triggered event gets picked up by the data store and newsletter modal, so they run their logic

The /reader/session response payload has a filter so components can initialize their state without additional requests. The reader data store leverages that filter to populate stored reader data items for db -> localStorage hydration.

How to test the changes in this Pull Request:

Simple authentication flow

  1. In a fresh session, open devtools to inspect network requests
  2. Sign in as an existing reader using the auth modal
  3. Confirm that after authenticating, a request is made to /reader/session with the following response:
{
    "nonce": "365493a533",
    "reader_data_items": {
        "is_donor": "false",
        "is_former_donor": "true",
        "pageviews": "{\"day\":{\"count\":22,\"start\":1775053485659},\"week\":{\"count\":22,\"start\":1775053485659},\"month\":{\"count\":22,\"start\":1775053485659}}",
        "is_newsletter_subscriber": "true",
        "newsletter_subscribed_lists": "[\"5\"]",
        "active_subscriptions": "[20157]",
        "article_view_per_week": "{\"1772247600000\":{\"3836\":true},\"1774062000000\":{\"4837\":true},\"1774666800000\":{\"4837\":true,\"8597\":true,\"20826\":true}}",
        "article_view_per_month": "{\"1772334000000\":{\"3836\":true},\"1775012400000\":{\"4837\":true,\"8597\":true,\"20826\":true}}",
    }
}

Reader data store hydration

  1. In the same session, confirm that the reader data items from the response above are now hydrated to localStorage
  2. Also confirm a successful request is made to /reader-data with the nonce as X-WP-Nonce header, syncing local items to the db

Premium newsletters

  1. Set up premium newsletters attached to a subscription product
  2. Make sure to check the premium lists in the "Present newsletter signup after checkout and registration" section of audience configuration
  3. In a fresh session, open the console and run newspackReaderActivation.openNewslettersSignupModal()
  4. Confirm the modal doesn't include the premium list(s) and close
  5. Go through the modal checkout flow to purchase the subscription that unlocks the lists
  6. Confirm that the post-checkout newsletters modal includes the list(s)

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable?
  • Have you successfully ran tests with your changes locally?

@miguelpeixe miguelpeixe requested a review from Copilot April 2, 2026 13:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements “session hydration” to bridge the gap between authentication and the next page load by fetching a fresh wp_rest nonce (and optionally initial payload) immediately after login, enabling the reader-data store and newsletters modal to update without requiring a refresh.

Changes:

  • Backend: add /newspack/v1/reader/session endpoint backed by a short-lived CID→user transient, plus a newspack_session_hydration_response filter for augmenting the response.
  • Frontend: add a session hydration module + EVENTS.session, rehydrate reader-data items and use hydrated nonce for syncing, and refresh the newsletters modal on hydration.
  • Tests: add unit tests for session hydration, and remove now-obsolete tests related to the removed content-restriction user-id override filter.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
includes/reader-activation/class-session-hydration.php Adds CID binding + REST endpoint to mint a fresh wp_rest nonce after auth, with response filter.
includes/class-newspack.php Loads the new session hydration class.
includes/reader-activation/class-reader-data.php Adds reader-data payload to hydration response; always localizes api_url.
src/reader-activation/session.js Frontend session hydration fetch + shared nonce storage + event emission.
src/reader-activation/events.js Adds session event type.
src/reader-activation/index.js Triggers hydration after detecting auth cookie.
src/reader-activation/store.js Uses hydrated nonce for sync; listens for hydration event to rehydrate + requeue sync.
includes/reader-activation/class-reader-activation.php Simplifies newsletters refresh endpoint to rely on current session user; removes prior “override user id” logic.
src/reader-activation-newsletters/newsletters-modal.js Updates refresh endpoint usage and refreshes modal on hydration event.
includes/content-gate/class-content-restriction-control.php Removes newspack_content_restriction_control_user_id filter usage.
tests/unit-tests/session-hydration.php Adds unit tests for CID binding and hydration endpoint behavior.
tests/unit-tests/content-gate/content-gates.php Removes tests for the removed content-restriction user-id override filter.
tests/unit-tests/content-gate/class-premium-newsletters.php Removes filter cleanup that no longer applies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/unit-tests/session-hydration.php
Comment thread src/reader-activation/session.js Outdated
Comment thread src/reader-activation/store.js
Comment thread src/reader-activation-newsletters/newsletters-modal.js Outdated
Comment thread includes/reader-activation/class-session-hydration.php Outdated
Comment thread src/reader-activation/session.js Outdated
@miguelpeixe miguelpeixe marked this pull request as ready for review April 2, 2026 14:09
@miguelpeixe miguelpeixe requested a review from a team as a code owner April 2, 2026 14:09
@miguelpeixe miguelpeixe self-assigned this Apr 2, 2026
@miguelpeixe miguelpeixe added the [Status] Needs Review The issue or pull request needs to be reviewed label Apr 2, 2026
@miguelpeixe miguelpeixe requested a review from dkoo April 2, 2026 14:09
Copy link
Copy Markdown
Contributor

@dkoo dkoo left a comment

Choose a reason for hiding this comment

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

@miguelpeixe I like the overall approach here, and I think it provides everything we need to fix the long-standing issue of the newsletter signup modal showing lists for the "wrong" user after a new account registration. However, that testing step is not working for me currently. See more details inline.

More feedback from a Claude code review below.

Comment thread includes/reader-activation/class-reader-activation.php
Comment thread tests/unit-tests/session-hydration.php Outdated
Comment thread includes/reader-activation/class-session-hydration.php Outdated
Comment thread includes/reader-activation/class-session-hydration.php
Comment thread src/reader-activation/session.js Outdated
@github-actions github-actions bot added the [Status] Needs Changes or Feedback The issue or pull request needs action from the original creator label Apr 2, 2026
@miguelpeixe miguelpeixe requested a review from dkoo April 6, 2026 13:51
@github-actions github-actions bot added [Status] Approved The pull request has been reviewed and is ready to merge and removed [Status] Needs Review The issue or pull request needs to be reviewed [Status] Needs Changes or Feedback The issue or pull request needs action from the original creator labels Apr 6, 2026
@miguelpeixe miguelpeixe merged commit 665b152 into trunk Apr 6, 2026
11 checks passed
@miguelpeixe miguelpeixe deleted the feat/session-hydration branch April 6, 2026 17:16
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Hey @miguelpeixe, good job getting this PR merged! 🎉

Now, the needs-changelog label has been added to it.

Please check if this PR needs to be included in the "Upcoming Changes" and "Release Notes" doc. If it doesn't, simply remove the label.

If it does, please add an entry to our shared document, with screenshots and testing instructions if applicable, then remove the label.

Thank you! ❤️

matticbot pushed a commit that referenced this pull request Apr 16, 2026
# [6.38.0-alpha.1](v6.37.0...v6.38.0-alpha.1) (2026-04-16)

### Bug Fixes

* `is_donor` read-only only on WooCommerce-backed donations ([1dde930](1dde930))
* **access-control:** fix incorrect class name ([#4638](#4638)) ([a7a7908](a7a7908))
* **access-control:** hide My Account group features if Memberships is still active ([#4650](#4650)) ([214fa0d](214fa0d))
* add object-level authorization to corrections REST endpoint ([#4643](#4643)) ([a39a62d](a39a62d))
* address false positives in subscription status alerts on My Account ([f7d6bb8](f7d6bb8))
* address false positives in subscription status alerts on My Account ([31c8313](31c8313))
* clear stored referrer data where referrer is none or local ([c7ef6f3](c7ef6f3))
* count `pending-cancel` by testing against our canonical constant ([2a1f85b](2a1f85b))
* count `pending-cancel` by testing against our canonical constant ([8906b64](8906b64))
* include products without children when reviewing active subscriptions ([d714f3c](d714f3c))
* include products without children when reviewing active subscriptions ([aae3da2](aae3da2))
* limit success and notice snackbar treatments to My Account page ([3b40bec](3b40bec))
* **my-account:** limit notice/success snackbar overrides to My Account ([49517c1](49517c1))
* normalize referrer data when comparing ([d377afb](d377afb))
* **reader-activation:** clear referrer data when none provided ([696012b](696012b))
* **reader-data:** make is_donor read-only only when platform has server-side tracking ([92b0d34](92b0d34))
* update docblock to reflect simple product support ([ea1f03e](ea1f03e))
* update docblock to reflect simple product support ([ba14f85](ba14f85))
* **woocommerce:** image handling in paginated block ([#4149](#4149)) ([1c91f6c](1c91f6c))

### Features

* **access-control:** advanced settings for RSS content restriction ([#4613](#4613)) ([85aa560](85aa560))
* **access-control:** update default patterns ([#4569](#4569)) ([7f9a6c9](7f9a6c9))
* add filter for future integrations to self-declare server-side (secure?) donor tracking ([76e40fc](76e40fc))
* add README.md for the Overlay Block ([#4651](#4651)) ([6d7de6e](6d7de6e))
* **advertising:** replace placements UI with inline expandable cards ([#4625](#4625)) ([e5de003](e5de003))
* **block-theme:** add overlay block for the block theme ([#4578](#4578)) ([af1e4b9](af1e4b9))
* **components:** update CardFeature button size and variant ([#4609](#4609)) ([1d03d4c](1d03d4c))
* **content-gate:** add per-block access control for Group, Stack, and Row blocks ([#4646](#4646)) ([5bdf458](5bdf458))
* **content-gate:** add POST method for external IP access checks ([#4598](#4598)) ([36bfeea](36bfeea))
* **google-site-kit:** enable custom GA frontend params by default ([#4664](#4664)) ([19830ce](19830ce))
* **handoff:** add URL-based handoff with customizable banner text ([#4603](#4603)) ([be59c68](be59c68))
* **integrations:** add My Account menu hook to integration abstraction ([#4640](#4640)) ([4ef2c91](4ef2c91))
* **integrations:** add subscription and donation metadata ([#4597](#4597)) ([ca928f8](ca928f8))
* **integrations:** implement profile metadata ([#4624](#4624)) ([b1daf85](b1daf85))
* make it easier to do "Block until consent given" setups in Complianz and improve blocking ([#4549](#4549)) ([44dda72](44dda72))
* reader activation segments ([#4604](#4604)) ([3821fed](3821fed))
* **reader-data:** add engagement fields ([#4594](#4594)) ([1cba4ef](1cba4ef))
* **reader-data:** store sync reconciliation ([#4633](#4633)) ([69bdda4](69bdda4))
* require reader to set name when commenting ([#4647](#4647)) ([db5ad73](db5ad73))
* session hydration ([#4618](#4618)) ([665b152](665b152))

### Reverts

* address false positives in subscription status alerts on My Account ([9d203b0](9d203b0))
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 6.38.0-alpha.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Status] Approved The pull request has been reviewed and is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants