android: the first run — a folder to keep music in, and an account to reach the network
Nobody had seen this app on a phone that has just installed it. On Android the library root is never missing — config.rs falls back to the app's own sandbox, a real folder nothing else can see — so "no folder yet" cannot be a null check, and setup_done has never been written by the phone, so every phone in existence reads as unstamped, Kyle's included. The tour therefore opens on three clauses, not one: no flag, no saved account, and no records, read only after the library and the credentials have both answered. Two cards, skippable, and no password anywhere in it — the Soulseek step goes to the card on Community and stops. Every screen that is empty because of those two things now says so with the row that fixes it: Home (whose copy predated the folder picker), Library → Albums, Library → Artists, which had no empty state at all, and Search, where a flat sentence about signing in became the row that does it. A row goes away because the config moved, so the two doors onto the same folder tell each other — You re-reads when the tour moves it, the tour's store is told when You's own picker does — and the all-files row re-reads the grant when the system screen hands the app back, the way You already did. You → This phone opens the tour again as a preview that reads the config and writes nothing, which is the door the phone walk drives on Kyle's own device. The mock can answer as a fresh install now — an empty database, no account, no permission, the sandbox root — so the whole thing is walked end to end, and two commands that had no mock at all (player_control, open_external) got one on the way past. It is the mock walk's first reload, which turned up two things a reload does not clear by itself: the player writes the session back on pagehide, so the fresh block clears it from a pagehide listener of its own, and the viewport emulation lives on the DevTools session, so the harness hands it back after the re-attach.
This commit is contained in:
1 parent
46cf6a98fe
commit
95c2bfeeba
13 files changed
+633
-14
No files matched your search
@@ -42,6 +42,7 @@
|
||||
import { player, hooks, bindEvents, restoreSession, next, prev, toggle, seekSecs, playQueueAt } from "./player.svelte.js";
|
||||
import { nav, TABS, top, isRoot, bootNav, go, push, back, patch, openSheet, closeSheet, openHalf, closeHalf } from "./nav.svelte.js";
|
||||
import { loadPrefs } from "./prefs.svelte.js";
|
||||
import { setup, loadSetup, maybeOpenFirstRun, openSetup, finishSetup, setupState } from "./setup.svelte.js";
|
||||
import { catalog, artistKey } from "./catalog.svelte.js";
|
||||
import { bindMedia, syncMedia } from "./media.svelte.js";
|
||||
import { slsk, bindSlsk, connectStored, refreshRooms, unreadTotal } from "./slsk.svelte.js";
|
||||
@@ -159,6 +160,24 @@
|
||||
which is why that record is a plain object. */
|
||||
$effect(() => { syncShade(); });
|
||||
|
||||
/* ── the first run stamps when it goes away, whatever closed it ────
|
||||
The foot's button calls finishSetup itself; the scrim, the back
|
||||
gesture and the Soulseek row (which navigates, and navigating clears
|
||||
the half sheet) do not — and a tour that ran is a tour that ran.
|
||||
finishSetup writes only in boot mode and only once, so all four
|
||||
doors land in the same place, and a PREVIEW opened from You writes
|
||||
nothing at all.
|
||||
|
||||
untrack is not optional here: finishSetup calls closeHalf, which
|
||||
reads nav.half, which is exactly what this effect tracks. Read your
|
||||
own write in a Svelte 5 effect and the subtree dies painted. */
|
||||
let setupWasUp = false;
|
||||
$effect(() => {
|
||||
const up = nav.half?.kind === "setup";
|
||||
if (setupWasUp && !up) untrack(() => { void finishSetup(); });
|
||||
setupWasUp = up;
|
||||
});
|
||||
|
||||
/* ── a library read that failed gets a Try again ──────────────────
|
||||
The library is the one thing every screen stands on, and a rejected
|
||||
read left the app empty with a Rust string over it. The retry is wired
|
||||
@@ -248,9 +267,17 @@
|
||||
.catch(() => {});
|
||||
hooks.recentsDirty = refreshRecents;
|
||||
bindMedia();
|
||||
loadLibrary();
|
||||
/* the first run. The config is read at boot because the notes on
|
||||
the screens that are empty BECAUSE of it read this store — and
|
||||
the tour itself is offered only once the library and the saved
|
||||
account have both answered. Asked any earlier they are `[]` and
|
||||
`""` on every phone in existence, Kyle's included, and the sheet
|
||||
that must never open on his would open on his. */
|
||||
const configRead = loadSetup();
|
||||
const libRead = loadLibrary();
|
||||
/* the network: subscribe first, then the saved account signs in on its own */
|
||||
bindSlsk().then(() => connectStored()).catch(() => {});
|
||||
const netRead = bindSlsk().then(() => connectStored()).catch(() => {});
|
||||
Promise.all([configRead, libRead, netRead]).then(() => maybeOpenFirstRun()).catch(() => {});
|
||||
bindNet();
|
||||
watchLibrary();
|
||||
bindTransfers();
|
||||
@@ -278,7 +305,7 @@
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => { clearTimeout(sealTimer); sealBoot(); }));
|
||||
/* the probe handle — the desktop has __soulstream for the same reason:
|
||||
tests/mobile/smoke.mjs drives the real app through it */
|
||||
window.__ss = { nav, top, go, push, back, openSheet, closeSheet, openHalf, closeHalf, player, lib, art, catalog, slsk, boot, net, resolver, streamSong, streamCatalogAlbum, sources, api, transfers, xfn: xfernote, pls, notice, importer, imp: { startFolder }, wifi: { downloadsAllowed, cellular }, next, prev, toggle, seekSecs, playQueueAt, patch, contacts, loadContacts, say, refreshRooms };
|
||||
window.__ss = { nav, top, go, push, back, openSheet, closeSheet, openHalf, closeHalf, player, lib, art, catalog, slsk, boot, net, resolver, streamSong, streamCatalogAlbum, sources, api, transfers, xfn: xfernote, pls, notice, importer, imp: { startFolder }, setup, openSetup, setupState, wifi: { downloadsAllowed, cellular }, next, prev, toggle, seekSecs, playQueueAt, patch, contacts, loadContacts, say, refreshRooms };
|
||||
return () => { if (unlisten) unlisten(); };
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in new issue
Block a user