Skip to content

feat: shared mailbox autocomplete picker with access verification (v1.5.3)#31

Open
LouisSkyline wants to merge 5 commits into
mainfrom
feature/better-inbox-selection
Open

feat: shared mailbox autocomplete picker with access verification (v1.5.3)#31
LouisSkyline wants to merge 5 commits into
mainfrom
feature/better-inbox-selection

Conversation

@LouisSkyline

Copy link
Copy Markdown
Member

Added Value & Functional Changes

Replace the plain free-text mailbox input in the Inbox tab with a smart autocomplete picker that only shows shared mailboxes the signed-in user actually has access to.

  • Replace bare <input>\ in \InboxTab\ with \AutocompletePicker\ for mailbox selection
  • Add \VITE_SHARED_MAILBOXES\ env var support — admin-configured mailbox addresses shown as instant suggestions on field focus
  • Add \localStorage\ recents — previously used mailbox addresses surfaced on next open
  • Add \searchPeopleMailboxes()\ to \graph.js\ — uses \GET /me/people\ (Microsoft People API, \People.Read\ scope, no admin consent) for fuzzy name/email search
  • Add \ ilterAccessibleMailboxes()\ to \graph.js\ — probes /mailFolders/inbox/messages\ via Graph Batch API (\POST /\) to verify Exchange FullAccess before showing any mailbox as a suggestion; results cached in \sessionStorage\ (15 min TTL with candidate fingerprint)
  • Add \invalidateMailboxCache()\ — removes a mailbox from the session cache when it returns an access error during inbox load
  • All typed search results are filtered against the verified accessible set — inaccessible mailboxes are never suggested, neither on focus nor while typing
  • Extend \AutocompletePicker\ with three backward-compatible optional props: \initialSuggestions, \onQueryChange, \onBlur\
  • Add \People.Read\ scope to \loginRequest\ and \graphRequest\ in \�uthConfig.js\
  • Bump version to 1.5.3

Test Procedure

  • Unit tests
  • Component tests
  • API Tests
  1. Add one or more shared mailbox addresses to \VITE_SHARED_MAILBOXES\ (or leave empty to test localStorage recents only)
  2. Open the Inbox tab and click the Mailbox field — only mailboxes you have Exchange FullAccess to should appear in the dropdown
  3. Type a partial name or email — suggestions should be filtered to accessible mailboxes only
  4. Add a mailbox address you do not have access to in \VITE_SHARED_MAILBOXES\ — it should not appear in the dropdown after the probe completes
  5. Type any address manually and press Enter — free-text entry still works regardless of the probe
  6. Select a mailbox and verify the inbox loads correctly; confirm that addresses used appear in recents on the next session

Backlog management


Definition of Done

  • Code is releasable
  • Code is tested and tests have been written
  • Documentation is added/updated where applicable
  • Release note is made if applicable

LouisSkyline and others added 5 commits June 26, 2026 16:31
… recents

- Replace plain text input in InboxTab with AutocompletePicker
- Add searchPeopleMailboxes() using /me/people Graph endpoint (People.Read scope)
- Extend AutocompletePicker with initialSuggestions, onQueryChange, onBlur props
- Show VITE_SHARED_MAILBOXES env var addresses + localStorage recents on focus
- Free-text entry still supported (commit on Enter/blur)
- Save used addresses to localStorage for future suggestions
- Bump version to 1.5.3

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Filter initial mailbox suggestions (env var list + localStorage recents)
to only show mailboxes the signed-in user has Exchange FullAccess to.

Uses Graph Batch API (POST /\) to probe up to 20 mailboxes in a
single HTTP request: 200 = accessible, 403 = no access. Results are
cached in sessionStorage for 15 minutes to avoid repeated probing.

Falls back to showing all candidates unfiltered on any error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cache on inbox error

- Initialize accessibleSuggestions to [] so no mailboxes are shown
  before the probe resolves (fixes race condition where all candidates
  appeared in the dropdown before the 200/403 check finished)
