Files
buzz/desktop/src/features/projects/projectFetch.ts
thomaspblock 9733863516 perf(desktop): restore project context during startup (#6939)
## Summary
Project channels previously appeared as ordinary channels until the
community-wide project enumeration completed, delaying the contextual
right rail most noticeably on larger relays.

- Restore the last fully validated, relay-and-identity-scoped project
collection immediately while keeping live relay data authoritative.
- Resolve the active channel's project home through scoped
`#buzz-channel` queries instead of waiting for the complete project
scan.
- Keep snapshots aligned with community removal and relay reconnect
invalidation.

### Related issue
None found.

### Testing
- Pre-push `file-size-check`, `desktop-check`, `desktop-typecheck`, and
`desktop-test`
- Project enumeration, snapshot persistence, and relay invalidation unit
coverage
- Targeted Playwright scenario: project sidebar rows open the home
channel and nest extra channels

No numeric startup benchmark was captured; this draft validates the
cache and scoped-query behavior while leaving timing measurement for
review.

---------

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
2026-08-27 22:36:26 -04:00

76 lines
2.5 KiB
TypeScript

import { getCachedRelayOrigin } from "@/shared/lib/mediaUrl";
import { getIdentity } from "@/shared/api/tauriIdentity";
import {
buildProjectHomeFromFetcher,
buildProjectsFromFetcher,
type FetchProjectEventsExhaustively,
fetchProjectEventsExhaustively,
} from "./projectEnumeration";
import type { Project } from "./projectModels";
import { markProjectDataAuthoritative } from "./projectSnapshot";
const HIDDEN_PROJECT_CARDS_KEY = "buzz.projects.hidden-cards.v1";
function readHiddenProjectCards(): string[] {
if (typeof window === "undefined") {
return [];
}
try {
const parsed = JSON.parse(
window.localStorage.getItem(HIDDEN_PROJECT_CARDS_KEY) ?? "[]",
);
return Array.isArray(parsed)
? parsed.filter((item): item is string => typeof item === "string")
: [];
} catch {
return [];
}
}
/** Enumerates the projects visible to the current relay identity. */
export async function fetchProjects(
fetchExhaustively?: FetchProjectEventsExhaustively,
signal?: AbortSignal,
): Promise<Project[]> {
// Delegates to `buildProjectsFromFetcher` in `projectEnumeration.ts`, which
// is the pure, Tauri-free core of this operation. Its javadoc explains
// fail-closed tombstones and NIP-OA owner-deletion suppression.
const viewerPubkey = await getIdentity()
.then((identity) => identity.pubkey)
.catch(() => undefined);
const fetcher: FetchProjectEventsExhaustively =
fetchExhaustively ??
((kinds, extraFilter) =>
fetchProjectEventsExhaustively(kinds, extraFilter, undefined, signal));
const projects = await buildProjectsFromFetcher(fetcher, {
relayOrigin: getCachedRelayOrigin(),
hiddenAddresses: new Set(readHiddenProjectCards()),
viewerPubkey,
});
return projects.map((project) =>
markProjectDataAuthoritative(project, "relay"),
);
}
/** Resolves the active channel's project home with a scoped relay query. */
export async function fetchProjectHomeForChannel(
channelId: string,
signal?: AbortSignal,
): Promise<Project | null> {
const viewerPubkey = await getIdentity()
.then((identity) => identity.pubkey)
.catch(() => undefined);
const project = await buildProjectHomeFromFetcher(
(kinds, extraFilter) =>
fetchProjectEventsExhaustively(kinds, extraFilter, undefined, signal),
channelId,
{
relayOrigin: getCachedRelayOrigin(),
hiddenAddresses: new Set(readHiddenProjectCards()),
viewerPubkey,
},
);
return project ? markProjectDataAuthoritative(project, "relay") : null;
}