Fix critical bug: prevent optimistic UI updates when event publish fails

**Problem:**
Task status changes (claim/start/complete/unclaim/delete) would update the
local UI state even when the Nostr event failed to publish to ANY relays.
This caused users to see "completed" tasks that were never actually published,
leading to confusion when the UI reverted after page refresh.

**Root Cause:**
ScheduledEventService optimistically updated local state after calling
publishEvent(), without checking if any relays accepted the event. If all
relay publishes failed (result.success = 0), the UI still updated.

**Solution:**
Modified RelayHub.publishEvent() to throw an error when no relays accept the
event (success = 0). This ensures:
- Existing try-catch blocks handle the error properly
- Error toast shown to user: "Failed to publish event - none of X relay(s) accepted it"
- Local state NOT updated (UI remains accurate)
- Consistent behavior across all services using publishEvent()

**Changes:**
- relay-hub.ts: Add check after publish - throw error if successful === 0
- ScheduledEventService.ts: Update comments to reflect new behavior

**Benefits:**
- Single source of truth for publish failure handling
- No code duplication (no need to check result.success everywhere)
- Better UX: Users immediately see error instead of false success
- UI state always matches server state after operations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-11-18 20:35:59 +01:00
parent 3b8c82514a
commit 078c55b8e9
2 changed files with 9 additions and 5 deletions

View file

@ -540,9 +540,13 @@ export class RelayHub extends BaseService {
const successful = results.filter(result => result.status === 'fulfilled').length
const total = results.length
this.emit('eventPublished', { eventId: event.id, success: successful, total })
// Throw error if no relays accepted the event
if (successful === 0) {
throw new Error(`Failed to publish event - none of the ${total} relay(s) accepted it`)
}
return { success: successful, total }
}

View file

@ -499,8 +499,8 @@ export class ScheduledEventService extends BaseService {
const result = await this.relayHub.publishEvent(signedEvent)
console.log('✅ Task status published to', result.success, '/', result.total, 'relays')
// Optimistically update local state
console.log('🔄 Optimistically updating local state')
// Update local state (publishEvent throws if no relays accepted)
console.log('🔄 Updating local state (event published successfully)')
this.handleCompletionEvent(signedEvent)
} catch (error) {
@ -562,7 +562,7 @@ export class ScheduledEventService extends BaseService {
const result = await this.relayHub.publishEvent(signedEvent)
console.log('✅ Deletion request published to', result.success, '/', result.total, 'relays')
// Optimistically remove from local state
// Remove from local state (publishEvent throws if no relays accepted)
this._completions.delete(completionKey)
console.log('🗑️ Removed completion from local state:', completionKey)
@ -624,7 +624,7 @@ export class ScheduledEventService extends BaseService {
const result = await this.relayHub.publishEvent(signedEvent)
console.log('✅ Task deletion request published to', result.success, '/', result.total, 'relays')
// Optimistically remove from local state
// Remove from local state (publishEvent throws if no relays accepted)
this._scheduledEvents.delete(eventAddress)
console.log('🗑️ Removed task from local state:', eventAddress)