From 3091ad7fd4af815da2b7d179c15dbe6c3d67dd89 Mon Sep 17 00:00:00 2001 From: Gigi Date: Wed, 15 Oct 2025 09:29:54 +0200 Subject: [PATCH] feat(write): add unified publishEvent service and refactor highlight and settings to use it --- src/services/highlightCreationService.ts | 63 ++++++------------------ src/services/settingsService.ts | 17 +++---- src/services/writeService.ts | 59 ++++++++++++++++++++++ 3 files changed, 82 insertions(+), 57 deletions(-) create mode 100644 src/services/writeService.ts diff --git a/src/services/highlightCreationService.ts b/src/services/highlightCreationService.ts index edbdd47d..e5a93d21 100644 --- a/src/services/highlightCreationService.ts +++ b/src/services/highlightCreationService.ts @@ -7,8 +7,8 @@ import { Helpers, IEventStore } from 'applesauce-core' import { RELAYS } from '../config/relays' import { Highlight } from '../types/highlights' import { UserSettings } from './settingsService' -import { areAllRelaysLocal } from '../utils/helpers' -import { markEventAsOfflineCreated } from './offlineSyncService' +import { isLocalRelay, areAllRelaysLocal } from '../utils/helpers' +import { publishEvent } from './writeService' // Boris pubkey for zap splits // npub19802see0gnk3vjlus0dnmfdagusqrtmsxpl5yfmkwn9uvnfnqylqduhr0x @@ -118,59 +118,26 @@ export async function createHighlight( // Sign the event const signedEvent = await factory.sign(highlightEvent) - // Publish to all configured relays - let the relay pool handle connection state - const targetRelays = RELAYS - - // Store the event in the local EventStore FIRST for immediate UI display - eventStore.add(signedEvent) - console.log('💾 Stored highlight in EventStore:', signedEvent.id.slice(0, 8)) - - // Check current connection status - are we online or in flight mode? + // Use unified write service to store and publish + await publishEvent(relayPool, eventStore, signedEvent, settings) + + // Check current connection status for UI feedback const connectedRelays = Array.from(relayPool.relays.values()) .filter(relay => relay.connected) .map(relay => relay.url) - - const hasRemoteConnection = connectedRelays.some(url => - !url.includes('localhost') && !url.includes('127.0.0.1') - ) - - // Determine which relays we expect to succeed - const expectedSuccessRelays = hasRemoteConnection - ? RELAYS - : RELAYS.filter(r => r.includes('localhost') || r.includes('127.0.0.1')) - + + const hasRemoteConnection = connectedRelays.some(url => !isLocalRelay(url)) + const expectedSuccessRelays = hasRemoteConnection + ? RELAYS + : RELAYS.filter(isLocalRelay) const isLocalOnly = areAllRelaysLocal(expectedSuccessRelays) - - console.log('📍 Highlight relay status:', { - targetRelays: targetRelays.length, - expectedSuccessRelays, - isLocalOnly, - hasRemoteConnection, - eventId: signedEvent.id - }) - - // If we're in local-only mode, mark this event for later sync - if (isLocalOnly) { - markEventAsOfflineCreated(signedEvent.id) - } - + // Convert to Highlight with relay tracking info and return IMMEDIATELY const highlight = eventToHighlight(signedEvent) - highlight.publishedRelays = expectedSuccessRelays // Show only relays we expect to succeed + highlight.publishedRelays = expectedSuccessRelays highlight.isLocalOnly = isLocalOnly - highlight.isOfflineCreated = isLocalOnly // Mark as created offline if local-only - - // Publish to relays in the background (non-blocking) - // This allows instant UI updates while publishing happens asynchronously - relayPool.publish(targetRelays, signedEvent) - .then(() => { - console.log('✅ Highlight published to', targetRelays.length, 'relay(s):', targetRelays) - }) - .catch((error) => { - console.warn('⚠️ Failed to publish highlight to relays (event still saved locally):', error) - }) - - // Return the highlight immediately for instant UI updates + highlight.isOfflineCreated = isLocalOnly + return highlight } diff --git a/src/services/settingsService.ts b/src/services/settingsService.ts index 021b6655..5e081106 100644 --- a/src/services/settingsService.ts +++ b/src/services/settingsService.ts @@ -3,6 +3,7 @@ import { EventFactory } from 'applesauce-factory' import { RelayPool, onlyEvents } from 'applesauce-relay' import { NostrEvent } from 'nostr-tools' import { firstValueFrom } from 'rxjs' +import { publishEvent } from './writeService' const SETTINGS_IDENTIFIER = 'com.dergigi.boris.user-settings' const APP_DATA_KIND = 30078 // NIP-78 Application Data @@ -148,10 +149,10 @@ export async function saveSettings( eventStore: IEventStore, factory: EventFactory, settings: UserSettings, - relays: string[] + _relays: string[] ): Promise { console.log('💾 Saving settings to nostr:', settings) - + // Create NIP-78 application data event manually // Note: AppDataBlueprint is not available in the npm package const draft = await factory.create(async () => ({ @@ -160,14 +161,12 @@ export async function saveSettings( tags: [['d', SETTINGS_IDENTIFIER]], created_at: Math.floor(Date.now() / 1000) })) - + const signed = await factory.sign(draft) - - console.log('📤 Publishing settings event:', signed.id, 'to', relays.length, 'relays') - - eventStore.add(signed) - await relayPool.publish(relays, signed) - + + // Use unified write service + await publishEvent(relayPool, eventStore, signed, settings) + console.log('✅ Settings published successfully') } diff --git a/src/services/writeService.ts b/src/services/writeService.ts new file mode 100644 index 00000000..55ef4e17 --- /dev/null +++ b/src/services/writeService.ts @@ -0,0 +1,59 @@ +import { RelayPool } from 'applesauce-relay' +import { NostrEvent } from 'nostr-tools' +import { IEventStore } from 'applesauce-core' +import { UserSettings } from './settingsService' +import { RELAYS } from '../config/relays' +import { isLocalRelay, areAllRelaysLocal } from '../utils/helpers' +import { markEventAsOfflineCreated } from './offlineSyncService' + +/** + * Unified write helper: add event to EventStore, detect connectivity, + * mark for offline sync if needed, and publish in background. + */ +export async function publishEvent( + relayPool: RelayPool, + eventStore: IEventStore, + event: NostrEvent, + settings?: UserSettings +): Promise { + // Store the event in the local EventStore FIRST for immediate UI display + eventStore.add(event) + console.log('💾 Stored event in EventStore:', event.id.slice(0, 8), `(kind ${event.kind})`) + + // Check current connection status - are we online or in flight mode? + const connectedRelays = Array.from(relayPool.relays.values()) + .filter(relay => relay.connected) + .map(relay => relay.url) + + const hasRemoteConnection = connectedRelays.some(url => !isLocalRelay(url)) + + // Determine which relays we expect to succeed + const expectedSuccessRelays = hasRemoteConnection + ? RELAYS + : RELAYS.filter(isLocalRelay) + + const isLocalOnly = areAllRelaysLocal(expectedSuccessRelays) + + console.log('📍 Event relay status:', { + targetRelays: RELAYS.length, + expectedSuccessRelays: expectedSuccessRelays.length, + isLocalOnly, + hasRemoteConnection, + eventId: event.id.slice(0, 8) + }) + + // If we're in local-only mode, mark this event for later sync + if (isLocalOnly) { + markEventAsOfflineCreated(event.id) + } + + // Publish to all configured relays in the background (non-blocking) + relayPool.publish(RELAYS, event) + .then(() => { + console.log('✅ Event published to', RELAYS.length, 'relay(s):', event.id.slice(0, 8)) + }) + .catch((error) => { + console.warn('⚠️ Failed to publish event to relays (event still saved locally):', error) + }) +} +