mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
5.4 KiB
5.4 KiB
NostrClient Overview
js/nostr/client.js exports the NostrClient class, which serves as the central controller for all Nostr network interactions and state management within the application. It acts as a facade over nostr-tools, managing connections, subscriptions, caching, and signing.
Key Responsibilities
- Connection Management: Maintains a pool of connections to multiple relays (
SimplePool). - State Management: Tracks video events, deduplicates them, and manages deletions via tombstones.
- Caching: Implements a "stale-while-revalidate" strategy using IndexedDB (with localStorage fallback) to restore UI state immediately upon load.
- Event Publishing: Orchestrates the signing and broadcasting of complex event chains (Video Note -> NIP-94 Mirror -> NIP-71 Metadata).
- Signer Abstraction: Unifies NIP-07 (Extension), NIP-46 (Remote/Bunker), and Local (nsec) signing under a single interface.
- Video History: Reconstructs the edit history of videos using
videoRootIdanddtags.
Architecture & State
The client maintains several internal maps to manage state:
allEvents: A raw map of all fetched video events (key: event ID).activeMap: A derived map containing only the latest version of each video, keyed by a stable identifier (videoRootIdor addressable pointer).rawEvents: A cache of the exact raw JSON events from relays, used for signature verification and re-publishing.tombstones: Tracks timestamps of deletion events to prevent "zombie" events (deleted events reappearing from slow relays) from resurfacing.
Caching Strategy (EventsCacheStore)
To ensure instant load times, the client persists its state to IndexedDB (bitvid-events-cache).
- Snapshot: On a debounced timer (or idle callback), the current state (
allEvents,tombstones) is serialized. - Fingerprinting: Events are fingerprinted to avoid writing unchanged data to the DB.
- Restore: On
init(), the client loads the latest snapshot into memory, allowing the UI to render before any network requests complete.
Key Flows
1. Initialization (init)
graph TD
A[init()] --> B[restoreLocalData]
B --> C{IndexedDB Available?}
C -- Yes --> D[Load Snapshot]
C -- No --> E[Load localStorage]
D & E --> F[Render UI with Cached Data]
F --> G[ensurePool]
G --> H[connectToRelays]
H --> I[Ready for Subscriptions]
2. Video Subscription (subscribeVideos)
To prevent UI thrashing during high-volume event ingress (e.g., initial flood from relays), the client uses a buffering and debouncing strategy.
- Buffer: Incoming events are pushed to
eventBufferinstead of being processed immediately. - Debounce: A flush timer is scheduled (75ms).
- Flush: When the timer fires, the entire buffer is processed:
- Events are validated and converted.
activeMapis updated (latest-wins logic).onVideocallback is fired once with the batch updates.- Cache persistence is scheduled.
3. Publishing a Video (publishVideo)
Publishing is a multi-step process ensuring compatibility and discoverability.
- Video Note (Kind 30078): The core content (magnet, infohash, metadata) is signed and published. A unique
videoRootIdis generated for new videos. - NIP-94 Mirror (Kind 1063): If a direct HTTP URL is provided, a file header event is published to point to the hosted file.
- NIP-71 Metadata (Kind 22/21): Addressable metadata tags are published to support categorization/grouping without modifying the immutable video note.
4. Remote Signer Handshake (connectRemoteSigner)
For NIP-46 (Nostr Connect), the client implements a full handshake flow:
- Ephemeral Key: Generates a local key pair for the session.
- Handshake: Publishes a Kind 24133 (
NIP46_RPC_KIND) event to the relay. - Wait: Listens for an acknowledgment or
auth_urlchallenge from the signer. - Session: Once connected, stores the session metadata to allow auto-reconnection.
Why it works this way
- Buffering: essential for performance. React rendering cycles are expensive; updating state for every single event in a 5000-event relay dump would freeze the browser.
- Tombstones: essential for eventual consistency. Relays are distributed and can be out of sync. If a user deletes a video, we must remember that deletion timestamp so a slow relay doesn't "undelete" it by serving the old event later.
- Dual-Layer Cache: IndexedDB is asynchronous and large-capacity; localStorage is synchronous but small. We use IndexedDB for the bulk data but keep a fallback for environments where IDB might be flaky or slow to initialize.
When to change
- Refactor
EventsCacheStore: If moving to a more robust state management library (like RxDB or dedicated worker), this logic should be extracted. - New Event Kinds: If the video data model changes (e.g., V4 schema),
convertEventToVideoandpublishVideowill need updates. - Signer upgrades: As NIP-46 evolves (e.g., Bunker standards), the handshake and connection logic in
nip46Client.js(and its orchestration here) will need adjustment.
Related Files
js/nostrEventSchemas.js: Defines the structure of the events being built/parsed.js/nostr/nip46Client.js: Handles the low-level RPC for remote signers.js/state/profileCache.js: Handles user profile metadata (Kind 0), which is separate from video data.