Postbox -> TelegramEngine wave 17: ItemListAvatarAndNameInfoItem stateManager: collapse

Applies the wave-11/12/15 collapse pattern to ItemListAvatarAndNameInfoItem.
ItemContext.other payload changes from (accountPeerId:, postbox: Postbox,
network: Network) to (accountPeerId:, stateManager: AccountStateManager).
Internal AvatarNode.setPeer forward rewires through stateManager.postbox /
.network. Module drops import Postbox (source) and //submodules/Postbox:Postbox
(BUILD). Sole external caller — DeviceContactInfoController.swift:413 at the
Share-Extension boundary — migrated atomically.

Handle choice is stateManager: (not engine:) because the sole caller fires
specifically when the context is not a ShareControllerAppAccountContext;
ShareControllerAccountContext protocol exposes stateManager: AccountStateManager
but not engine: TelegramEngine, and no full TelegramEngine is constructible in
the Share Extension (no Account). Per feedback_postbox_refactor_handle.md and
the wave-15 precedent.

Pre-flight public-Postbox-type inventory grep (per CLAUDE.md's Wave-selection
guidance) returned only Postbox itself — no hidden leaks like wave 16's
EngineMessageHistoryThread / PeerStoryStats surprises.

Module fully Postbox-free after this wave. Running tally updated in CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-04-20 21:46:02 +02:00
parent 8215c057cf
commit 9b06909edd
4 changed files with 26 additions and 6 deletions

View file

@ -427,6 +427,27 @@ Commit `a5432e44a8`. Net: 2 files, +17 / 30.
Plan / record: `project_postbox_wave16_plan.md` (updated with outcome).
### Wave 17 outcome (2026-04-20)
Applies the wave-11/12/15 `stateManager: AccountStateManager` collapse pattern to `ItemListAvatarAndNameInfoItem` — another wave-1-era candidate. Module becomes fully Postbox-free (source + BUILD). Clean one-shot execution (no abandonment, no replan).
**`ItemListAvatarAndNameInfoItem.ItemContext` enum case collapsed.** Before: `case other(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network)` + matching destructure at L761 + `AvatarNode.setPeer(…, postbox: postbox, network: network, …)` internal forward. After: `case other(accountPeerId: EnginePeer.Id, stateManager: AccountStateManager)` + `case let .other(accountPeerId, stateManager):` destructure + `AvatarNode.setPeer(…, postbox: stateManager.postbox, network: stateManager.network, …)` forward. The `.accountContext(AccountContext)` sister case is unchanged.
**Share-Extension-boundary handle choice: `stateManager:`.** The sole external `.other(...)` construction site codebase-wide is `DeviceContactInfoController.swift:413`, inside a ternary that fires only when `arguments.context` is not a `ShareControllerAppAccountContext` — i.e., when running inside the Share Extension. `ShareControllerAccountContext` (protocol at `AccountContext/Sources/ShareController.swift:16`) exposes `stateManager: AccountStateManager` but not `engine: TelegramEngine`, and constructing a full `TelegramEngine` is physically unreachable in the Share Extension's `ShareControllerAccountContextExtension` impl (no `Account`). Per `feedback_postbox_refactor_handle.md` and the wave-15 precedent, use `stateManager:` at Share-Extension boundaries.
**Pre-flight inventory was correct.** Running the public-Postbox-type inventory grep returned only `Postbox` itself (the one enum-case payload leak) — no `EngineMessageHistoryThread`-style surprises. Wave 17 validates the post-wave-16 lesson: when planning-time inventory uses the full Postbox public-types allowlist (not just `Postbox`/`Network` tokens), wave-11-shape candidates execute cleanly.
**Single external caller migrated:**
- `PeerInfoUI/Sources/DeviceContactInfoController.swift:413``postbox: arguments.context.stateManager.postbox, network: arguments.context.stateManager.network``stateManager: arguments.context.stateManager`. The enclosing `PeerInfoUI` module still imports Postbox for its own unrelated reasons; that stays.
The 5 other `ItemListAvatarAndNameInfoItem(itemContext:…)` construction sites codebase-wide all use `.accountContext(arguments.context)` and need no change (`ChannelBannedMemberController.swift:321`, `DeviceContactInfoController.swift:415`, `ChannelAdminController.swift:370`, `CreateChannelController.swift:197`, `CreateGroupController.swift:324`).
**Pattern-consistency note (reinforced).** `accountPeerId: EnginePeer.Id` is kept as a separate enum-case payload even though `AccountStateManager` also exposes `accountPeerId`. This matches waves 11/12/15 (`ActionSheetPeerItem`, `ChatListSearchRecentPeersNode`, `SelectablePeerNode` all kept `accountPeerId` explicit alongside `stateManager`). Future wave-11-pattern executions should default to this shape unless a specific reason exists to collapse further.
Net: 3 files changed, +4 / -5 lines (ItemListAvatarAndNameItem.swift: +2 / -3, DeviceContactInfoController.swift: +1 / -1, BUILD: 1). Build verified green for target modules (`ItemListAvatarAndNameInfoItem`, `PeerInfoUI` both compiled and linked successfully); the one unrelated failing target in the full build (`ChatMessageInteractiveMediaNode.swift`) is user-uncommitted work-in-progress that predates this wave.
Plan / record: (plan doc `project_postbox_wave17_plan.md` deleted post-commit per the plan's own post-commit housekeeping instructions).
### Modules currently free of `import Postbox` (running tally)
Consumer modules that no longer import Postbox, across all waves and standalone commits:
@ -446,6 +467,7 @@ Consumer modules that no longer import Postbox, across all waves and standalone
- `ActionSheetPeerItem` (wave 11; revisits wave-1 abandonment)
- `HorizontalPeerItem` (wave 12; applies wave-11 pattern)
- `SelectablePeerNode` (wave 15; applies wave-11 pattern; ShareExtension-boundary stateManager fallback)
- `ItemListAvatarAndNameInfoItem` (wave 17; applies wave-11 pattern; ShareExtension-boundary stateManager fallback)
- `AttachmentTextInputPanelNode` BUILD cleanup (wave 13; source was already clean from wave 6)
- **Wave 14 BUILD-dep sweep: 98 modules' BUILDs cleaned** — same modules as the wave-6 batch; this sweep fixed their leftover `//submodules/Postbox:Postbox` BUILD deps. Candidate list in `/tmp/postbox-dep-candidates.txt` at commit time; derivable by the script in "Wave 14 outcome".

View file

@ -13,7 +13,6 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager",

View file

@ -2,7 +2,6 @@ import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
@ -135,7 +134,7 @@ public enum ItemListAvatarAndNameInfoItemMode {
public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
public enum ItemContext {
case accountContext(AccountContext)
case other(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network)
case other(accountPeerId: EnginePeer.Id, stateManager: AccountStateManager)
var accountContext: AccountContext? {
if case let .accountContext(accountContext) = self {
@ -758,8 +757,8 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
switch item.itemContext {
case let .accountContext(context):
strongSelf.avatarNode.setPeer(context: context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
case let .other(accountPeerId, postbox, network):
strongSelf.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: .default, theme: item.presentationData.theme, peer: peer, authorOfMessage: nil, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
case let .other(accountPeerId, stateManager):
strongSelf.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: stateManager.postbox, network: stateManager.network, contentSettings: .default, theme: item.presentationData.theme, peer: peer, authorOfMessage: nil, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
}
}

View file

@ -410,7 +410,7 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry {
if let context = arguments.context as? ShareControllerAppAccountContext {
itemContext = .accountContext(context.context)
} else {
itemContext = .other(accountPeerId: arguments.context.accountPeerId, postbox: arguments.context.stateManager.postbox, network: arguments.context.stateManager.network)
itemContext = .other(accountPeerId: arguments.context.accountPeerId, stateManager: arguments.context.stateManager)
}
return ItemListAvatarAndNameInfoItem(itemContext: itemContext, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in
arguments.updateEditingName(editingName)