android: the handoff — the pending rounds, the pipeline, and what is unverified
Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
This commit is contained in:
1 parent
867fd42972
commit
9f0601027c
400 files changed
+26401
-3
No files matched your search
@@ -0,0 +1,51 @@
|
||||
/** The rows a record's own artist anchor found no count for — a feature's
|
||||
* guest, a compilation's stranger — asked by each row's OWN Deezer artist
|
||||
* id, the rule the desktop's search results follow. A row the backend has
|
||||
* no count for keeps nothing: unknown is not a zero. */
|
||||
export function loadTrackListeners(albumId, tracks) {
|
||||
if (albumId == null || catalog.listeners[albumId]) return;
|
||||
const want = (tracks || []).filter((t) => t.listeners == null && t.artist_id != null);
|
||||
if (!want.length) return;
|
||||
catalog.listeners[albumId] = { status: "loading", counts: {} };
|
||||
api.catalogTrackListeners(want.map((t) => ({ artistId: t.artist_id, title: t.title }))).then(
|
||||
(counts) => {
|
||||
const out = {};
|
||||
(counts || []).forEach((n, i) => { if (n != null && want[i]) out[want[i].id] = n; });
|
||||
catalog.listeners[albumId] = { status: "ready", counts: out };
|
||||
},
|
||||
() => { catalog.listeners[albumId] = { status: "error", counts: {} }; },
|
||||
);
|
||||
}
|
||||
|
||||
/** Forget an artist's page and ask for it again — the desktop's
|
||||
* `invalidateCatalogArtist`, for a hero that has just changed. The seed is
|
||||
* kept, so the identity search does not run a second time. */
|
||||
export function forgetArtist(name) {
|
||||
const k = artistKey(name);
|
||||
const cur = catalog.artists[k];
|
||||
if (!cur) return;
|
||||
delete catalog.artists[k];
|
||||
lookupArtist(name, cur.seed ?? null);
|
||||
}
|
||||
|
||||
/** A photo from this phone as an artist's hero — the desktop's Choose hero,
|
||||
* through the phone's own image picker. The page is asked again, so the
|
||||
* banner, the ground and the accent all follow. */
|
||||
export async function heroFromPhoto(id, name) {
|
||||
let r;
|
||||
try { r = await api.pickImage(); } catch (e) { if (/cancelled/.test(String(e))) return false; throw e; }
|
||||
if (!r?.path) return false;
|
||||
const hero = await api.setArtistHero(id, r.path);
|
||||
forgetArtist(name);
|
||||
note(`Hero set · ${hero.width}×${hero.height}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** The chosen hero removed — the catalog's own photos come back. */
|
||||
export async function heroClear(id, name) {
|
||||
const removed = await api.clearArtistHero(id);
|
||||
forgetArtist(name);
|
||||
note(removed ? "The catalog's own photos are back" : "No custom hero here");
|
||||
}
|
||||
|
||||
export function lookupAlbum(id) {
|
||||
Reference in new issue
Block a user