mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
FavoriteAppsRegistry and BrowserHistoryRegistry were the two stores that
2539e510 repointed at AppPreferenceStores without relocating. That commit's
scope was removing the Context.preferencesDataStore delegates, so these kept
sitting in amethyst/ — by inertia, not because anything platform-specific held
them there.
Nothing in either was Android. The one real tie was that they reached out for
their store:
private val favoriteAppsDataStore: DataStore<Preferences>
get() = Amethyst.instance.appStores.getDataStore("favorite_apps")
Every store that already lives in commons takes its DataStore instead, for the
reason DataStoreSearchHistoryStorage documents: DataStore refuses a second live
instance on a path that already has one, so the caller's holder has to stay the
single registry. Both are classes taking (store, scope) now, and the Android
side does the binding in AppModules from appStores.getDataStore(FILE_NAME) and
applicationIOScope. File names are unchanged, so nothing migrates.
The Context parameter turned out to be dead. It was written once in init() and
then only read as `appContext ?: return` — a has-init-run gate that happened to
be typed Context?, never used as a Context. It is a Boolean now. The other
platform call, System.currentTimeMillis() in record(), is TimeUtils.nowMillis().
ConcurrentHashMap.newKeySet() becomes commons' ConcurrentSet, which exists for
exactly this (lock-striped on JVM, lock-guarded on iOS).
BrowserHistoryRegistry's `hydrated` flag is gone: it was assigned and never
read, unlike FavoriteAppsRegistry's, which gates the removal tombstones.
The point of the move is that the disk lifecycle is now testable — as objects
reaching into a singleton these had no unit coverage at all. 14 new tests, the
ones worth naming being the two sides of the hydration window: a favorite
removed after init() but before the merge lands must not be resurrected by it,
and an add made in that same window must not be dropped by it. Both are driven
on the test scheduler, so the window is a state the test controls rather than
races. Also pinned: nothing is written before init() (without that gate a write
in the window flushes a partial list over the stored one), a corrupt file
hydrates empty, the history cap at 500, and that a blank title on a revisit
does not overwrite the name the user recognises.
Writing those tests surfaced the single-instance rule the hard way: the first
restart tests opened a second DataStore on a live path and read an empty store,
which looks exactly like data loss. Each test session now owns its Job and is
cancelled before the next opens — the same rule production follows by keeping
one instance per file.
Verified: :commons:jvmTest, :commons:verifyKmpPurity,
:commons:compileCommonMainKotlinMetadata, :amethyst:compilePlayDebugKotlin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXvKXakvup4inNFfAhhr4L