Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / frontend/src/lib/mailbox.ts |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | frontend/src/lib/mailbox.ts |
| Original SHA-256 | 5eaca4f712ab3aefc77a08b5b2d30b6376e12cf2341170a7f2da2b25aad99e7c |
| Git blob SHA | 6a99d122171c55819dbb48171495b2014de340e3 |
| Reading-copy SHA-256 | 5eaca4f712ab3aefc77a08b5b2d30b6376e12cf2341170a7f2da2b25aad99e7c |
Recovered source transcript
Download complete source text ↓121 lines · 3.8 KB
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { request } from './api'
// THE PERSONAL MAILBOX.
//
// A separate module from settings.ts because the audience is different in a way that matters: organization
// settings are the owner's and are gated on org.settings.write, while a connected mailbox is one recruiter's
// own and is gated on candidate.write. Putting them together would invite a screen that mixes the two, and
// the one thing this surface must never do is let one person configure another person's mailbox.
export interface MailAccount {
imap_host: string
imap_port: number
imap_username: string
mailbox: string
sync_sent: boolean
enabled: boolean
password_set: boolean
verified_at?: string
last_sync_at?: string
last_error?: string
lookback_days: number
}
export interface MailAccountEnvelope {
connected: boolean
account: MailAccount | null
note: string
}
export interface SyncResult {
examined: number
matched: number
logged: number
already_known: number
unmatched_and_discarded: number
note?: string
}
export interface MailAccountInput {
imap_host: string
imap_port: number
imap_username: string
password?: string
mailbox: string
lookback_days: number
enabled?: boolean
}
export const mailboxApi = {
get: () => request<MailAccountEnvelope>('/mail/account'),
save: (v: MailAccountInput) =>
request<{ saved: boolean; note: string }>('/mail/account', { method: 'PUT', body: JSON.stringify(v) }),
disconnect: () =>
request<{ disconnected: boolean; note: string }>('/mail/account', { method: 'DELETE' }),
test: () => request<{ verified: boolean }>('/mail/account/test', { method: 'POST' }),
setEnabled: (enabled: boolean) =>
request<{ enabled: boolean }>('/mail/account/enabled', {
method: 'POST',
body: JSON.stringify({ enabled }),
}),
sync: () => request<SyncResult>('/mail/sync', { method: 'POST' }),
}
const KEY = ['mail', 'account'] as const
export function useMailAccount() {
return useQuery({ queryKey: KEY, queryFn: mailboxApi.get })
}
// Every mutation invalidates rather than patching the cache, for the same reason the org settings do: the
// server adjusts what was sent. Saving a new host clears the verification and switches sync off, and a hand
// written cache update would leave the screen claiming a mailbox is still proven when it is not.
export function useSaveMailAccount() {
const qc = useQueryClient()
return useMutation({
mutationFn: (v: MailAccountInput) => mailboxApi.save(v),
onSuccess: () => void qc.invalidateQueries({ queryKey: KEY }),
})
}
export function useDisconnectMailbox() {
const qc = useQueryClient()
return useMutation({
mutationFn: () => mailboxApi.disconnect(),
onSuccess: () => void qc.invalidateQueries({ queryKey: KEY }),
})
}
export function useTestMailbox() {
const qc = useQueryClient()
return useMutation({
mutationFn: () => mailboxApi.test(),
// Both outcomes change stored state: success stamps the verification, failure records the error and
// switches sync off, so it refetches either way.
onSettled: () => void qc.invalidateQueries({ queryKey: KEY }),
})
}
export function useSetMailboxEnabled() {
const qc = useQueryClient()
return useMutation({
mutationFn: (enabled: boolean) => mailboxApi.setEnabled(enabled),
onSuccess: () => void qc.invalidateQueries({ queryKey: KEY }),
})
}
export function useSyncMailbox() {
const qc = useQueryClient()
return useMutation({
mutationFn: () => mailboxApi.sync(),
onSuccess: () => {
void qc.invalidateQueries({ queryKey: KEY })
// A sync writes activity rows against candidates, so every record's history is stale.
void qc.invalidateQueries({ queryKey: ['recruiting', 'activities'] })
void qc.invalidateQueries({ queryKey: ['candidates'] })
},
})
}