Skip to content
20 changes: 12 additions & 8 deletions src/chat/components/ConversationSession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1514,18 +1514,22 @@ export function ConversationSession({
<div className="queue-controls">
<span role="status">
{pendingSend.busy
? 'Checking send…'
? pendingSend.checking
? 'Checking send…'
: 'Sending…'
: pendingSend.pending
? 'Send not confirmed.'
: 'Send recovery unavailable.'}
</span>
<button
type="button"
disabled={pendingSend.busy}
onClick={() => void pendingSend.check()}
>
Check again
</button>
{(!pendingSend.busy || pendingSend.checking) && (
<button
type="button"
disabled={pendingSend.busy}
onClick={() => void pendingSend.check()}
>
Check again
</button>
)}
</div>
)}
{readOnly ? (
Expand Down
22 changes: 13 additions & 9 deletions src/chat/components/RetryComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -545,19 +545,23 @@ export function RetryComposer({
<div className="retry-draft-recovery">
<span role="status">
{pending.busy
? 'Checking send…'
? pending.checking
? 'Checking send…'
: 'Sending…'
: pending.pending
? 'Send not confirmed.'
: 'Send recovery unavailable.'}
</span>
<button
type="button"
className="quiet-button"
disabled={pending.busy}
onClick={() => void pending.check()}
>
Check again
</button>
{(!pending.busy || pending.checking) && (
<button
type="button"
className="quiet-button"
disabled={pending.busy}
onClick={() => void pending.check()}
>
Check again
</button>
)}
</div>
)}
{draft.draft && (
Expand Down
21 changes: 15 additions & 6 deletions src/chat/components/usePendingSend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function usePendingSend(
pending: null as SendEnvelope | null,
ready: false,
busy: true,
checking: true,
error: '',
})
const [submitted, setSubmitted] = useState<SendEnvelope | null>(null)
Expand Down Expand Up @@ -86,14 +87,20 @@ export function usePendingSend(
return (service.current = { key, store, coordinator })
}
async function perform(
action: 'send' | 'retry' | 'check',
operation: (coordinator: PendingSendCoordinator) => Promise<SendResolution>,
) {
if (!enabled) return
if (working.current) return
working.current = true
const acknowledge = accepted.current
if (mounted.current)
setState((previous) => ({ ...previous, busy: true, error: '' }))
setState((previous) => ({
...previous,
busy: true,
checking: action === 'check',
error: '',
}))
try {
const { coordinator, store } = getService()
const pending = store.read()
Expand All @@ -111,6 +118,7 @@ export function usePendingSend(
pending: nextPending,
ready: true,
busy: false,
checking: false,
error: 'error' in result ? (result.error ?? '') : '',
})
} catch (error) {
Expand All @@ -125,6 +133,7 @@ export function usePendingSend(
pending,
ready: false,
busy: false,
checking: false,
error:
error instanceof Error
? error.message
Expand All @@ -137,10 +146,10 @@ export function usePendingSend(
useEffect(() => {
if (!enabled) return
mounted.current = true
void perform((coordinator) => coordinator.check())
void perform('check', (coordinator) => coordinator.check())
const changed = (event: StorageEvent) => {
if (event.key === key || event.key === null)
void perform((coordinator) => coordinator.check())
void perform('check', (coordinator) => coordinator.check())
}
window.addEventListener('storage', changed)
return () => {
Expand All @@ -153,8 +162,8 @@ export function usePendingSend(
submitted,
forgetSubmitted: () => setSubmitted(null),
submit: (input: NewSend) =>
perform((coordinator) => coordinator.submit(input)),
retry: () => perform((coordinator) => coordinator.retry()),
check: () => perform((coordinator) => coordinator.check()),
perform('send', (coordinator) => coordinator.submit(input)),
retry: () => perform('retry', (coordinator) => coordinator.retry()),
check: () => perform('check', (coordinator) => coordinator.check()),
}
}
Loading