- Add invalidateMailboxCache() to remove a mailbox from the
  sessionStorage cache when it returns an access error during inbox load
- Call invalidateMailboxCache on inbox load error so stale cached
  permissions don't persist for the remainder of the session

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Change probe URL from /mailFolders/inbox to
  /mailFolders/inbox/messages?top=1&select=id so the probe tests
  the exact same endpoint as the real inbox loader (fixes mismatch
  where folder GET returned 200 but messages listing returned 404)
- Include a fingerprint of the candidate email list in the cache entry
  so adding/removing mailboxes from VITE_SHARED_MAILBOXES or localStorage
  immediately busts the stale sessionStorage cache
- Change error fallback from returning all candidates to returning []
  (fail safe: better to show nothing than inaccessible mailboxes)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously the People API search returned any matching contact,
including mailboxes the user doesn't have access to.

- Replace direct searchPeopleMailboxes searchFn with searchMailboxes
  wrapper that filters all results against the probed accessibleSuggestions
  set — inaccessible mailboxes are never shown as suggestions
- Local accessible matches are returned immediately (no API latency)
  then merged with People API results that also pass the access check
- Falls back to local matches only if People API call fails

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the Inbox tab’s shared mailbox selection by replacing the free-text mailbox input with an access-verified autocomplete experience powered by Microsoft Graph, while also updating auth scopes and versioning for release.

Changes:

  • Replaced the Inbox mailbox <input> with AutocompletePicker, adding env-configured suggestions and localStorage “recents”.
  • Added Graph helpers to (1) search candidates via People API and (2) verify mailbox accessibility via Graph Batch probing with sessionStorage caching.
  • Added People.Read scope and bumped app/package versions to 1.5.3.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/components/InboxTab.jsx Swaps mailbox input to AutocompletePicker, adds initial suggestions/recents, and integrates accessibility filtering + cache invalidation.
src/components/AutocompletePicker.jsx Adds initialSuggestions, onQueryChange, and onBlur to support mailbox free-text commit and focus suggestions.
src/authConfig.js Adds People.Read to Graph scopes for People API search.
src/api/graph.js Implements People search + mailbox access probing/caching + cache invalidation helpers.
package.json Bumps version to 1.5.3.
DynamicsActivitiesPackage/DynamicsActivitiesPackage/DynamicsActivitiesPackage.csproj Bumps package version/comment for deployment (1.5.3).
.env.example Documents VITE_SHARED_MAILBOXES configuration.

setOpen(true)
}
}}
onKeyDown={handleKeyDown}
Comment on lines +874 to +878
const envEmails = new Set(fromEnv.map((m) => m.email))
const recents = JSON.parse(localStorage.getItem('inbox_recent_mailboxes') || '[]')
.filter((e) => !envEmails.has(e))
.map((e) => ({ email: e, label: e }))
return [...fromEnv, ...recents].slice(0, 8)
Comment on lines +911 to +914
if (!address) return
const recents = JSON.parse(localStorage.getItem('inbox_recent_mailboxes') || '[]')
const updated = [address, ...recents.filter((a) => a !== address)].slice(0, 8)
localStorage.setItem('inbox_recent_mailboxes', JSON.stringify(updated))
Comment on lines +811 to +814
.catch((e) => {
if (mailbox) invalidateMailboxCache(mailbox)
setError(e.message)
})
Comment thread src/api/graph.js
Comment on lines +186 to +190
const url = `${GRAPH}/me/people?$search=${q}&$select=displayName,scoredEmailAddresses&$top=10`
const res = await fetch(url, {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
})
if (!res.ok) return []
Comment thread src/api/graph.js
Comment on lines +277 to +280
const cached = JSON.parse(sessionStorage.getItem(MAILBOX_CACHE_KEY) || 'null')
if (!cached) return
const updated = cached.emails.filter((e) => e !== email.toLowerCase())
sessionStorage.setItem(MAILBOX_CACHE_KEY, JSON.stringify({ emails: updated, ts: cached.ts }))
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