community: spaces, an owner-signed roster with epochs, a bounded window of signed lines, bodies sealed to a rotating key when private

This commit is contained in:
KeelrLeek committed 2026-09-02 15:50:18 -05:00
1 parent 4378c4c0cc
commit 8b511b20e7
14 files changed
+2913 -13

No files matched your search

+61 -2
View File
@@ -75,6 +75,11 @@ export const community = $state({
inbox: null,
/** What the last check found ({new_items, quarantined, pruned}). */
inboxLast: null,
/** spaces_list: the Spaces this device owns or was invited to. */
spaces: [],
/** The open Space (space_open), or null. */
space: null,
spaceError: "",
/** persona key → ProfileView | {none} | {error} | {pending}: other
* people's signed profiles, read once per session when surfaced. */
remoteProfiles: {},
@@ -319,6 +324,10 @@ export async function initCommunity() {
else if (community.persona) community.persona.available = false;
});
api.onEvent("community:friends", () => void refreshFriends());
api.onEvent("community:spaces", () => {
void refreshSpaces();
if (community.space) void openSpace(community.space.owner, community.space.space_id);
});
api.onEvent("community:shares", () => {
void refreshShares();
void refreshPublished();
@@ -327,7 +336,7 @@ export async function initCommunity() {
if (p.status === 1 || p.status === 2) noteSeen(p.username);
});
}
await Promise.all([refreshFreenet(), loadContacts(), refreshLinks(), refreshFriends(), refreshShares(), refreshPublished(), refreshInbox()]);
await Promise.all([refreshFreenet(), loadContacts(), refreshLinks(), refreshFriends(), refreshShares(), refreshPublished(), refreshInbox(), refreshSpaces()]);
await refreshPersona();
}
@@ -519,7 +528,57 @@ export const friendshipForKey = (key) => {
};
// ---- spaces -------------------------------------------------------------------------
export async function refreshSpaces() {
try {
community.spaces = await api.spacesList();
} catch {
// stays as it was
}
}
/** Read a Space through the node; the view is what the checkpoint says. */
export async function openSpace(owner, space) {
community.spaceError = "";
community.busy["space:" + space] = true;
try {
community.space = await api.spaceOpen(owner, space);
} catch (e) {
community.spaceError = String(e?.message ?? e);
} finally {
delete community.busy["space:" + space];
}
}
export const createSpace = (name, description, isPrivate) =>
run("space:create", async () => {
community.space = await api.spaceCreate(name, description, isPrivate);
await refreshSpaces();
});
export const postToSpace = (owner, space, text) =>
run("space:post", async () => {
await api.spacePost(owner, space, text);
await openSpace(owner, space);
});
export const inviteToSpace = (owner, space, personaKey) =>
run("space:invite", async () => {
community.space = await api.spaceInvite(owner, space, personaKey);
});
export const removeFromSpace = (owner, space, personaKey) =>
run("space:remove", async () => {
community.space = await api.spaceRemoveMember(owner, space, personaKey);
});
export const pinToSpace = (owner, space, playlistOwner, playlistUuid) =>
run("space:pin", async () => {
community.space = await api.spaceSetPinned(owner, space, playlistOwner, playlistUuid);
});
export const leaveSpace = (owner, space) =>
run("space:leave", async () => {
await api.spaceLeave(owner, space);
community.space = null;
await refreshSpaces();
});
// 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, friendRequestByKey, friendAccept, friendDecline, friendRemove, friendForget, checkLink, refreshFriends, sharedPublish, sharedWithdraw, shareSend, sharePreview, shareImport, refreshShares, refreshPublished, publishInbox, checkInbox, refreshInbox };
if (import.meta.env.DEV) globalThis.__communityActions = { friendRequest, friendRequestByKey, friendAccept, friendDecline, friendRemove, friendForget, checkLink, refreshFriends, sharedPublish, sharedWithdraw, shareSend, sharePreview, shareImport, refreshShares, refreshPublished, publishInbox, checkInbox, refreshInbox, refreshSpaces, openSpace, createSpace, postToSpace, inviteToSpace, removeFromSpace, pinToSpace, leaveSpace };