android: a name of your own for anyone on the network
The desktop's local contact metadata on the phone: a peer's sheet ends in Name and notes — a petname, a group, a private note and the Favorite and Trusted marks, kept in the app's own config and published nowhere. The petname stands in for the username on the buddy list, in the chat list, on a thread's bar, in the peer sheet, in a room's roster and on a room line, and the real username stays with it — on the line beneath, or beside the name in a room — so a stranger is never quietly turned into someone you know. Favourites sit at the top of the buddy list, and clearing a person's details drops the entry rather than storing an empty one — the backend's own rule.
This commit is contained in:
1 parent
f792d7955d
commit
7eae0ee945
10 files changed
+211
-16
No files matched your search
@@ -18,6 +18,7 @@
|
||||
import { lib, albumById, albumsBy, tracksOf, loadLibrary, refreshPlaylists, refreshRecents } from "./lib.svelte.js";
|
||||
import { lyr, nudge, searchCandidates, closeCandidates, chooseCandidate, forgetLyrics, pasteLyrics } from "./lyrics.svelte.js";
|
||||
import { art, wantArt } from "./art.svelte.js";
|
||||
import { metaOf, display, realName, setMeta, clearMeta } from "./contacts.svelte.js";
|
||||
import { downloadsAllowed } from "./network.svelte.js";
|
||||
import { note } from "./notice.svelte.js";
|
||||
import { fmtOf, albumFmt, fmtBytes, basename, dirname, longDur, mmss, titleFromName } from "./util.js";
|
||||
@@ -208,6 +209,16 @@
|
||||
note(`Saving ${g.files.length} file${g.files.length === 1 ? "" : "s"} from ${g.username}`);
|
||||
} catch (e) { oops(e); }
|
||||
}
|
||||
/* the contact editor's three fields go through the prompt sheet. The
|
||||
username travels as a VALUE, the way the playlist prompts do: the
|
||||
sheet switches kind under the prompt, so the contact block's
|
||||
{@const} is a destroyed derived by the time Save lands. */
|
||||
function metaPrompt(u, field, title, placeholder, value, multiline = false) {
|
||||
openHalf("prompt", {
|
||||
title, value, placeholder, ok: "Save", multiline, allowEmpty: true,
|
||||
onsubmit: (v) => { void setMeta(u, { [field]: v }); openHalf("contact", { username: u }); },
|
||||
});
|
||||
}
|
||||
/* the desktop's per-record cover refresh; the thumbnail is asked for again */
|
||||
async function refetchCover(album) {
|
||||
try {
|
||||
@@ -501,9 +512,10 @@
|
||||
{@const u = shown.arg.username}
|
||||
{@const g = shown.arg.group || null}
|
||||
{@const buddy = slsk.buddies.includes(u)}
|
||||
{@const m = metaOf(u)}
|
||||
<div class="sheet-head">
|
||||
<span class="art idc"><Identicon seed={u} size={44} /></span>
|
||||
<span class="b"><span class="t">{u}</span><span class="m">{g ? (g.slot ? "slot free" : "queue " + g.queue) + " · " + speedLabel(g.speed) + " · " + g.folderName : "on Soulseek"}</span></span>
|
||||
<span class="b"><span class="t">{display(u)}{#if m.favorite}<span class="nmark fav"><Icon name="star" size={12} /></span>{/if}{#if m.trusted}<span class="nmark tr"><Icon name="shield" size={12} /></span>{/if}</span><span class="m">{[realName(u), m.group, g ? (g.slot ? "slot free" : "queue " + g.queue) + " · " + speedLabel(g.speed) + " · " + g.folderName : "on Soulseek"].filter(Boolean).join(" · ")}</span></span>
|
||||
</div>
|
||||
<div class="sheet-rows">
|
||||
{#if g}
|
||||
@@ -519,6 +531,35 @@
|
||||
{/if}
|
||||
{@render row("message", "Message", "", act(() => { openThread(u); push("thread", { username: u }); }))}
|
||||
{@render row(buddy ? "x" : "heart", buddy ? "Remove buddy" : "Add buddy", buddy ? "" : "buddies stay on the Community screen", act(() => (buddy ? removeBuddy : addBuddy)(u).catch(oops)))}
|
||||
<div class="sheet-hair"></div>
|
||||
{@render row("pencil", "Name and notes", "a petname, a group, a note — this phone only", () => openHalf("contact", { username: u }))}
|
||||
</div>
|
||||
{:else if kind === "contact" && shown?.arg?.username}
|
||||
{@const u = shown.arg.username}
|
||||
{@const m = metaOf(u)}
|
||||
<div class="sheet-head">
|
||||
<span class="art idc"><Identicon seed={u} size={44} /></span>
|
||||
<span class="b"><span class="t">{display(u)}</span><span class="m">{realName(u) ? realName(u) + " · " : ""}kept on this phone, published nowhere</span></span>
|
||||
</div>
|
||||
<div class="sheet-rows">
|
||||
{@render row("pencil", "Petname", m.petname || "what you call them here", () => metaPrompt(u, "petname", "Petname", u, m.petname || ""))}
|
||||
{@render row("tag", "Group", m.group || "a word to sort them by", () => metaPrompt(u, "group", "Group", "Traders", m.group || ""))}
|
||||
{@render row("file-text", "Note", m.note || "a private note, for you only", () => metaPrompt(u, "note", "Note", "What you want to remember about them", m.note || "", true))}
|
||||
<div class="sheet-hair"></div>
|
||||
<button class="row" onclick={() => setMeta(u, { favorite: !m.favorite })}>
|
||||
<span class="lead"><Icon name="star" size={20} /></span>
|
||||
<span class="b"><span class="t">Favorite</span><span class="m">kept at the top of the buddy list</span></span>
|
||||
<span class="sw" class:on={m.favorite}><span></span></span>
|
||||
</button>
|
||||
<button class="row" onclick={() => setMeta(u, { trusted: !m.trusted })}>
|
||||
<span class="lead"><Icon name="shield" size={20} /></span>
|
||||
<span class="b"><span class="t">Trusted</span><span class="m">someone you take at their word</span></span>
|
||||
<span class="sw" class:on={m.trusted}><span></span></span>
|
||||
</button>
|
||||
{#if m.petname || m.group || m.note || m.trusted || m.favorite}
|
||||
<div class="sheet-hair"></div>
|
||||
{@render row("x", "Clear these details", "everything above leaves the list", act(() => { void clearMeta(u); }))}
|
||||
{/if}
|
||||
</div>
|
||||
{:else if (kind === "catalogAlbum-more" || kind === "catalogAlbum") && catPage}
|
||||
{@const al = catPage.album}
|
||||
@@ -634,7 +675,7 @@
|
||||
{@const buddy = slsk.buddies.includes(u.username)}
|
||||
<button class="row" onclick={act(() => { openThread(u.username); push("thread", { username: u.username }); })} oncontextmenu={(e) => { e.preventDefault(); openHalf("peer", { username: u.username }); }}>
|
||||
<span class="lead"><span class="dot" class:off={u.status === 0} class:warn={u.status === 1}></span></span>
|
||||
<span class="b"><span class="t">{u.username}{#if buddy} <span class="tag hi">BUDDY</span>{/if}</span><span class="m">{u.file_count ? u.file_count.toLocaleString() + " files" : ""}</span></span>
|
||||
<span class="b"><span class="t">{display(u.username)}{#if buddy} <span class="tag hi">BUDDY</span>{/if}</span><span class="m">{[realName(u.username), u.file_count ? u.file_count.toLocaleString() + " files" : ""].filter(Boolean).join(" · ")}</span></span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user