4.1 KiB
Read It Later — Design Document
Date: 2026-02-27 Status: Approved
Goal
A standalone Indiekit plugin (@rmdes/indiekit-endpoint-readlater) that provides a private "read it later" bookmark list. Save URLs from any context — backend readers (microsub, activitypub) and frontend pages (blogroll, podroll, listening, news) — into a unified collection for later consumption.
Architecture
A minimal standalone plugin owns a single MongoDB collection and exposes a save/delete API. Other plugins and the Eleventy theme add per-item save icons that POST to this API. The plugin has its own admin page for managing saved items. No content is copied — only URLs with metadata.
Data Model
Collection: readlater_items
{
_id: ObjectId,
url: "https://example.com/article", // Unique key — prevents duplicates
title: "Article Title", // Display title
source: "microsub" | "activitypub" | "blogroll" | "podroll" | "listening" | "news" | "manual",
savedAt: "2026-02-27T12:00:00.000Z", // ISO 8601 string
}
Index: { url: 1 } unique.
API Endpoints
All routes require authentication. No public routes.
| Method | Path | Description |
|---|---|---|
| GET | /readlater |
Admin page — list saved items with filters |
| POST | /readlater/save |
Save a URL — accepts {url, title, source}, returns JSON |
| POST | /readlater/delete |
Delete a saved item — accepts {url} or {id}, returns JSON |
Save response: {success: true, item: {...}} or {error: "Already saved"}.
Delete response: {success: true} or {error: "Not found"}.
Admin Page (/readlater)
List view showing all saved items with:
- Sort toggle: Newest first (default) / oldest first —
?sort=asc|desc - Source filter: Dropdown showing only sources with saved items —
?source=microsub - Search box: Text search across title and URL —
?q=search+term - Per-item display: Title (linked to original URL, new tab), source badge, saved date, delete button
- Delete animation: Fade-out on delete, same pattern as microsub mark-read
Filters work via query parameters (bookmarkable, no JS required for filtering).
Navigation: Sidebar entry under "Read & Engage".
Integration Strategy
Detection
Each consuming plugin checks at startup/render whether @rmdes/indiekit-endpoint-readlater is installed. If not installed, save buttons don't render. No hard dependency.
Button Behavior
- Click save icon -> POST to
/readlater/save-> icon changes to filled/checkmark state - Already saved -> API returns "already saved" -> icon stays in saved state
- No unsave from item cards — manage saved items from
/readlateradmin page
Phase 1: Plugin + Backend Readers
New plugin: @rmdes/indiekit-endpoint-readlater
- MongoDB collection, indexes
- Save/delete API endpoints
- Admin page with filters
Microsub reader (indiekit-endpoint-microsub):
- Add save icon to
item-card.njkaction bar (alongside reply, like, repost, bookmark, mark-read) - Button sends
{url: item.url, title: item.name, source: "microsub"}
ActivityPub reader (indiekit-endpoint-activitypub):
- Add save icon to post action bar (alongside reply, boost, like, view original)
- Button sends
{url: originalPostUrl, title: contentSnippet, source: "activitypub"}
Phase 2: Frontend Theme Integration
Repo: indiekit-eleventy-theme (separate from plugin)
Add per-item save icons to frontend pages, only visible when logged in:
/blogroll/— per blog post link/podroll— per episode link/listening/— per track/listen/news— per RSS item
Same API call (POST /readlater/save), same button behavior.
Auth gating uses the same mechanism as the existing "Create new post" FAB.
Lifecycle
- Items persist until manually deleted
- No auto-expiry, no archiving
- No content storage — just URL bookmarks
Tech Stack
- Express routes (Indiekit plugin API)
- MongoDB (single collection)
- Nunjucks templates (@indiekit/frontend layout)
- Vanilla JS for save button fetch calls