internetarchive / internetarchive/openlibrary
"Continue Reading" Phase 2: Client-Side Read Tracking (Local Storage)
- Dominant language
- Python
- Stars
- 6.7k
- Forks
- 2k
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 138
Description
> **Epic:** #13256 — "Continue Reading" Flow & Auto-Add Prompts *(open)*
> This is **Phase 2** of that epic. Check the epic issue for overall status and the dependency graph.
---
## Feature Request
### Problem / Opportunity
Patrons who read open-access books (via the "Read" button) or browse previews leave no trace of their reading activity on the server — there's no loan record, no reading log entry, and no bookmark we can build a dashboard from. This means their My Books page stays permanently empty even if they've read dozens of books, and we have no "continue reading" surface to re-engage them.
This is especially impactful for:
- **Logged-out patrons** — zero server-side history by definition.
- **Logged-in patrons who opt out of public shelves** — privacy-conscious readers who don't want a public reading log still deserve a local "pick up where you left off" experience.
**Success:** After this ships, any patron who clicks "Read", "Borrow", or "Browse" on a book will have that interaction captured in `localStorage`, providing a client-side fallback for the Continue Reading carousel (Phase 3 of Epic #13256).
### Proposal
Implement a lightweight, self-contained `localStorage`-backed reading tracker in the Open Library JS frontend:
- Intercept click events on **"Read"**, **"Borrow"**, and **"Browse"** links across the site.
- On each click, push a record `{ olid, title, coverId, timestamp }` into `localStorage.ol_read_history`.
- Cap the list at the **last 20 unique books** (deduplicate by OLID, promote on re-read).
- Expose a small store module so Phase 3 can read from it when assembling the carousel.
This is entirely client-side and self-contained — nothing downstream depends on it yet. Safe to ship alone.
This is Phase 2 of Epic #13256.
---
## Breakdown
Implementation Details (for maintainers)
#### Related files
Refer to [this map of common Endpoints](https://docs.openlibrary.org/developers/backend/endpoints.html):
* `openlibrary/plugins/openlibrary/js/my-books/store/` — new store module for `ol_read_history` read/write helpers [NEW]
* `openlibrary/plugins/openlibrary/js/index.js` — register global click listeners on "Read", "Borrow", "Browse" links
* `openlibrary/plugins/openlibrary/js/my-books/` — existing my-books JS directory for context
#### Requirements Checklist
* [ ] Create `openlibrary/plugins/openlibrary/js/my-books/store/readingHistory.js` [NEW] with:
* `addEntry({ olid, title, coverId, timestamp })` — upserts by OLID, promotes to top on re-read, caps at 20 entries
* `getHistory()` — returns the full list sorted by timestamp descending
* `clearHistory()` — utility for testing / user opt-out
* [ ] Register global click listeners in `index.js` targeting `[data-ol-action="read"]`, `[data-ol-action="borrow"]`, `[data-ol-action="browse"]` (or equivalent selectors used on book page CTAs)
* [ ] Extract OLID, title, and cover ID from the clicked link's surrounding DOM context or `data-*` attributes
* [ ] Write unit tests for the store module (add, deduplicate, cap at 20, promote on re-read)
* [ ] Ensure no personally identifiable data is stored — only public book metadata
#### Stakeholders
* @Sadashii (author, implementation lead)
* @mekarpeles (maintainer review)
#### Instructions for Contributors
* **Before** [creating a new branch](https://docs.openlibrary.org/developers/tools/git.html#making-changes-and-creating-a-pull-request) or pushing up changes to a PR, please first [run these commands](https://docs.openlibrary.org/developers/tools/git.html#working-on-your-branch) to ensure your repository is up to date, as the pre-commit bot may add commits to your PRs upstream.
* JS must be ESLint-compliant, use single quotes, and contain no jQuery.
* Run `npm run test:js` to validate new store module unit tests pass.
Contributor guide
Assessment
This issue has not been assessed yet.