POWR/lib/hooks/useSubscribe.ts

145 lines
4.0 KiB
TypeScript
Raw Permalink Normal View History

// lib/hooks/useSubscribe.ts
import { useState, useEffect, useCallback, useRef } from 'react';
import { NDKEvent, NDKFilter, NDKSubscription, NDKSubscriptionOptions } from '@nostr-dev-kit/ndk-mobile';
import { useNDK } from './useNDK';
interface UseSubscribeOptions extends Partial<NDKSubscriptionOptions> {
enabled?: boolean;
deduplicate?: boolean;
}
export function useSubscribe(
filters: NDKFilter[] | false,
options: UseSubscribeOptions = {}
) {
const { ndk } = useNDK();
const [events, setEvents] = useState<NDKEvent[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [eose, setEose] = useState(false);
const subscriptionRef = useRef<NDKSubscription | null>(null);
// Default options
const {
enabled = true,
closeOnEose = false,
deduplicate = true,
...subscriptionOptions
} = options;
// Function to clear all events
const clearEvents = useCallback(() => {
setEvents([]);
}, []);
// Function to manually resubscribe
const resubscribe = useCallback(() => {
if (subscriptionRef.current) {
subscriptionRef.current.stop();
subscriptionRef.current = null;
}
setEvents([]);
setEose(false);
setIsLoading(true);
}, []);
2025-03-16 21:31:38 -04:00
// Direct fetch function for manual fetching
const manualFetch = useCallback(async () => {
if (!ndk || !filters) return;
try {
console.log('[useSubscribe] Manual fetch triggered');
setIsLoading(true);
const fetchedEvents = await ndk.fetchEvents(filters);
const eventsArray = Array.from(fetchedEvents);
setEvents(prev => {
if (deduplicate) {
const existingIds = new Set(prev.map(e => e.id));
const newEvents = eventsArray.filter(e => !existingIds.has(e.id));
return [...prev, ...newEvents];
}
return [...prev, ...eventsArray];
});
setIsLoading(false);
setEose(true);
} catch (err) {
console.error('[useSubscribe] Manual fetch error:', err);
setIsLoading(false);
}
}, [ndk, filters, deduplicate]);
// Only run the subscription effect when dependencies change
const filtersKey = filters ? JSON.stringify(filters) : 'none';
const optionsKey = JSON.stringify(subscriptionOptions);
useEffect(() => {
if (!ndk || !filters || !enabled) {
setIsLoading(false);
return;
}
2025-03-16 21:31:38 -04:00
// Clean up any existing subscription
if (subscriptionRef.current) {
subscriptionRef.current.stop();
subscriptionRef.current = null;
}
setIsLoading(true);
setEose(false);
try {
2025-03-16 21:31:38 -04:00
console.log('[useSubscribe] Creating new subscription');
// Create subscription with NDK
const subscription = ndk.subscribe(filters, {
closeOnEose,
...subscriptionOptions
});
subscriptionRef.current = subscription;
2025-03-16 21:31:38 -04:00
// Event handler - use a function reference to avoid recreating
const handleEvent = (event: NDKEvent) => {
setEvents(prev => {
if (deduplicate && prev.some(e => e.id === event.id)) {
return prev;
}
return [...prev, event];
});
2025-03-16 21:31:38 -04:00
};
2025-03-16 21:31:38 -04:00
// EOSE handler
const handleEose = () => {
setIsLoading(false);
setEose(true);
2025-03-16 21:31:38 -04:00
};
subscription.on('event', handleEvent);
subscription.on('eose', handleEose);
// Clean up on unmount or when dependencies change
return () => {
if (subscription) {
subscription.off('event', handleEvent);
subscription.off('eose', handleEose);
subscription.stop();
}
subscriptionRef.current = null;
};
} catch (error) {
2025-03-16 21:31:38 -04:00
console.error('[useSubscribe] Subscription error:', error);
setIsLoading(false);
}
2025-03-16 21:31:38 -04:00
}, [ndk, enabled, filtersKey, optionsKey, closeOnEose, deduplicate]);
return {
events,
isLoading,
eose,
clearEvents,
resubscribe,
2025-03-16 21:31:38 -04:00
fetchEvents: manualFetch // Function to trigger manual fetch
};
}