feat(m2): public shard widgets + live SSE stream
All checks were successful
PR Checks / android-build (pull_request) Successful in 9m4s

Implements M2 of docs/android/PLAN.md §6.2 (functional pass): the public
shard surface over /api/v1/public/shard/*, plus the live SSE feed with
reconnect/backoff and graceful degradation (§7).

- Shard DTOs (status/economy/feed/online/presence/champs/guilds/governors/
  houses) mirroring public/shard.controller.js; ignoreUnknownKeys keeps
  additive backend fields safe, and the live *.update frames decode into the
  same board DTOs.
- PublicApi: the /public/shard/* GETs (status, feed, economy, online,
  presence, champs, guilds, governors + history, houses).
- ShardStreamClient: OkHttp SSE over /public/shard/stream. Unlike the browser
  EventSource it reconnects itself — a cold Flow<ShardStreamEvent> with
  growing backoff (reset on open), no read timeout for the idle keepalive,
  and clean teardown on cancel so a dropped feed degrades to "offline".
- ShardRepository: typed ApiResult snapshot reads + the shared live feed and
  frame decoders.
- Screens: a Shard hub (status/online count/economy/presence/staff + live
  activity feed with a live indicator) linking to live boards for champion
  spawns, guilds, governors (+ on-demand term history) and falling houses
  (IDOC). Boards seed from a snapshot then merge SSE deltas in place via a
  reusable LiveBoard, mirroring the website's merge semantics. Wired into the
  shared navigation drawer (§5); all strings externalized (§2).
- Tests (28): DTO/frame decode, LiveBoard merge, event-text formatting, and
  SSE frame parsing.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-19 18:34:14 -05:00
parent 81f10fbca4
commit c8f4e76370
26 changed files with 2192 additions and 3 deletions

View File

@@ -38,6 +38,7 @@
<string name="menu_home">Home</string>
<string name="menu_news">News</string>
<string name="menu_wiki">Wiki</string>
<string name="menu_shard">Shard</string>
<string name="menu_about">About</string>
<string name="menu_contact">Contact</string>
<string name="menu_change_server">Change server</string>
@@ -73,4 +74,46 @@
<string name="contact_fallback">This site has no mailer configured. Email directly: %1$s</string>
<string name="contact_validation">Please fill in every field.</string>
<string name="contact_error">Couldn\'t send your message. Please try again.</string>
<!-- ── Shard hub &amp; live feed (§6.2) ─────────────────────────────── -->
<string name="shard_live">Live</string>
<string name="shard_offline">Offline</string>
<string name="shard_status_online">Shard online</string>
<string name="shard_status_offline">Shard offline</string>
<string name="shard_online_count">%1$d players online</string>
<string name="shard_economy_gold">Gold supply: %1$s</string>
<string name="shard_section_staff">Staff online</string>
<string name="shard_section_activity">Live activity</string>
<string name="shard_feed_empty">No recent activity.</string>
<string name="shard_nav_champs">Champion spawns</string>
<string name="shard_nav_guilds">Guilds</string>
<string name="shard_nav_governors">Governors</string>
<string name="shard_nav_houses">Falling houses</string>
<!-- ── Champion spawns (§6.2) ──────────────────────────────────────── -->
<string name="champs_empty">No champion spawns are being tracked right now.</string>
<string name="champs_fallback_name">Champion spawn</string>
<string name="champ_level">Level %1$d</string>
<string name="champ_cooldown">Restarting</string>
<!-- ── Guilds (§6.2) ───────────────────────────────────────────────── -->
<string name="guilds_empty">No guilds are being tracked right now.</string>
<string name="guilds_fallback_name">Guild</string>
<string name="guilds_members">%1$d members</string>
<string name="guilds_leader">Led by %1$s</string>
<string name="guilds_alliance">Alliance: %1$s</string>
<!-- ── Governors (§6.2) ────────────────────────────────────────────── -->
<string name="governors_empty">No governors — this shard may not run the City Loyalty system.</string>
<string name="governor_current">Governed by %1$s</string>
<string name="governor_none">No governor</string>
<string name="governor_election">Election: %1$s</string>
<string name="governor_history">Term history</string>
<string name="governor_history_loading">Loading…</string>
<string name="governor_history_empty">No past terms recorded.</string>
<!-- ── Falling houses / IDOC (§6.2) ────────────────────────────────── -->
<string name="houses_empty">No houses are in danger right now.</string>
<string name="houses_fallback_name">A house</string>
<string name="houses_idoc_badge">IDOC</string>
</resources>