import { useNostr } from "@nostrify/react"; import { useMutation, type UseMutationResult } from "@tanstack/react-query"; import { useCurrentUser } from "./useCurrentUser"; import type { NostrEvent } from "@nostrify/nostrify"; /** * Publishes a Nostr event with retry logic and improved error handling */ export function useNostrPublish(): UseMutationResult { const { nostr } = useNostr(); const { user } = useCurrentUser(); return useMutation({ mutationFn: async (t: Omit) => { if (!user) { throw new Error("You must be logged in to publish events"); } const tags = t.tags ?? []; // Add the client tag if it doesn't exist if (location.protocol === "https:" && !tags.some(([name]) => name === "client")) { tags.push(["client", location.hostname]); } // Sign the event const event = await user.signer.signEvent({ kind: t.kind, content: t.content ?? "", tags, created_at: t.created_at ?? Math.floor(Date.now() / 1000), }); // Publish with retry logic await publishEventWithRetry(nostr, event); return event; }, onError: (error) => { console.error("Failed to publish event:", error); }, }); } /** * Publishes event with retry logic and partial success handling */ async function publishEventWithRetry( nostr: any, event: NostrEvent, maxRetries = 1, timeoutMs = 15000 ): Promise { let lastError: Error | null = null; // Try publishing with retries for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { // Attempt to publish await nostr.event(event, { signal: controller.signal }); return; // Success! } finally { clearTimeout(timeoutId); } } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); // If this isn't the last attempt, wait before retrying if (attempt < maxRetries) { const backoffMs = 1000 * Math.pow(2, attempt); await new Promise(resolve => setTimeout(resolve, backoffMs)); } } } // All retries failed if (lastError) { // Provide user-friendly error messages if (lastError.name === 'AbortError' || lastError.message.includes('timeout')) { throw new Error( 'Publishing timed out. Your relays may be slow or unreachable. ' + 'Try checking your relay connections in Settings > Relays.' ); } else if (lastError.message.includes('network') || lastError.message.includes('fetch')) { throw new Error( 'Network error while publishing. Please check your internet connection and try again.' ); } else { throw new Error( `Failed to publish after ${maxRetries + 1} attempts: ${lastError.message}` ); } } throw new Error('Failed to publish event to any relay'); }