Stream the page shell first so a loader shows while feeds fetch
Opening the page used to block on refreshStale() before a single byte went out, so the browser sat on a blank tab for the whole fetch. index.php now renders templates/shell.php (head, header, filters, skeleton loader) and flushes it, then refreshes and appends templates/content.php, which removes the loader as the feed HTML arrives. - templates/layout.php is split into shell.php + content.php - refresh chip moved to a right-aligned row above the feed: the toolbar paints before the refresh report exists - header story count shows the cached total, then corrects to the real one - skeleton styles match the card grid and keep the sources' chroma; a noscript style hides the loader for clients without JS - README: file map and page-load lifecycle updated
This commit is contained in:
@@ -19,6 +19,9 @@ No framework, no build step. PHP + SQLite for caching, Bootstrap 5 for the UI.
|
||||
selection** from the browser (`manage.php`, password protected): add
|
||||
channels by `@handle`, disable or remove feeds — no config editing needed.
|
||||
- Paginates with a **"Load more stories"** button (AJAX, 20 per page).
|
||||
- Streams the page shell (header, filters and a skeleton loader) to the browser
|
||||
**before** the feeds are fetched, so something paints within milliseconds on
|
||||
every open.
|
||||
- Caches everything in **SQLite** so page loads never wait on the network; feeds
|
||||
are refreshed on demand when stale (default TTL 30 min).
|
||||
|
||||
@@ -217,13 +220,16 @@ index.php api/items.php
|
||||
FeedService::page(source, page, pageSize, since) ← read path
|
||||
└─ FeedRepository::getItems()/countItems() SQL over the SQLite cache
|
||||
│
|
||||
└─ View::render('layout' | 'cards') server-rendered HTML
|
||||
└─ View::render('cards') server-rendered HTML
|
||||
(api/items.php returns that HTML inside a JSON envelope)
|
||||
```
|
||||
|
||||
**Request lifecycle (page load):** bootstrap → refresh stale feeds → validate
|
||||
`source`/`range` → query one page → render `templates/layout.php` which includes
|
||||
`templates/cards.php`.
|
||||
**Request lifecycle (page load):** bootstrap → sync configured feeds → render
|
||||
`templates/shell.php` (head, header, filters, skeleton loader) and **flush it**
|
||||
→ refresh stale feeds → query one page → append the rendered
|
||||
`templates/content.php` (which includes `templates/cards.php`) and drop the
|
||||
loader. The shell paints while the refresh is still running; the loader never
|
||||
outlives the fetch — the content chunk removes it as soon as it arrives.
|
||||
|
||||
**Request lifecycle (load more):** `assets/app.js` calls `api/items.php` with the
|
||||
next `page` (plus current `source` and `range`), receives
|
||||
@@ -283,7 +289,8 @@ button when `has_more` is false.
|
||||
│ ├── ManageAuth.php manage.php gate: password, CSRF, flash messages
|
||||
│ └── View.php Escaping + render() + labels/colors/time formatting
|
||||
├── templates/
|
||||
│ ├── layout.php Full page shell, filters, grid, empty state, Load more
|
||||
│ ├── shell.php Streamed first: head, header, filters, feed loader
|
||||
│ ├── content.php Streamed after the refresh: grid, empty state, Load more
|
||||
│ ├── manage.php Feed management page (login, add forms, feed list)
|
||||
│ └── cards.php One feed card partial (used by page + API)
|
||||
├── assets/
|
||||
|
||||
Reference in New Issue
Block a user