/* Taste: what this device has told the Soulseek server it likes and what it hates, and the people the server ranks as close to it. The desktop keeps all three in `community.svelte.js` and follows one rule — the ranking only moves when its input does, so every declaration asks the server again and the chips stay quiet until it has answered. The lists are THIS CLIENT's: the backend keeps them and re-declares every one of them at login, because the server's own copy belongs to the account and another client signed in elsewhere can change it under us. */ import * as api from "../lib/api.js"; import { slsk } from "./slsk.svelte.js"; import { player } from "./player.svelte.js"; export const taste = $state({ /** the things declared, as the backend last answered */ likes: [], hates: [], /** null until asked; { users: [{ username, rating }] } after */ similar: null, /** a declaration in flight, so the chips go quiet */ busy: false, asking: false, /** the server's word when it would not answer */ error: "", }); /* the account these lists were read for — a plain variable, never $state: the screen's effect is what calls loadTaste, and reading this inside it would arm that effect on our own write */ let readFor = null; /** What this device has declared. Once per account, unless forced. */ export async function loadTaste(force = false) { const who = slsk.username || slsk.savedUser || ""; if (readFor === who && !force) return; readFor = who; try { const v = await api.slskInterestsMine(); taste.likes = v?.likes ?? []; taste.hates = v?.hates ?? []; } catch (e) { player.error = String(e); } await askSimilar(true); } /** Declare a thing, or take it back. The backend answers with both lists, * and the server's match is asked again before the chips come back — it * ranks on exactly this, so a second declaration must not overtake it. */ export async function setInterest(thing, like, on) { const t = String(thing || "").trim(); if (!t || taste.busy) return false; taste.busy = true; try { const v = await api.slskInterestSet(t, like, on); taste.likes = v?.likes ?? []; taste.hates = v?.hates ?? []; await askSimilar(true); return true; } catch (e) { player.error = String(e); return false; } finally { taste.busy = false; } } /** The people the server puts near us, by the likes we declared. */ export async function askSimilar(force = false) { if ((taste.similar && !force) || taste.asking) return; taste.asking = true; taste.error = ""; try { taste.similar = { users: (await api.slskSimilarUsers()) || [] }; } catch (e) { taste.similar = { users: [] }; taste.error = String(e); } finally { taste.asking = false; } }