Converge home-active feed-col chrome (date range, filter bar, stats/ cycling panels) with the trip page via a shared partial + shared initTripStats() glue. Map convergence deferred to a later spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BftDn9vu9SonFAY4vxu4uk
8.6 KiB
Home / Trip View Convergence Design
Date: 2026-06-27 Status: Approved for implementation
Problem
The home page in active-trip mode (home.html.twig, config.site.travelling branch) and the trip page (trip.html.twig) are meant to present the same experience — the same content, behaving near-identically. Today their feeds already match (both render journal + story entries via the shared entry-journal/entry-story partials), but the feed-col chrome diverges:
| Feature | Trip page | Home-active | Converge? |
|---|---|---|---|
| Feed lists journal + stories | ✅ | ✅ | already matches |
| Date-range header | ✅ | ❌ | yes |
| Filter bar (All / Journal / Stories) | ✅ | ❌ | yes |
| Stats panel | ✅ | ❌ | yes |
| Cycling panel | ✅ (if GPX) | ❌ | yes |
| Sort toggle button | ✅ | ❌ | no — intended difference |
| Default feed order | oldest→newest (sort flag 4) | its own (sort flag 3) | no — intended difference |
The chrome markup is the divergence. The supporting behavior is already global: js/main.js (loaded for every page via base.html.twig:10) runs initFilterBar(), initPanelToggles(), and initSortButton('trip-sort-toggle', …), each a silent no-op when its markup is absent. The entry partials already emit data-type, which the filter relies on. So rendering the same markup on home is enough for the filter bar, panel toggles, and (where present) the sort button to work with zero new JS.
The single exception is the stats/cycling computation glue (writing distance/elevation values into #stat-distance, #cyc-*). That code is currently inline in trip.html.twig and not global, so the stats/cycling panels cannot function on home until it is shared.
Goals
- Home-active gains the date-range header, filter bar, and stats/cycling panels — matching the trip page.
- The shared feed-col chrome lives in one place (a partial), so future header/chrome changes apply to both pages.
- Home-active keeps its own default feed order and has no sort button (the two intended differences).
- Stats/cycling computation works on both pages from a single shared JS function.
- No change to the trip page's rendered output (structural refactor only on that side).
Non-goals
- Map convergence is out of scope. The home-active map omitting story markers, lacking a fullscreen button, and its hash-only click behavior are all deferred to a later map-init spec (see
project-map-init-refactormemory). Both inline map scripts and both map-col markup blocks stay exactly as they are. - Extracting the
all_items/map_entriesbuild loops to a macro — Twig macros output HTML, not arrays (established constraint; see2026-06-23-template-refactor-design.md). Each page keeps its own data-build loops. - Any visual restyling of the chrome — home reuses the trip's existing CSS classes unchanged.
- Adding a sort button to home, or changing home's default order.
Architecture
1. New shared partial: templates/partials/trip-feed-col.html.twig
Holds the entire .home-feed-col content currently inline in trip.html.twig:70-120:
- header (
.home-trip-header): title, date range (whentrip_page.header.date_startset), counts - filter bar (
.trip-filter-bar): All / Journal / Stories buttons - the sort button (
#trip-sort-toggle) — rendered only whenshow_sortis true - panel toggles (
.trip-panel-toggles): Stats, and Cycling (whenhas_gpx) stats_panel(...)and (whenhas_gpx)cycling_panel(...)macro calls- the feed loop over
all_itemswith the#feed-filter-emptysentinel
Interface (called via {% include 'partials/trip-feed-col.html.twig' with {…} only %}):
| Param | Type | Trip passes | Home-active passes |
|---|---|---|---|
trip_page |
Page | page |
trip |
all_items |
array | sorted by date, flag 4 | sorted by date, flag 3 |
journal_entries |
array | dailies children | dailies children |
journal_count |
int | count | count |
story_count |
int | count | count |
has_gpx |
bool | gpx_urls|length > 0 |
home_gpx_urls|length > 0 |
show_sort |
bool | true |
false |
Because the partial is called with only, it must {% import 'macros/stats.html.twig' %} and {% import 'macros/cycling.html.twig' %} itself.
Both pages already build all_items, the counts, and has_gpx for their existing map data, so these are passed in rather than rebuilt — no new duplication is introduced.
2. Shared stats glue: initTripStats(config) in js/src/main.js
Extract the inline stats/cycling computation from trip.html.twig:213-249 into a config-driven function. Current inline logic: if GPX present, MapUtils.parseGpxFiles(urls, …) fills #stat-distance and all #cyc-* fields; otherwise sum haversineKm over gps_points and write the ~-prefixed estimate to #stat-distance.
function initTripStats(config) {
// config: { gpxUrls: [], gpsPoints: [[lat,lng],...], hasGpx: bool }
// No-op if #stat-distance is absent (page has no stats panel).
}
Called from the boot block alongside the other inits. Each page provides the config via a small inline <script> that defines the data (the *_GPX_URLS / gps_points arrays are page-specific Twig output), then calls initTripStats(...) — or the boot reads globals the page sets. Implementation detail for the plan; the contract is: the function is selector-guarded and runs on any page that rendered a stats panel.
js/main.js is the built artifact; the asset pipeline rebuilds it from js/src/main.js (see 2026-06-22-asset-pipeline-design.md).
3. Home-active data additions
Home-active currently builds map_entries (journal-only) and home_gpx_urls. For the stats panel's no-GPX fallback it must also build gps_points (journal entries with lat/lng), mirroring trip.html.twig:27-32.
4. Template wiring
trip.html.twig: replace the inline .home-feed-col block (:70-120) with the partial include; remove the inline stats script (:213-249) in favor of initTripStats(...). Map script, fullscreen wiring, and map data-build stay untouched.
home.html.twig (active branch): replace the bespoke feed-col (:60-83) with the same partial include (show_sort: false); add gps_points build; wire initTripStats(...). Map script and map-col stay untouched.
Data / behavior flow after change
base.html.twig ──loads──> js/main.js (global)
├─ initFilterBar() ← works on both via .trip-filter-btn + [data-type]
├─ initPanelToggles() ← works on both via .trip-panel-toggle
├─ initSortButton('trip-sort-toggle', …) ← trip only (home omits button → no-op)
└─ initTripStats(cfg) ← works on both via #stat-distance guard
trip.html.twig ─include─> partials/trip-feed-col.html.twig (show_sort: true)
home.html.twig ─include─> partials/trip-feed-col.html.twig (show_sort: false)
└─ stats_panel(), cycling_panel(), feed loop
Testing
No JS test harness exists in this project; verification is manual browser smoke testing at http://localhost:8081, consistent with prior template work.
Trip page (regression — must be unchanged):
- Load the trip page. Confirm header, date range, filter bar, sort button, stats/cycling panels, and feed render identically to before.
- Filter bar All/Journal/Stories filters the feed; sort button flips order; Stats/Cycling panels toggle open/closed.
- Stats panel distance and cycling figures populate (GPX present) or show the
~estimate (no GPX).
Home page, active-trip mode (new behavior):
4. With config.site.travelling: true, load /. Confirm date range, counts, filter bar (no sort button), and Stats panel (+ Cycling if the trip has GPX) now appear.
5. Filter bar filters the feed; panel toggles work; stats figures populate.
6. Confirm the feed default order is home's own order (unchanged from today) and that no sort button is present.
Home page, between-trips mode (regression):
7. With config.site.travelling: false, load /. Confirm the highlights layout is unaffected (this branch does not use the partial).
Files touched
- New:
user/themes/intotheeast/templates/partials/trip-feed-col.html.twig - Edit:
user/themes/intotheeast/templates/trip.html.twig(feed-col → include; remove inline stats script) - Edit:
user/themes/intotheeast/templates/home.html.twig(active branch feed-col → include; addgps_points; wire stats) - Edit:
user/themes/intotheeast/js/src/main.js(+initTripStats); rebuildjs/main.js