diff --git a/CLAUDE.md b/CLAUDE.md index 30105b2237..6cd8e8405b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -358,6 +358,31 @@ Net (wave 14 alone): 98 files changed, 0 insertions / 98 deletions. Plan / record: (no plan doc this wave — mechanical sweep). +### Wave 15 outcome (2026-04-20) + +Applies the wave-11/12 `stateManager: AccountStateManager` collapse pattern to `SelectablePeerNode` — another wave-1-era candidate listed in the post-wave-14 shortlist. Module becomes fully Postbox-free (source + BUILD). + +**`SelectablePeerNode` fully Postbox-free.** Two public setup methods migrated: +- `setup(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, …)` → `setup(accountPeerId:, stateManager: AccountStateManager, …)`. +- `setupStoryRepost(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, …)` → `setupStoryRepost(accountPeerId:, stateManager: AccountStateManager, …)`. + +Internal forwards rewired: `AvatarNode.setPeer(…, postbox: stateManager.postbox, network: stateManager.network, …)` and `EmojiStatusComponent(postbox: stateManager.postbox, …)`. Neither site names `Postbox` in the consumer — Swift infers through transitive module visibility. + +**Namespaces.Peer.SecretChat fixup (×3).** Replaced `peer.peerId.namespace == Namespaces.Peer.SecretChat` checks with `peer.peerId.isSecretChat` at three sites, matching the wave-13 pattern (`PeerId.isSecretChat` at `TelegramCore/Sources/Utils/PeerUtils.swift:611`). The third site (`updateSelection` in the not-selected branch) additionally needed an `?? false` fallback — previous expression was `self.peer?.peerId.namespace == Namespaces.Peer.SecretChat` (optional-equals-non-optional produces `Bool`), new expression is `(self.peer?.peerId.isSecretChat ?? false)`. + +**Share-Extension boundary — `stateManager:` over `engine:`.** `SelectablePeerNode` is used by `ShareControllerPeerGridItem`, whose context is `ShareControllerAccountContext`. That protocol exposes `stateManager: AccountStateManager` and `engineData: TelegramEngine.EngineData`, but **no `engine: TelegramEngine`** — and the Share Extension's `ShareControllerAccountContextExtension` concrete impl has no `Account`, so constructing a full `TelegramEngine` (`init(account: Account)`) is physically unreachable there. This is the documented "rare but genuine" fallback to `stateManager:` from the user-preference memory (`feedback_postbox_refactor_handle.md`) — prefer `engine:` except when crossing the Share-Extension boundary. + +**Three external call sites migrated:** +- `HorizontalPeerItem/Sources/HorizontalPeerItem.swift:227` (wave 12's `stateManager:` field now forwards directly): `postbox: item.stateManager.postbox, network: item.stateManager.network` → `stateManager: item.stateManager`. +- `ShareController/Sources/ShareControllerPeerGridItem.swift:237` (setup): `postbox: context.stateManager.postbox, network: context.stateManager.network` → `stateManager: context.stateManager`. +- `ShareController/Sources/ShareControllerPeerGridItem.swift:273` (setupStoryRepost): same. + +**Convenience init unchanged.** `setup(context: AccountContext, …)` now delegates with `stateManager: context.account.stateManager`; signature unchanged — `JoinLinkPreviewPeerContentNode.swift:147` (the one caller using the convenience init) needed no edit. + +Net: 4 files changed, +12 / -17 lines. Build verified green (193 actions, 131s — Telegram.ipa target built successfully). + +Plan / record: (no plan doc this wave — pattern-application, low-complexity). + ### Modules currently free of `import Postbox` (running tally) Consumer modules that no longer import Postbox, across all waves and standalone commits: @@ -376,6 +401,7 @@ Consumer modules that no longer import Postbox, across all waves and standalone - `StorageUsageScreen` (waves 8–10) - `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) - `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". diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index 0bb350af80..2e095f765e 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -224,7 +224,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode { } else { strongSelf.peerNode.compact = false } - strongSelf.peerNode.setup(accountPeerId: item.accountPeerId, postbox: item.stateManager.postbox, network: item.stateManager.network, energyUsageSettings: item.energyUsageSettings, contentSettings: item.contentSettings, animationCache: item.animationCache, animationRenderer: item.animationRenderer, resolveInlineStickers: item.resolveInlineStickers, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), requiresPremiumForMessaging: false, numberOfLines: 1, synchronousLoad: synchronousLoads) + strongSelf.peerNode.setup(accountPeerId: item.accountPeerId, stateManager: item.stateManager, energyUsageSettings: item.energyUsageSettings, contentSettings: item.contentSettings, animationCache: item.animationCache, animationRenderer: item.animationRenderer, resolveInlineStickers: item.resolveInlineStickers, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), requiresPremiumForMessaging: false, numberOfLines: 1, synchronousLoad: synchronousLoads) strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size) strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false) diff --git a/submodules/SelectablePeerNode/BUILD b/submodules/SelectablePeerNode/BUILD index b13c4278f4..add7a0f4d8 100644 --- a/submodules/SelectablePeerNode/BUILD +++ b/submodules/SelectablePeerNode/BUILD @@ -12,7 +12,6 @@ swift_library( deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/TelegramCore:TelegramCore", - "//submodules/Postbox:Postbox", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/TelegramPresentationData:TelegramPresentationData", diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index f4ba6fd9fc..13eee759a7 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore -import Postbox import SwiftSignalKit import TelegramPresentationData import AvatarNode @@ -108,7 +107,7 @@ public final class SelectablePeerNode: ASDisplayNode { didSet { if !self.theme.isEqual(to: oldValue) { if let peer = self.peer, let mainPeer = peer.chatMainPeer { - self.textNode.attributedText = NSAttributedString(string: mainPeer.debugDisplayTitle, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : (peer.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center) + self.textNode.attributedText = NSAttributedString(string: mainPeer.debugDisplayTitle, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : (peer.peerId.isSecretChat ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center) } } } @@ -159,8 +158,7 @@ public final class SelectablePeerNode: ASDisplayNode { public func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, requiresPremiumForMessaging: Bool, requiresStars: Int64? = nil, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { self.setup( accountPeerId: context.account.peerId, - postbox: context.account.postbox, - network: context.account.network, + stateManager: context.account.stateManager, energyUsageSettings: context.sharedContext.energyUsageSettings, contentSettings: context.currentContentSettings.with { $0 }, animationCache: context.animationCache, @@ -182,7 +180,7 @@ public final class SelectablePeerNode: ASDisplayNode { ) } - public func setupStoryRepost(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool, storyMode: StoryMode) { + public func setupStoryRepost(accountPeerId: EnginePeer.Id, stateManager: AccountStateManager, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool, storyMode: StoryMode) { self.peer = nil let title: String @@ -202,14 +200,14 @@ public final class SelectablePeerNode: ASDisplayNode { self.textNode.maximumNumberOfLines = 2 self.textNode.attributedText = NSAttributedString(string: title, font: textFont, textColor: self.theme.textColor, paragraphAlignment: .center) - self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: ContentSettings.default, theme: theme, peer: nil, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: .round, synchronousLoad: synchronousLoad) + self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: stateManager.postbox, network: stateManager.network, contentSettings: ContentSettings.default, theme: theme, peer: nil, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: .round, synchronousLoad: synchronousLoad) if case .repostIcon = overrideImage { self.avatarNode.playRepostAnimation() } } - public func setup(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, energyUsageSettings: EnergyUsageSettings, contentSettings: ContentSettings, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, requiresPremiumForMessaging: Bool, requiresStars: Int64? = nil, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { + public func setup(accountPeerId: EnginePeer.Id, stateManager: AccountStateManager, energyUsageSettings: EnergyUsageSettings, contentSettings: ContentSettings, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, requiresPremiumForMessaging: Bool, requiresStars: Int64? = nil, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { let isFirstTime = self.peer == nil self.peer = peer guard let mainPeer = peer.chatOrMonoforumMainPeer else { @@ -222,7 +220,7 @@ public final class SelectablePeerNode: ASDisplayNode { if requiresPremiumForMessaging { defaultColor = self.theme.textColor.withMultipliedAlpha(0.4) } else { - defaultColor = peer.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor + defaultColor = peer.peerId.isSecretChat ? self.theme.secretTextColor : self.theme.textColor } var isForum = false @@ -256,7 +254,7 @@ public final class SelectablePeerNode: ASDisplayNode { } else { clipStyle = .round } - self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: contentSettings, theme: theme, peer: mainPeer, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad) + self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: stateManager.postbox, network: stateManager.network, contentSettings: contentSettings, theme: theme, peer: mainPeer, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad) if let requiresStars { let avatarBadgeOutline: UIImageView @@ -373,7 +371,7 @@ public final class SelectablePeerNode: ASDisplayNode { let iconSize = self.iconView.update( transition: .easeInOut(duration: 0.2), component: AnyComponent(EmojiStatusComponent( - postbox: postbox, + postbox: stateManager.postbox, energyUsageSettings: energyUsageSettings, resolveInlineStickers: resolveInlineStickers, animationCache: animationCache, @@ -404,7 +402,7 @@ public final class SelectablePeerNode: ASDisplayNode { self.currentSelected = selected if let attributedText = self.textNode.attributedText { - self.textNode.attributedText = NSAttributedString(string: attributedText.string, font: textFont, textColor: selected ? self.theme.selectedTextColor : (self.peer?.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center) + self.textNode.attributedText = NSAttributedString(string: attributedText.string, font: textFont, textColor: selected ? self.theme.selectedTextColor : ((self.peer?.peerId.isSecretChat ?? false) ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center) } var isForum = false diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index f9ee2a4670..1389f89037 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -236,8 +236,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { let resolveInlineStickers = context.resolveInlineStickers self.peerNode.setup( accountPeerId: context.accountPeerId, - postbox: context.stateManager.postbox, - network: context.stateManager.network, + stateManager: context.stateManager, energyUsageSettings: environment.energyUsageSettings, contentSettings: context.contentSettings, animationCache: context.animationCache, @@ -272,8 +271,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { } self.peerNode.setupStoryRepost( accountPeerId: context.accountPeerId, - postbox: context.stateManager.postbox, - network: context.stateManager.network, + stateManager: context.stateManager, theme: theme, strings: strings, synchronousLoad: synchronousLoad,