1/**
2 * Work a draft has started but not finished — compressing a pasted image, downloading a pasted
3 * attachment's bytes. Sending while any of it is outstanding snapshots a message whose chip has
4 * nothing behind it, and the late result lands in whatever draft is open by then.
5 *
6 * Counted rather than flagged: two pastes can be in flight at once, and the first to finish must
7 * not clear the second's claim.
8 */
9class PendingDraftWork {
10 private readonly counts = new Map<string, number>();
11
12 begin(draftKey: string): void {
13 this.counts.set(draftKey, (this.counts.get(draftKey) ?? 0) + 1);
14 }
15
16 end(draftKey: string): void {
17 const remaining = (this.counts.get(draftKey) ?? 0) - 1;
18 if (remaining > 0) this.counts.set(draftKey, remaining);
19 else this.counts.delete(draftKey);
20 }
21
22 has(draftKey: string): boolean {
23 return (this.counts.get(draftKey) ?? 0) > 0;
24 }
25}
26
27/**
28 * Module-scoped, so a transfer keeps its claim on a draft across a remount of the composer.
29 * Ownership belongs to the draft, which outlives any one mounted component; a per-instance
30 * counter would come back empty while the download it was tracking was still running.
31 */
32export const pendingDraftWork = new PendingDraftWork();