Skip to content

Handle Graph API conversation fetch failures without stalling UI (ERMAIN-347)#769

Draft
bdart wants to merge 4 commits into
mainfrom
feat/graph-api-conversation-fetch-graceful-degradation-9bc5
Draft

Handle Graph API conversation fetch failures without stalling UI (ERMAIN-347)#769
bdart wants to merge 4 commits into
mainfrom
feat/graph-api-conversation-fetch-graceful-degradation-9bc5

Conversation

@bdart

@bdart bdart commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Problem

When the Graph API conversation fetch is slow or times out (full timeout is 20 seconds), the chat-input composer was disabled for the entire wait via isWaitingForSuggestedEmail. Users on slow or flaky networks were unable to type for up to 20 seconds — the UI appeared frozen.

The email loading chip was already shown, providing a visual indicator, but the send button remained disabled for the full fetch duration.

Solution

Introduce a short UI-block deadline (5 seconds). The composer blocks briefly — enough for a fast network to return the email context and attach it automatically — then unblocks after the deadline regardless of whether the fetch has completed.

The loading chip continues to display for the full fetch duration so the user knows the email context is still resolving. If the fetch eventually succeeds before the user sends, the email is still included.

Changes

office-addin/src/utils/graphRequestTimeout.ts

  • Add OUTLOOK_GRAPH_THREAD_UI_BLOCK_DEADLINE_MS = 5_000 with a clear docstring explaining the intent.

office-addin/src/hooks/useCurrentThread.ts

  • Add isBlockingLoad: boolean to UseCurrentThreadResult.
    • true while the fetch is in-flight and within the 5-second deadline.
    • false once the deadline elapses (even if the fetch is still running), or when the fetch completes or errors.
  • A timer effect resets and restarts the deadline whenever itemId / conversationId changes, so switching conversations re-blocks the input briefly for the new fetch.
  • The cleanup function clears the timer on unmount or key change.

office-addin/src/providers/OutlookEmailSourceProvider.tsx

  • Add isBlockingLoadEmailBody: boolean to OutlookEmailSourceContextValue, mapped from useCurrentThread.isBlockingLoad.
  • Kept isLoadingEmailBody unchanged — it still drives the loading chip for the full fetch duration.

office-addin/src/components/AddinChatInput.tsx

  • Update isWaitingForSuggestedEmail to gate on isBlockingLoadEmailBody (5-second window) instead of isLoadingEmailBody (full fetch duration).
  • shouldShowEmailSourcePreview and the chip rendering logic continue to use isLoadingEmailBody so the loading chip remains visible throughout.

Tests

  • useCurrentThread.test.ts: 4 new cases for isBlockingLoad:
    • False when disabled (no itemId/conversationId)
    • True on initial load, flips false after the UI-block deadline (using fake timers)
    • False immediately when the fetch errors
    • Resets to true when conversationId changes after the deadline elapsed
  • OutlookEmailSourceProvider.test.tsx: 2 new cases verifying isBlockingLoadEmailBody is forwarded correctly from useCurrentThread, including the "deadline elapsed but still loading" scenario.
  • Updated existing toEqual assertions in useCurrentThread.test.ts to include isBlockingLoad: false.

All 524 tests pass.

Linear Issue: ERMAIN-347

Open in Web Open in Cursor 

cursoragent and others added 4 commits June 24, 2026 06:56
When the Graph API conversation fetch is slow or times out (up to 20s),
the chat-input composer was disabled for the entire fetch duration via
isWaitingForSuggestedEmail. This freezes the UI for users on slow or
flaky networks.

Changes:
- Add OUTLOOK_GRAPH_THREAD_UI_BLOCK_DEADLINE_MS (5s) to graphRequestTimeout.ts
- Add isBlockingLoad to UseCurrentThreadResult: true while the fetch is
  in-flight AND within the short deadline; false once the deadline elapses
  (even if the fetch is still running) or when the fetch completes/errors
- Expose isBlockingLoadEmailBody from OutlookEmailSourceProvider, mapping
  directly from useCurrentThread.isBlockingLoad
- Update AddinChatInput.isWaitingForSuggestedEmail to gate on
  isBlockingLoadEmailBody instead of isLoadingEmailBody

The loading chip (driven by isLoadingEmailBody) continues to display for
the full fetch duration so the user knows the email context is still
resolving. If the fetch eventually succeeds the email is attached when
the user sends. The UI is never blocked for more than 5 seconds.

Co-authored-by: Daniel <bdart@users.noreply.github.com>
Add tests for:
- isBlockingLoad is false when fetch is disabled
- isBlockingLoad is true during initial load, flips false after deadline
- isBlockingLoad is false immediately on fetch error
- isBlockingLoad resets to true on conversationId change after deadline
- isBlockingLoadEmailBody is forwarded from useCurrentThread via provider

Update existing toEqual assertions to include the new isBlockingLoad field.

Co-authored-by: Daniel <bdart@users.noreply.github.com>
Reflects the freshly-built frontend library tarball produced during
dependency installation for the test run.

Co-authored-by: Daniel <bdart@users.noreply.github.com>
- Run prettier on useCurrentThread.ts and useCurrentThread.test.ts
- Remove the eslint-disable-line react-hooks/exhaustive-deps comment;
  the rule does not flag the deps array since OUTLOOK_GRAPH_THREAD_UI_BLOCK_DEADLINE_MS
  is a module-level constant and setIsWithinBlockDeadline is a stable
  state setter — both are correctly excluded by the rule

Co-authored-by: Daniel <bdart@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants