mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
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>
49 lines
1.3 KiB
Swift
49 lines
1.3 KiB
Swift
import Foundation
|
|
|
|
final class MutableAllTypingDraftsView: MutablePostboxView {
|
|
fileprivate var keys: Set<PeerAndThreadId>
|
|
|
|
init(postbox: PostboxImpl) {
|
|
self.keys = Set(postbox.currentTypingDrafts.keys)
|
|
}
|
|
|
|
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
|
|
if transaction.updatedTypingDrafts.isEmpty {
|
|
return false
|
|
}
|
|
var updated = false
|
|
for (key, update) in transaction.updatedTypingDrafts {
|
|
if update.value != nil {
|
|
if self.keys.insert(key).inserted {
|
|
updated = true
|
|
}
|
|
} else {
|
|
if self.keys.remove(key) != nil {
|
|
updated = true
|
|
}
|
|
}
|
|
}
|
|
return updated
|
|
}
|
|
|
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
|
let new = Set(postbox.currentTypingDrafts.keys)
|
|
if new == self.keys {
|
|
return false
|
|
}
|
|
self.keys = new
|
|
return true
|
|
}
|
|
|
|
func immutableView() -> PostboxView {
|
|
return AllTypingDraftsView(self)
|
|
}
|
|
}
|
|
|
|
public final class AllTypingDraftsView: PostboxView {
|
|
public let keys: Set<PeerAndThreadId>
|
|
|
|
init(_ view: MutableAllTypingDraftsView) {
|
|
self.keys = view.keys
|
|
}
|
|
}
|