Event-driven search and schedule views
Mailbox views use events to refresh search history, results, saved searches, schedules, and autonomous-sort progress. Sync Center operation/conflict refresh and passive-sort status refresh have separate behavior.
When the browser reads data
| Data | Initial read | Later reads |
|---|---|---|
| Search history | After account event subscription | Unknown-search authorization, local action, reconnection |
| Search results | When opening the selected search | Live event, gap/completion backfill, reconnection |
| Saved searches | When search opens | Save, rename, delete, local action, reconnection |
| Schedules | When opening the account view | schedule.changed, sync.recovered, local action, reconnection |
| Autonomous-sort progress | Initial account-scoped status read | appProcessProgress, reconnection |
These event-driven views have no fallback polling during a WebSocket outage. They display the last known state. On reconnection, authenticated HTTP reads restore observed accounts. Browser disconnection does not stop backend processing.
Passive search components observe the cache through useSyncExternalStore without an active QueryObserver. An empty search ID starts no search. They do not create a passive skipToken observer on an active query's key. Local subscription-readiness state remains separate. Stable autonomous-sort callbacks prevent a mail-list rerender from restarting status reads.
Another account's event, an old response, or an old completion timer cannot replace newer state.
Recover search results
The connected-account ID identifies the browser's authenticated account selection. A search event's userId is the backend mailbox user ID. They are not interchangeable. For an unknown search, an authenticated BFF history response must establish access before the event enters that account's cache.
The live cursor records the latest observed event state. The backfill cursor advances only after the client reads durable result pages. WebSocket arrival order and a higher live cursor do not prove that all results arrived.
The client pages through durable results on completion, detected gaps, opening, and reconnection. Older responses cannot regress newer event state. Within one QueryClient, an account shares its listener and deleted-search record.
Event-driven HTTP reads coalesce. An event during a read causes a trailing read. Deleted searches cannot return through an older response.
Returning to an account rereads history and saved searches once because events could arrive while unsubscribed. Opening the schedule view also reads current state. This is an entry read, not timed polling.
Schedule event contract
UserRealtimeSyncEventV1.eventType uses schedule.changed.
| Payload field | Meaning |
|---|---|
scheduleId | Schedule UUID |
draftId | Stable draft identity |
status | Current schedule state |
revision | Requested schedule revision |
Envelope scope includes tenant, mailbox, and user identity. correlationId is the schedule ID. The event excludes message content and recipients.
The state change and outbox entry share a transaction. Delivery passes through graphSyncNotifications, InterfaceNotifier, appRealtime, and the authenticated BFF WebSocket. The BFF resolves the connected account on the server and refreshes its email-schedules cache.
Events cover acceptance, rescheduling, cancellation, worker transitions, sending, failures, recovery, and Outlook projection. Conditional writes retain revision and lock predicates. Rollback removes the event too. Rechecking unchanged visible state does not emit another event. A failed send prerequisite persists failure and notification before the processor throws.
For Outlook cancellation/rescheduling, the client reads once after acceptance, then checks the requested revision on events or reconnection. The 45-second deadline returns pending. It starts no extra mutation and does not report unconfirmed work as success. See change a schedule.