Wayfinder: Flutter Android tt-rss client MVP #1

Closed
opened 2026-08-02 15:15:01 +00:00 by TheAnachronism · 1 comment

Destination

A locked tech-decision set and MVP specification for a Flutter Android Tiny Tiny RSS client: personal use now, shareable later — enough to hand off and build, not the built app itself.

Notes

  • Domain: self-hosted tt-rss reader client (Android). Local tt-rss source of truth for the API: /home/theanachronism/git/github/tt-rss/ (especially classes/API.php, API level 23).
  • Stack decision (charting): Flutter (Dart).
  • Audience: personal now, designed so it can be shared later (sideload / F-Droid-shaped), not a Play launch in this effort.
  • MVP must-haves: nested category/feed browse → article list → in-app HTML reader (+ open original); mark read/starred; virtual feeds All / Fresh / Starred; unread-by-default with toggle to all; multi-instance accounts from MVP.
  • Category UX: left-side expandable tree (drawer/popover). Tapping a category opens the article list for that category and all descendants; expand/collapse is a separate control.
  • Data posture: online-first, cache-ready — structure so an offline reading cache can be added later without a large rewrite. Offline reading is out of MVP scope.
  • Prior art: study existing Android tt-rss clients for API/UX lessons; still greenfield Flutter.
  • Skills: /grilling, /domain-modeling, /research, /prototype (UI). Prefer decisions and specs over implementation.
  • Glossary: repo-root CONTEXT.md (updated as grilling locks terms).
  • Architecture decisions: docs/adr/ (ADR-0001 online-first seams).
  • Handoff: docs/mvp-spec.md.
  • Styling: dark mode by default for MVP; theme switcher deferred past this map.
  • Tracker: Forgejo via fj; blocking via Blocked by: #n body lines; claim via assignee.

Decisions so far

  • Research: tt-rss JSON API surface for MVP — API level 23; JSON POST /api/ with sid (no cookies); virtual All=-4/Fresh=-3/Starred=-1; nest via getFeeds(include_nested)/getFeedTree (getCategories omits parent_cat); ENABLE_API_ACCESS default off.
  • Research: existing Android tt-rss clients — Copy virtual ids / sid auth / optimistic mark+sync / unread toggle; avoid flat cats, category→feeds default, single-account prefs; official Android client gone 2025-11-01.
  • Research: Flutter secure storage, HTML reader, drawer tree — flutter_secure_storage + flutter_html/url_launcher; drawer with custom tree rows (tap≠expand); WebView optional fallback; keep UI behind cache-ready repos.
  • Grill: MVP screen inventory and IA — Screens: Sign-in, Manage accounts, Settings, List hub+drawer, Reader (pushed); land/switch on Fresh+Unread; drawer = virtual feeds + nested tree (tap≠expand); empty-unread auto-shows all; reader next/prev; Mark-read mode setting (default: opened in reader).
  • Grill: online-first cache-ready Flutter architecture — Riverpod; UI→use-cases→domain→repos (API now); AccountRepository + per-Account http ApiClient; Account-scoped providers; optimistic marks + durable outbox; disk=accounts/secrets/outbox/prefs; FeedTreeSource via getFeedTree; lean headlines + getArticle; re-login+retry. ADR-0001.
  • Grill: multi-instance account model — Account = id + normalized baseUrl + user/password/sid + optional displayName (default user@host); unique (baseUrl, user); editable w/ re-login keeping id; remove wipes secrets+outbox; persist active id; Mark-read global; Sign-in validates login+api_level≥23; advanced SSL/HTTP-auth/proxy/export deferred.
  • Prototype: drawer tree, article list, reader shell — Accept Material drawer shell (tap≠expand; cat+descendants; Unread default + empty auto-show-all; pushed reader). Dark mode by default; theme switcher deferred. Asset: branch prototype/drawer-list-reader-shell.
  • Grill: assemble MVP handoff specification — Handoff at docs/mvp-spec.md: scope/non-goals/stack/IA/Accounts/seams/API/acceptance; inline images + enclosure links; soft fog deferred with defaults; packaging + offline later.

Not yet specified

(none — destination reached)

Out of scope

  • Theme switcher / light mode — deferred past MVP dark-by-default (Prototype: drawer tree, article list, reader shell)
  • Advanced Account connection options (HTTP basic auth, client certs, custom CA / insecure SSL, proxy, Wi‑Fi overlays, Account export/import) — deferred past this MVP Account model (Grill: multi-instance account model)
  • Packaging / “shareable later” distribution surface (stable public app id, signing story, privacy text) — later effort; MVP handoff names it as a Non-goal (Grill: assemble MVP handoff specification)
  • Implementing/shipping the Flutter application inside this map (destination is the spec + decisions)
  • Offline reading as a feature of the MVP (concrete cache design deferred; seams only via ADR-0001)
  • Feed or category management from the phone
  • Server-side search
  • Background sync / push notifications
  • Forking or wrapping an existing Android client instead of greenfield Flutter

Tickets

  • #2 Research: tt-rss JSON API surface for MVP
  • #3 Research: existing Android tt-rss clients
  • #4 Research: Flutter secure storage, HTML reader, drawer tree
  • #5 Grill: MVP screen inventory and IA
  • #6 Grill: online-first cache-ready Flutter architecture
  • #7 Grill: multi-instance account model
  • #8 Prototype: drawer tree, article list, reader shell
  • #9 Grill: assemble MVP handoff specification
