community: the inbox check belongs to the app, not to the page that happened to be open
This commit is contained in:
1 parent
fd216ecd3d
commit
b3c121fa94
3 files changed
+77
-18
No files matched your search
+64
-12
@@ -314,12 +314,12 @@ export function toggleRoomFav(name) {
|
||||
}
|
||||
|
||||
// ---- boot -------------------------------------------------------------------------
|
||||
let booted = false;
|
||||
/** Idempotent: the first Community mount wires the listeners and pulls the
|
||||
* initial state; later mounts only refresh. */
|
||||
export async function initCommunity() {
|
||||
if (!booted) {
|
||||
booted = true;
|
||||
let wired = false;
|
||||
/** The backend's community events, wired once per session. Both the app-wide
|
||||
* inbox watch and the Community page need them; whoever arrives first wires. */
|
||||
function wireEvents() {
|
||||
if (!wired) {
|
||||
wired = true;
|
||||
api.onEvent("freenet:status", (status) => {
|
||||
if (community.freenet) community.freenet.status = status;
|
||||
else void refreshFreenet();
|
||||
@@ -348,10 +348,59 @@ export async function initCommunity() {
|
||||
if (p.status === 1 || p.status === 2) noteSeen(p.username);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Idempotent: pulls the state the Community page draws. */
|
||||
export async function initCommunity() {
|
||||
wireEvents();
|
||||
await Promise.all([refreshFreenet(), loadContacts(), refreshLinks(), refreshFriends(), refreshShares(), refreshPublished(), refreshInbox(), refreshSpaces(), refreshOverlap()]);
|
||||
await refreshPersona();
|
||||
}
|
||||
|
||||
// ---- the inbox watch ---------------------------------------------------------------
|
||||
/** Everything sealed — friend requests, shares, proposal pings — arrives on one
|
||||
* inbox check, and that check used to live in Community.svelte's effect. So
|
||||
* nothing arrived unless that page happened to be open: a friend request sat
|
||||
* unseen while you were in the Library, and the proposal toast could only fire
|
||||
* somewhere it was least needed. The watch belongs to the app.
|
||||
*
|
||||
* App.svelte starts it once at boot. The gate is re-read every tick, so a node
|
||||
* that comes up later — or an inbox published later — simply starts working
|
||||
* without anything having to restart the timer. */
|
||||
const INBOX_POLL_MS = 90_000;
|
||||
let inboxWatch = 0;
|
||||
let polling = false;
|
||||
|
||||
export async function startInboxWatch() {
|
||||
if (inboxWatch) return;
|
||||
wireEvents();
|
||||
inboxWatch = setInterval(() => void pollInbox(), INBOX_POLL_MS);
|
||||
await refreshFreenet();
|
||||
await pollInbox();
|
||||
}
|
||||
|
||||
/** One tick, and quiet by design: a node that is down or an inbox that was
|
||||
* never published is the resting state of this app, not an error to show. */
|
||||
async function pollInbox() {
|
||||
if (polling) return;
|
||||
polling = true;
|
||||
try {
|
||||
// If the first read failed we have no status to gate on, and the
|
||||
// status event only speaks when something changes — so try again
|
||||
// rather than sit inert for the session.
|
||||
if (!community.freenet) await refreshFreenet();
|
||||
await refreshInbox();
|
||||
if (!adapterConnected(community.freenet?.status)) return;
|
||||
if (!community.inbox?.published) return;
|
||||
await readInbox();
|
||||
} catch {
|
||||
// A background poll never surfaces; the manual button is the one that
|
||||
// reports. The backend logs the reason either way.
|
||||
} finally {
|
||||
polling = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- native Soulseek cards ---------------------------------------------------------
|
||||
/** Fetch a person's native card and interests once per session, both in
|
||||
* flight together; errors are kept per person so the drawer can say so. */
|
||||
@@ -527,12 +576,15 @@ export const publishInbox = () =>
|
||||
community.inbox = await api.inboxPublish();
|
||||
});
|
||||
/** Read our inbox through the node, act on sealed cargo, prune it. */
|
||||
export const checkInbox = () =>
|
||||
run("inboxcheck", async () => {
|
||||
community.inboxLast = await api.inboxCheck();
|
||||
await Promise.all([refreshInbox(), refreshFriends(), refreshShares()]);
|
||||
return community.inboxLast;
|
||||
});
|
||||
async function readInbox() {
|
||||
community.inboxLast = await api.inboxCheck();
|
||||
await Promise.all([refreshInbox(), refreshFriends(), refreshShares()]);
|
||||
return community.inboxLast;
|
||||
}
|
||||
/** The Check now button: busy state, and a failure the page can show. The
|
||||
* background watch below deliberately does NOT go through this — a poll that
|
||||
* fails must not stamp the shared error slot. */
|
||||
export const checkInbox = () => run("inboxcheck", readInbox);
|
||||
export const friendRequestByKey = (personaKey) => friendAction("friend:" + personaKey, () => api.friendRequestByKey(personaKey));
|
||||
export const friendshipForKey = (key) => {
|
||||
const mine = community.friends.filter((f) => f.persona_key === key);
|
||||
|
||||
Reference in new issue
Block a user