community: shared playlists, a signed revision and a pointer on the network, a reference over soulseek, an import through the normal door

This commit is contained in:
KeelrLeek committed 2026-09-02 15:02:06 -05:00
1 parent 984738135a
commit 13c44f731a
11 files changed
+1166 -3

No files matched your search

+72 -2
View File
@@ -65,6 +65,12 @@ export const community = $state({
linkErrors: {},
/** friends_list: every relationship this device holds, newest first. */
friends: [],
/** shares_inbox: playlist references people sent us, newest first. */
shares: [],
/** card id → fetched ShareView | {pending} | {error} (the Verify button). */
sharePreviews: {},
/** shared_playlists_mine: what this device has published. */
published: [],
/** persona key → ProfileView | {none} | {error} | {pending}: other
* people's signed profiles, read once per session when surfaced. */
remoteProfiles: {},
@@ -309,11 +315,15 @@ export async function initCommunity() {
else if (community.persona) community.persona.available = false;
});
api.onEvent("community:friends", () => void refreshFriends());
api.onEvent("community:shares", () => {
void refreshShares();
void refreshPublished();
});
api.onEvent("soulseek:user-state", (p) => {
if (p.status === 1 || p.status === 2) noteSeen(p.username);
});
}
await Promise.all([refreshFreenet(), loadContacts(), refreshLinks(), refreshFriends()]);
await Promise.all([refreshFreenet(), loadContacts(), refreshLinks(), refreshFriends(), refreshShares(), refreshPublished()]);
await refreshPersona();
}
@@ -419,7 +429,67 @@ export const friendCount = () => community.friends.filter((f) => f.state === "fr
export const pendingIn = () => community.friends.filter((f) => f.state === "pending-in");
// ---- shared playlists ---------------------------------------------------------------
export async function refreshShares() {
try {
community.shares = await api.sharesInbox();
} catch {
// stays as it was
}
}
export async function refreshPublished() {
try {
community.published = await api.sharedPlaylistsMine();
} catch {
// stays as it was
}
}
export const publishedFor = (playlistId) => community.published.find((p) => p.playlist_id === playlistId) ?? null;
export const sharedPublish = (playlistId) =>
run("publish:" + playlistId, async () => {
const r = await api.sharedPlaylistPublish(playlistId);
await refreshPublished();
return r;
});
export const sharedWithdraw = (playlistId) =>
run("publish:" + playlistId, async () => {
await api.sharedPlaylistWithdraw(playlistId);
await refreshPublished();
});
export const shareSend = (username, playlistId) => run("share:" + playlistId, () => api.shareSend(username, playlistId));
export const shareDismiss = (id) =>
run("share:" + id, async () => {
await api.shareDismiss(id);
await refreshShares();
});
/** Fetch and verify a shared playlist through our node — nothing imported. */
export async function sharePreview(card) {
community.sharePreviews[card.id] = { pending: true };
try {
community.sharePreviews[card.id] = await api.sharedPlaylistFetch(card.owner_key, card.playlist_uuid);
} catch (e) {
community.sharePreviews[card.id] = { error: String(e?.message ?? e) };
}
}
/** Fetch, verify, stage through the app's import path, commit — the new
* playlist's id comes back; the card is dismissed. */
export const shareImport = (card) =>
run("import:" + card.id, async () => {
const draft = await api.sharedPlaylistImport(card.owner_key, card.playlist_uuid);
const id = await api.playlistImportCommit(
draft.name ?? card.name,
draft.description ?? null,
draft.kind ?? "manual",
draft.entries,
draft.cover_url ?? null,
);
await api.shareDismiss(card.id);
await refreshShares();
return id;
});
// Dev handle beside `__soulstream` (state.svelte.js): probes reach the store
// through this, never through a bare import (which would be a second copy).
if (import.meta.env.DEV) globalThis.__community = community;
if (import.meta.env.DEV) globalThis.__communityActions = { friendRequest, friendAccept, friendDecline, friendRemove, friendForget, checkLink, refreshFriends };
if (import.meta.env.DEV) globalThis.__communityActions = { friendRequest, friendAccept, friendDecline, friendRemove, friendForget, checkLink, refreshFriends, sharedPublish, sharedWithdraw, shareSend, sharePreview, shareImport, refreshShares, refreshPublished };