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,70 @@
|
||||
/* The local view of a person on the network: a petname, a group, a private
|
||||
note and the two marks. All of it lives in this app's own config and is
|
||||
published nowhere — Soulseek never learns it, and neither does the person
|
||||
it describes. The desktop keeps the same index by username
|
||||
(src/lib/community.svelte.js), and the same rule: the backend drops an
|
||||
entry with nothing set, so the list it answers with is the one we take. */
|
||||
import * as api from "../lib/api.js";
|
||||
import { player } from "./player.svelte.js";
|
||||
|
||||
export const contacts = $state({
|
||||
/** username → { username, petname, group, note, trusted, favorite } */
|
||||
by: {},
|
||||
/** the list came back; false until it does, and after a failure */
|
||||
loaded: false,
|
||||
error: "",
|
||||
});
|
||||
|
||||
const BLANK = { petname: null, group: null, note: null, trusted: false, favorite: false };
|
||||
/** what the backend stores: a trimmed string, or nothing at all */
|
||||
const trimmed = (v) => { const s = typeof v === "string" ? v.trim() : v; return s ? s : null; };
|
||||
|
||||
function index(list) {
|
||||
contacts.by = Object.fromEntries((list || []).map((c) => [c.username, c]));
|
||||
contacts.loaded = true;
|
||||
contacts.error = "";
|
||||
}
|
||||
|
||||
let inflight = null;
|
||||
/** Read the whole list — once at boot, and again whenever anyone asks. A
|
||||
* failure is quiet: a petname is an enhancement, not the screen. */
|
||||
export function loadContacts() {
|
||||
if (inflight) return inflight;
|
||||
inflight = api
|
||||
.contactsMetaList()
|
||||
.then(index)
|
||||
.catch((e) => { contacts.loaded = false; contacts.error = String(e); })
|
||||
.finally(() => { inflight = null; });
|
||||
return inflight;
|
||||
}
|
||||
|
||||
export const metaOf = (u) => contacts.by[u] ?? { username: u, ...BLANK };
|
||||
/** The name to show: the petname when there is one, the username otherwise. */
|
||||
export const display = (u) => contacts.by[u]?.petname || u;
|
||||
/** The username, but only while a petname stands in front of it — a stranger
|
||||
* is never quietly turned into someone you know. */
|
||||
export const realName = (u) => (contacts.by[u]?.petname ? u : "");
|
||||
|
||||
/** Upsert one person. The answer is the whole list after the write, so it is
|
||||
* taken as it comes rather than patched into our own copy: that is how an
|
||||
* entry with nothing left in it disappears. A failure goes to the bar. */
|
||||
export async function setMeta(username, patch) {
|
||||
const m = { ...metaOf(username), ...patch };
|
||||
try {
|
||||
index(
|
||||
await api.contactMetaSet({
|
||||
username,
|
||||
petname: trimmed(m.petname),
|
||||
group: trimmed(m.group),
|
||||
note: trimmed(m.note),
|
||||
trusted: !!m.trusted,
|
||||
favorite: !!m.favorite,
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
player.error = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Everything blank: the entry leaves the list rather than being stored empty. */
|
||||
export const clearMeta = (u) => setMeta(u, { petname: "", group: "", note: "", trusted: false, favorite: false });
|
||||
Reference in new issue
Block a user