Delay outgoing messages while the peer in the same (peerId, threadId)
chat is live-typing an incoming message, gated via a new per-account
Postbox `.allTypingDrafts` view feeding a synchronous Set membership
check inside PendingMessageManager.
Postbox:
- Add `AllTypingDraftsView` tracking `Set<PeerAndThreadId>` of active
typing drafts; expose via `PostboxViewKey.allTypingDrafts`.
- Disambiguate `PostboxViewKey` hash arms for `.typingDrafts` (constant
prefix 23) and `.contacts` (16 → 24) to avoid runtime collisions
with peerId-only arms and `.combinedReadState`. Hash values are
runtime-only, so this is safe.
PendingMessageManager:
- Add `.waitingForSendGate(groupId:content:)` parking state for single
messages and albums whose upload finished but are gated on a
typing-draft-clearing event; update `groupId` accessor and
`dataForPendingMessageGroup` so partially-parked albums drain on
gate open.
- Add `.waitingForForwardSendGate` parking state for forwards (no
associated values) so drain section (1) routes single non-grouped
messages via `commitSendingSingleMessage` and section (3) routes
forwards via `sendGroupMessagesContent`, avoiding the double-dispatch
that occurred when forwards reused `.waitingForSendGate`.
- Subscribe to `.allTypingDrafts`; wire the gate at single-message,
album, and forward send paths and at drain.
- Cleanup parked forwards on pending-message removal; snapshot
Dictionary keys before iterating to avoid mutation during iteration.
Specs and plan documents are included under `docs/superpowers/`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Promote Postbox's internal MutableMessageHistoryThreadIndexView.Item to a
public top-level type MessageHistoryThreadIndexItem; drop Postbox's empty
EngineMessageHistoryThread namespace class and its nested Item.
MessageHistoryThreadIndexView.items is now [MessageHistoryThreadIndexItem]
directly (no wrap/unwrap step in the init). The two consumer iteration
sites (ChatListNodeLocation.swift, ShareControllerNode.swift) don't need
changes -- they iterate without type annotation and only read fields that
exist on both the old nested Item and the new top-level type.
Collapse TelegramCore's public extension EngineMessageHistoryThread into
a proper class definition:
public final class EngineMessageHistoryThread { class Info { ... } }.
After this, EngineMessageHistoryThread is a pure TelegramCore type --
consumers that only use EngineMessageHistoryThread.Info no longer need
import Postbox for the outer name to resolve.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>