mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 07:27:23 +02:00
* docs: add applesauce v5 upgrade plan Comprehensive migration plan covering: - Package updates (add applesauce-common, update to v5) - EventFactory import migration (applesauce-factory → applesauce-core) - Unified event loader setup - ActionHub → ActionRunner migration - useObservableMemo → use$ hook migration - New features: casting system, encrypted content caching - Documentation and skills updates needed * feat: upgrade applesauce libraries to v5 Major upgrade from applesauce v4 to v5 with breaking changes: Package updates: - applesauce-core: ^4.0.0 → ^5.0.0 - applesauce-actions: ^4.0.0 → ^5.0.0 - applesauce-loaders: ^4.0.0 → ^5.0.0 - applesauce-react: ^4.0.0 → ^5.0.0 - applesauce-relay: ^4.0.0 → ^5.0.0 - applesauce-signers: ^4.0.0 → ^5.0.0 - applesauce-accounts: ^4.0.0 → ^5.0.0 - Added new applesauce-common: ^5.0.0 package API migrations: - EventFactory: applesauce-factory → applesauce-core/event-factory - ActionHub → ActionRunner with async function pattern (not generators) - useObservableMemo → use$ hook across all components - Helper imports: article, highlight, threading, zap, comment, lists moved from applesauce-core to applesauce-common - parseCoordinate → parseReplaceableAddress - Subscription options: retries → reconnect - getEventPointerFromETag now returns null instead of throwing New features: - Unified event loader via createEventLoaderForStore - Updated loaders.ts to use v5 unified loader pattern Documentation: - Updated CLAUDE.md with v5 patterns and migration notes - Updated applesauce-core skill for v5 changes - Created new applesauce-common skill Test fixes: - Updated publish-spellbook.test.ts for v5 ActionRunner pattern - Updated publish-spell.test.ts with eventStore mock - Updated relay-selection.test.ts with valid test events - Updated loaders.test.ts with valid 64-char hex event IDs - Added createEventLoaderForStore mock --------- Co-authored-by: Claude <noreply@anthropic.com>
86 lines
2.3 KiB
TypeScript
86 lines
2.3 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import type { NostrEvent, Filter } from "nostr-tools";
|
|
import { useEventStore, use$ } from "applesauce-react/hooks";
|
|
import { createTimelineLoader } from "@/services/loaders";
|
|
import pool from "@/services/relay-pool";
|
|
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
|
import { useStableValue, useStableArray } from "./useStable";
|
|
|
|
interface UseTimelineOptions {
|
|
limit?: number;
|
|
}
|
|
|
|
interface UseTimelineReturn {
|
|
events: NostrEvent[];
|
|
loading: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
/**
|
|
* Hook for subscribing to a timeline of events from relays
|
|
* Uses applesauce loaders for efficient event loading and caching
|
|
* @param id - Unique identifier for this timeline (for caching)
|
|
* @param filters - Nostr filter object
|
|
* @param relays - Array of relay URLs
|
|
* @param options - Additional options like limit
|
|
* @returns Object containing events array, loading state, and error
|
|
*/
|
|
export function useTimeline(
|
|
id: string,
|
|
filters: Filter | Filter[],
|
|
relays: string[],
|
|
options: UseTimelineOptions = { limit: 20 },
|
|
): UseTimelineReturn {
|
|
const { limit } = options;
|
|
const eventStore = useEventStore();
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<Error | null>(null);
|
|
|
|
// Stabilize filters and relays to prevent unnecessary re-renders
|
|
const stableFilters = useStableValue(filters);
|
|
const stableRelays = useStableArray(relays);
|
|
|
|
// Load events into store
|
|
useEffect(() => {
|
|
if (relays.length === 0) return;
|
|
|
|
const loader = createTimelineLoader(
|
|
pool,
|
|
relays.concat(AGGREGATOR_RELAYS),
|
|
filters,
|
|
{
|
|
eventStore,
|
|
limit,
|
|
},
|
|
);
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const subscription = loader().subscribe({
|
|
error: (err: Error) => {
|
|
console.error("Timeline error:", err);
|
|
setError(err);
|
|
setLoading(false);
|
|
},
|
|
complete: () => {
|
|
setLoading(false);
|
|
},
|
|
});
|
|
|
|
return () => subscription.unsubscribe();
|
|
}, [id, stableRelays, limit, eventStore, stableFilters]);
|
|
|
|
// Watch store for matching events
|
|
const timeline = use$(() => {
|
|
return eventStore.timeline(filters, false);
|
|
}, [id]);
|
|
|
|
const hasItems = timeline ? timeline.length > 0 : false;
|
|
return {
|
|
events: timeline || [],
|
|
loading: hasItems ? false : loading,
|
|
error,
|
|
};
|
|
}
|