## Destination A locked tech-decision set and MVP specification for a **Flutter Android** Tiny Tiny RSS client: personal use now, shareable later — enough to hand off and build, not the built app itself. ## Notes - Domain: self-hosted tt-rss reader client (Android). Local tt-rss source of truth for the API: `/home/theanachronism/git/github/tt-rss/` (especially `classes/API.php`, API level 23). - Stack decision (charting): **Flutter (Dart)**. - Audience: personal now, designed so it can be shared later (sideload / F-Droid-shaped), not a Play launch in this effort. - MVP must-haves: nested category/feed browse → article list → in-app HTML reader (+ open original); mark read/starred; virtual feeds All / Fresh / Starred; unread-by-default with toggle to all; **multi-instance** accounts from MVP. - Category UX: left-side expandable tree (drawer/popover). **Tapping a category opens the article list for that category and all descendants**; expand/collapse is a separate control. - Data posture: **online-first, cache-ready** — structure so an offline reading cache can be added later without a large rewrite. Offline reading is out of MVP scope. - Prior art: study existing Android tt-rss clients for API/UX lessons; still **greenfield Flutter**. - Skills: `/grilling`, `/domain-modeling`, `/research`, `/prototype` (UI). Prefer decisions and specs over implementation. - Glossary: repo-root `CONTEXT.md` (updated as grilling locks terms). - Architecture decisions: `docs/adr/` (ADR-0001 online-first seams). - Handoff: [`docs/mvp-spec.md`](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/src/branch/master/docs/mvp-spec.md). - Styling: **dark mode by default** for MVP; theme switcher deferred past this map. - Tracker: Forgejo via `fj`; blocking via `Blocked by: #n` body lines; claim via assignee. ## Decisions so far <!-- the index — one line per closed ticket: enough to judge relevance, then zoom the link for the detail the ticket holds --> - [Research: tt-rss JSON API surface for MVP](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/2) — API level 23; JSON POST `/api/` with `sid` (no cookies); virtual All=-4/Fresh=-3/Starred=-1; nest via `getFeeds(include_nested)`/`getFeedTree` (`getCategories` omits `parent_cat`); `ENABLE_API_ACCESS` default off. - [Research: existing Android tt-rss clients](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/3) — Copy virtual ids / sid auth / optimistic mark+sync / unread toggle; avoid flat cats, category→feeds default, single-account prefs; official Android client gone 2025-11-01. - [Research: Flutter secure storage, HTML reader, drawer tree](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/4) — flutter_secure_storage + flutter_html/url_launcher; drawer with custom tree rows (tap≠expand); WebView optional fallback; keep UI behind cache-ready repos. - [Grill: MVP screen inventory and IA](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/5) — Screens: Sign-in, Manage accounts, Settings, List hub+drawer, Reader (pushed); land/switch on Fresh+Unread; drawer = virtual feeds + nested tree (tap≠expand); empty-unread auto-shows all; reader next/prev; Mark-read mode setting (default: opened in reader). - [Grill: online-first cache-ready Flutter architecture](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/6) — Riverpod; UI→use-cases→domain→repos (API now); AccountRepository + per-Account http ApiClient; Account-scoped providers; optimistic marks + durable outbox; disk=accounts/secrets/outbox/prefs; FeedTreeSource via getFeedTree; lean headlines + getArticle; re-login+retry. ADR-0001. - [Grill: multi-instance account model](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/7) — Account = id + normalized baseUrl + user/password/sid + optional displayName (default user@host); unique (baseUrl, user); editable w/ re-login keeping id; remove wipes secrets+outbox; persist active id; Mark-read global; Sign-in validates login+api_level≥23; advanced SSL/HTTP-auth/proxy/export deferred. - [Prototype: drawer tree, article list, reader shell](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/8) — Accept Material drawer shell (tap≠expand; cat+descendants; Unread default + empty auto-show-all; pushed reader). **Dark mode by default**; theme switcher deferred. Asset: branch `prototype/drawer-list-reader-shell`. - [Grill: assemble MVP handoff specification](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/9) — Handoff at `docs/mvp-spec.md`: scope/non-goals/stack/IA/Accounts/seams/API/acceptance; inline images + enclosure links; soft fog deferred with defaults; packaging + offline later. ## Not yet specified <!-- see "Fog of war": in-scope fog you can't ticket yet; graduates as the frontier advances --> _(none — destination reached)_ ## Out of scope <!-- see "Out of scope": work ruled beyond the destination; closed, never graduates --> - Theme switcher / light mode — deferred past MVP dark-by-default ([Prototype: drawer tree, article list, reader shell](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/8)) - Advanced Account connection options (HTTP basic auth, client certs, custom CA / insecure SSL, proxy, Wi‑Fi overlays, Account export/import) — deferred past this MVP Account model ([Grill: multi-instance account model](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/7)) - Packaging / “shareable later” distribution surface (stable public app id, signing story, privacy text) — later effort; MVP handoff names it as a Non-goal ([Grill: assemble MVP handoff specification](https://git.anachronis.dev/TheAnachronism/tt-rss-viewer/issues/9)) - Implementing/shipping the Flutter application inside this map (destination is the spec + decisions) - Offline reading as a feature of the MVP (concrete cache design deferred; seams only via ADR-0001) - Feed or category management from the phone - Server-side search - Background sync / push notifications - Forking or wrapping an existing Android client instead of greenfield Flutter ## Tickets - [x] #2 Research: tt-rss JSON API surface for MVP - [x] #3 Research: existing Android tt-rss clients - [x] #4 Research: Flutter secure storage, HTML reader, drawer tree - [x] #5 Grill: MVP screen inventory and IA - [x] #6 Grill: online-first cache-ready Flutter architecture - [x] #7 Grill: multi-instance account model - [x] #8 Prototype: drawer tree, article list, reader shell - [x] #9 Grill: assemble MVP handoff specification
Author
Owner

Destination reached: locked decisions + docs/mvp-spec.md handoff. Map complete.

Destination reached: locked decisions + docs/mvp-spec.md handoff. Map complete.
Sign in to join this conversation.
No description provided.