vc=90: cold-start store hydration off-Main + video-page status-bar fix + RYD FFI slim

Resume + Enrichment stores no longer JSON-decode on the main thread in Application.onCreate (Resume is the heaviest cold-start cost: ~50-100ms decode at its 100k-entry cap). Both seed an empty StateFlow and hydrate off-thread on their PrefsWriter single-thread dispatcher. Mutations that arrive before the hydrate finishes defer onto that FIFO dispatcher (a hydrated gate) so they re-run on fully-loaded state -- correct for adds AND clears; a naive loaded+live merge would resurrect a clear that landed on the empty seed. Subscriptions/Playlists/History/Settings stay eager (tiny + rendered directly -> would flash empty). (audit 2.1)

Video page: details/related rows no longer leak into the status-bar strip above the player when scrolled. topPadding is now a layout inset on the detail LazyColumn, so the scroll viewport begins+clips at the player's bottom edge instead of letting rows scroll up over the clock/signal.

RYD: dropped the dead rating + view_count fields from the strawcore RydVotes FFI Record + Kotlin shim -- only likes/dislikes are rendered; RYD's rating restates the like/dislike ratio and its viewCount is a stale aggregate conflicting with the real YouTube count already shown. (audit #2 L-9)

Adversarially reviewed (Opus): clear-resurrection closed, no defer-loop, FFI slim has no readers. Headless compileDebugKotlin green.
This commit is contained in:
Sulkta 2026-06-22 05:10:48 -07:00
parent 41b3999ad3
commit 7c90f70a0a
7 changed files with 150 additions and 25 deletions

View file

@ -9,6 +9,33 @@ const val STRAW_SDK_TARGET = 35
// Sulkta fork — Straw
//
// vc=90 / 0.1.0-CX — cold-start hydration + status-bar bleed fix + RYD FFI slim:
// * ResumePositionsStore + EnrichmentStore no longer JSON-decode on the main
// thread in Application.onCreate. Resume was the heaviest cold-start cost in
// the app — at its 100k-entry cap the blob is multi-MB and the decode runs
// ~50-100 ms on a low-end device, all on Main before the first frame. Both
// now seed an empty StateFlow and hydrate from disk on the store's own
// PrefsWriter single-thread dispatcher. Mutations that arrive before the
// hydrate finishes DEFER themselves onto that same FIFO dispatcher (a
// `hydrated` flag gate) so they re-run after the load on fully-loaded state
// — correct for adds AND clears (a naive `loaded + live` merge would
// resurrect a clear that landed on the empty seed). Subscriptions/Playlists/
// History/Settings stay eager: tiny (sub-ms) and rendered directly, so
// seeding them empty would flash an empty list every cold start. (audit 2.1)
// * Fixed the video page leaking details/related rows into the status-bar
// strip above the player when scrolled. The detail body fills the screen
// from y=0 (under the status bar) and the player only starts at the
// status-bar inset, so a leading Spacer-as-item let rows scroll up into the
// uncovered strip over the clock/signal. topPadding is now a layout inset on
// the LazyColumn itself, so the scroll viewport begins + clips at the
// player's bottom edge — rows can't enter the strip. (a tester, on-device.)
// * RYD: dropped the dead `rating` + `viewCount` fields from the strawcore
// `RydVotes` FFI Record + the Kotlin shim. Only likes/dislikes were ever
// rendered; RYD's rating just restates the like/dislike ratio and its
// viewCount is a stale aggregate that conflicts with the real YouTube count
// the detail screen already shows — surfacing either would be redundant or
// misleading, so they no longer cross the FFI. (audit #2 L-9)
//
// vc=89 / 0.1.0-CW — pagination-burst fix + finish the SP-write serialization:
// * Hide-shorts no longer drains a whole channel/search in one burst. The
// infinite-scroll trigger is computed from the FILTERED list while loadMore()
@ -285,6 +312,6 @@ const val STRAW_SDK_TARGET = 35
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
// strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no
// NewPipeExtractor in the runtime path.
const val STRAW_VERSION_CODE = 89
const val STRAW_VERSION_NAME = "0.1.0-CW"
const val STRAW_VERSION_CODE = 90
const val STRAW_VERSION_NAME = "0.1.0-CX"
const val STRAW_APPLICATION_ID = "com.sulkta.straw"