perf: Make NIP-17 inbox sync on-demand for optimal login performance

This commit eliminates automatic inbox initialization on login, preventing
unwanted network requests and heavy I/O operations. Inbox sync now only
activates when users explicitly enable it.

## Problem

When logging in, the app would automatically:
- Initialize gift wrap service immediately
- Auto-enable inbox sync without user consent
- Load encrypted content from Dexie
- Wait up to 1 second for cache readiness
- Fetch inbox relay lists from network
- Subscribe to gift wrap events
- Open persistent relay connections

This caused:
- App hangs during login (network/IO blocking)
- Unwanted network activity before user opts in
- Poor performance on initial load
- Unnecessary resource consumption when DMs not needed

## Solution

### 1. On-Demand Initialization (useAccountSync.ts)
**Before**: Auto-init and auto-enable on every login
**After**: Watch settings$ and only init when user enables

```typescript
// Only initialize when user explicitly enables inbox sync
const settingsSub = giftWrapService.settings$.subscribe((settings) => {
  if (settings.enabled && giftWrapService.userPubkey !== pubkey) {
    giftWrapService.init(pubkey, signer);
  }
});
```

### 2. Early Exit for Disabled State (gift-wrap.ts)
**Before**: Always loaded cache and relays, then checked enabled flag
**After**: Check enabled FIRST, exit early if disabled

```typescript
async init(pubkey: string, signer: ISigner | null) {
  // Set basic properties
  this.userPubkey = pubkey;
  this.signer = signer;

  // Early exit if disabled (prevents expensive operations)
  if (!this.settings$.value.enabled) {
    return;
  }

  // Only do expensive operations when enabled
  await getStoredEncryptedContentIds();
  await this.waitForCacheReady();
  this.loadInboxRelays();
  // ...
}
```

### 3. Updated Documentation
- Clarified on-demand initialization flow
- Updated lifecycle documentation with performance notes
- Changed "auto-enable" section to "on-demand" section

## Performance Impact

**Login Performance**:
-  No automatic Dexie reads
-  No cache waiting (up to 1s saved)
-  No network requests for inbox relays
-  No relay subscriptions until needed
-  Instant login when DMs not needed

**User Control**:
- Users explicitly opt-in via InboxViewer toggle
- Clear UI feedback about enabling inbox sync
- No surprise network activity

**When Enabled**:
- Full functionality identical to before
- All optimizations from previous commits preserved

## Testing

-  All 864 tests pass
-  Build succeeds with no errors
-  Verified on-demand initialization flow
-  Confirmed no auto-init on login

## Files Changed

- `src/hooks/useAccountSync.ts` - Watch settings, init only when enabled
- `src/services/gift-wrap.ts` - Early exit if disabled, expose userPubkey
- `src/components/InboxViewer.tsx` - Updated comments
- `docs/gift-wrap-architecture.md` - Updated flow and lifecycle docs

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gómez
2026-01-16 17:04:05 +01:00
parent cfeb40f42d
commit dc4345a64b
4 changed files with 59 additions and 52 deletions

View File

@@ -124,14 +124,15 @@ Chat Component
### Receiving Messages (Inbox Flow)
1. **Account Login**`useAccountSync` calls `giftWrapService.init(pubkey, signer)`
2. **Fetch Inbox Relays** → Load kind 10050 from user's outbox relays
3. **Subscribe to Gift Wraps** → Open subscription to inbox relays for `kind 1059` with `#p` = user pubkey
4. **Gift Wrap Arrival** → EventStore receives event → GiftWrapService detects new gift wrap
5. **Decrypt** (if auto-decrypt enabled) → Call `unlockGiftWrap(event, signer)`
6. **Extract Rumor** → Get kind 14 DM from gift wrap inner content
7. **Group into Conversations** → Compute conversation ID from participants → Update `conversations$` observable
8. **UI Update** → InboxViewer/ChatViewer re-renders with new messages
1. **User Enables Inbox Sync** → User toggles "Enable Inbox Sync" in InboxViewer settings
2. **Service Initialization**`useAccountSync` detects enabled setting and calls `giftWrapService.init(pubkey, signer)`
3. **Fetch Inbox Relays** → Load kind 10050 from user's outbox relays
4. **Subscribe to Gift Wraps** → Open subscription to inbox relays for `kind 1059` with `#p` = user pubkey
5. **Gift Wrap Arrival** → EventStore receives event → GiftWrapService detects new gift wrap
6. **Decrypt** (if auto-decrypt enabled) → Call `unlockGiftWrap(event, signer)`
7. **Extract Rumor** → Get kind 14 DM from gift wrap inner content
8. **Group into Conversations** → Compute conversation ID from participants → Update `conversations$` observable
9. **UI Update** → InboxViewer/ChatViewer re-renders with new messages
### Sending Messages (Outbox Flow)
@@ -161,18 +162,19 @@ Chat Component
### Lifecycle
**Init** (on account login):
**Init** (when user enables inbox sync):
```typescript
giftWrapService.init(pubkey, signer)
1. Load persisted encrypted content IDs from Dexie
2. Wait for cache readiness (prevents race condition)
3. Subscribe to user's kind 10050 (inbox relays)
4. Load stored gift wraps from Dexie into EventStore
5. Subscribe to EventStore timeline for real-time updates
6. Open persistent relay subscription for new gift wraps
1. Check if enabled (early return if disabled for performance)
2. Load persisted encrypted content IDs from Dexie
3. Wait for cache readiness (prevents race condition)
4. Subscribe to user's kind 10050 (inbox relays)
5. Load stored gift wraps from Dexie into EventStore
6. Subscribe to EventStore timeline for real-time updates
7. Open persistent relay subscription for new gift wraps
```
**Cleanup** (on account logout):
**Cleanup** (on account logout or disable):
```typescript
giftWrapService.cleanup()
1. Unsubscribe from all observables
@@ -180,6 +182,8 @@ giftWrapService.cleanup()
3. Clear in-memory state
```
**Performance Note**: Init is only called when user explicitly enables inbox sync via InboxViewer toggle. This prevents automatic network requests and heavy I/O operations on login.
## Cache Strategy
### Encrypted Content Persistence
@@ -231,13 +235,16 @@ giftWrapService.cleanup()
- Clear UI feedback about why sending is blocked
- Relay lists can be fetched in background without blocking UI
### Auto-Enable Inbox Sync
### On-Demand Inbox Sync
**Default**: Inbox sync is **auto-enabled** on first login to ensure users receive DMs.
**Default**: Inbox sync is **disabled** on login for optimal performance.
**Rationale**: Better UX to auto-enable with opt-out than require manual setup.
**Rationale**: Prevents automatic network requests and heavy I/O operations on login. Users must explicitly enable inbox sync to receive DMs.
**User Control**: Settings UI allows disabling inbox sync and auto-decrypt.
**User Control**:
- Enable/disable inbox sync via toggle in InboxViewer
- Configure auto-decrypt behavior in settings
- Service initializes only when explicitly enabled
### Relay List Privacy