Squash of 63 commits spanning waves 46-93 (plus interspersed docs commits) of the gradual Postbox->TelegramEngine consumer-side migration. Scope: 139 files changed, 2123 insertions(+), 452 deletions(-). ## Themes by wave-block **Waves 46-58 — Peer field migrations + facade additions** Foundational EnginePeer convenience init additions (PeerReference, RenderedPeer, SelectivePrivacyPeer). Multiple `peer: Peer` field migrations across PeerInfo, ChatList, and SettingsUI components. **Waves 59-73 — peer field cascade + EnginePeer wrap drops** Series of single- to two-file peer-field migrations; consumer-side wrap removal (`EnginePeer(peer)` -> direct EnginePeer use); `as? TelegramUser` cast conversion to `case let .user(...)` enum match. Wave 64: RenderedPeer convenience init. Wave 68: SelectivePrivacyPeer convenience init. **Waves 74-83 — controller-Node bridge cleanup + small migrations** Wave-71 shadow-pattern cleanup at controller->Node bridges. Migrations of ChatRecentActionsController.peer (74), PeerInfoMember (75), MentionChatInputPanelItem (76), PassportUI SecureIdAuthController (77), AccountWithInfo + ShareController (78), peerInputActivitiesPromise (79), InactiveChannel (80), BlockedPeers (81), openHashtag resolveSignal (82), NotificationExceptionsList (83). **Waves 84-90 — TelegramEngine.Resources facade migrations** Per-method Shape-A/B sweeps converting `<ctx>.account.postbox.mediaBox.X(...)` to `<ctx>.engine.resources.X(...)`. Wave 90 was a single-commit big sweep: 40 fetchedMediaResource sites in 25 files migrated to engine.resources.fetch facade in one atomic pass with first-pass-clean build. Methods covered: storeResourceData, completedResourcePath, cancelInteractiveResourceFetch, resourceRangesStatus, resourceStatus, fetch (fetchedMediaResource). **Waves 91-92 — additional type migrations** Wave 91: ItemListWebsiteItem.peer + RecentSessionsController enum-case payload + openWebSession callback Peer? -> EnginePeer?. Wave 92: ChatListController StateHolder.EntryContext status type MediaResourceStatus -> EngineMediaResource.FetchStatus. **Wave 93 — speculative `import Postbox` drop sweep** Drop import from 7 wave-touched files where it became unused; restore in 5 files where bare PeerId/Message/MediaId/StoryId references escaped the pre-flight regex. Includes one MediaId(...) -> EngineMedia.Id(...) swap in InAppPurchaseManager to unlock its import drop. ## Build state Final state at squash: clean Telegram/Telegram build at debug_sim_arm64. ## Persistent-state notes - Pre-existing WIP unchanged across the squashed range: - build-system/bazel-rules/sourcekit-bazel-bsp submodule marker - Untracked: build-system/tulsi/, submodules/TgVoip/, third-party/libx264/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.9 KiB
Wave 49 — PeerInfoScreenData.linkedDiscussionPeer + .linkedMonoforumPeer Peer? → EnginePeer? (bundle)
Date: 2026-04-25
Predecessor: Wave 48 (commit 1e4c2eea33) — savedMessagesPeer single-field migration.
Shape: Cross-file bundled struct-field migration (2 sibling fields, 2 files). Both fields are module-internal; no external consumer references them on PeerInfoScreenData. Bundled because both fields:
- Share a sibling declaration site in
PeerInfoData.swift. - Have parallel local-source patterns (raw
Peer?frompeerView.peers[id]dict lookup; not an engine signal as in wave 48). - Are both consumed in
PeerInfoProfileItems.swiftonly. - Migrating one without the other adds friction at the source-construction sites where they're computed together.
Pre-flight inventory
grep -rEn "(\w+\??)\.linkedDiscussionPeer\b|(\w+\??)\.linkedMonoforumPeer\b" submodules/ Telegram/:
submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift:600,651— references are on a localviewobject (different type, NOT PeerInfoScreenData). Out of scope.- All
data.linkedDiscussionPeer/data.linkedMonoforumPeeraccesses live in PIPI within the PeerInfoScreen module.
Within scope:
PeerInfoData.swift (storage class + 2 init sites that compute the locals)
| Site | Code | Action |
|---|---|---|
| :396 | let linkedDiscussionPeer: Peer? (field decl) |
Type → EnginePeer? |
| :397 | let linkedMonoforumPeer: Peer? (field decl) |
Type → EnginePeer? |
| :453 | linkedDiscussionPeer: Peer?, (init param) |
Type → EnginePeer? |
| :454 | linkedMonoforumPeer: Peer?, (init param) |
Type → EnginePeer? |
| :498 | self.linkedDiscussionPeer = linkedDiscussionPeer |
No change |
| :499 | self.linkedMonoforumPeer = linkedMonoforumPeer |
No change |
| :1038, :1111, :1631 | linkedDiscussionPeer: nil, (init kwargs) |
No change |
| :1039, :1112, :1632 | linkedMonoforumPeer: nil, (init kwargs) |
No change |
| :1836 | var discussionPeer: Peer? (local) |
Type → EnginePeer? |
| :1838 | discussionPeer = peer (where peer = peerView.peers[linkedDiscussionPeerId], raw Peer) |
Wrap → discussionPeer = EnginePeer(peer) |
| :1841 | var monoforumPeer: Peer? (local) |
Type → EnginePeer? |
| :1843 | monoforumPeer = peerView.peers[linkedMonoforumId] (dict lookup, Peer?) |
Wrap → monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init) |
| :2131 | var discussionPeer: Peer? (local, parallel to :1836) |
Type → EnginePeer? |
| :2133 | discussionPeer = peer (parallel to :1838) |
Wrap → discussionPeer = EnginePeer(peer) |
| :2136 | var monoforumPeer: Peer? (local, parallel to :1841) |
Type → EnginePeer? |
| :2138 | monoforumPeer = peerView.peers[linkedMonoforumId] (parallel to :1843) |
Wrap with .flatMap(EnginePeer.init) |
| :1878, :1879, :2216, :2217 | init kwargs linkedDiscussionPeer: discussionPeer, / linkedMonoforumPeer: monoforumPeer, |
No change (locals migrate; pass through) |
That's 12 edits in PID. Note the var declarations and assignments at :1836–:1843 and :2131–:2138 are parallel pairs (verified by grep). Use replace_all=true for the duplicate snippets.
PeerInfoProfileItems.swift (3 edits)
| Site | Code | Action |
|---|---|---|
| :1098 | if let peer = data.linkedDiscussionPeer { ... } |
No change (binding works on EnginePeer?) |
| :1099 | if let addressName = peer.addressName, !addressName.isEmpty { |
No change — EnginePeer.addressName forwarded (verified at submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift:461) |
| :1102 | discussionGroupTitle = EnginePeer(peer).displayTitle(strings: ..., displayOrder: ...) |
Drop wrap → peer.displayTitle(...) |
| :1197 | if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel { |
Pattern rewrite → if case let .channel(monoforumPeer) = data.linkedMonoforumPeer { |
| :1198 | monoforumPeer.sendPaidMessageStars |
No change — sendPaidMessageStars is a TelegramChannel property (SyncCore_TelegramChannel.swift:215); case .channel binds to TelegramChannel |
| :1404 | if let linkedDiscussionPeer = data.linkedDiscussionPeer { |
No change (binding works) |
| :1406 | if let addressName = linkedDiscussionPeer.addressName, !addressName.isEmpty { |
No change (forwarded) |
| :1409 | peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(...) |
Drop wrap → linkedDiscussionPeer.displayTitle(...) |
3 edits in PIPI.
EnginePeer property forwarding audit
EnginePeer.addressName— forwarded atPeer.swift:461. ✓EnginePeer.displayTitle(strings:displayOrder:)— defined asEnginePeerinstance method (used elsewhere viaEnginePeer(...).displayTitle(...)pattern; once we have anEnginePeer, it's directly callable). ✓case .channelbinding payload isTelegramChannel. ✓TelegramChannel.sendPaidMessageStars— exists (SyncCore_TelegramChannel.swift:215). ✓
Net bridge count
- ADDs (4): boundary lifts at PID:1838 (
EnginePeer(peer)), PID:1843 (.flatMap(EnginePeer.init)), PID:2133, PID:2138. These lift the Postbox-typedpeerView.peers[...]value to the engine type at the boundary — the correct semantic position for a Postbox→Engine refactor (mirrors wave 42 wherepeer.flatMap(EnginePeer.init)lift was added at PID:1620). - DROPs (2): PIPI:1102 and :1409 lose
EnginePeer(...)wraps arounddisplayTitlecalls. - Net text bridges: +2. But: the ADDs are correct boundary lifts; the field-typed-as-
EnginePeer?is the canonical state. The 2 displayTitle DROPs are the actual ratchet value. - Plus: 1 cleaner pattern (PIPI:1197
as?cast →case let .channel), no text saving but better Swift idiom.
Edit list
PeerInfoData.swift (12 edits, but Edit text uses replace_all=true to bundle parallel pairs)
- Line 396:
let linkedDiscussionPeer: Peer?→let linkedDiscussionPeer: EnginePeer? - Line 397:
let linkedMonoforumPeer: Peer?→let linkedMonoforumPeer: EnginePeer? - Line 453:
linkedDiscussionPeer: Peer?,→linkedDiscussionPeer: EnginePeer?, - Line 454:
linkedMonoforumPeer: Peer?,→linkedMonoforumPeer: EnginePeer?, - Lines 1836 + 2131 (
replace_all=trueovervar discussionPeer: Peer?): →var discussionPeer: EnginePeer? - Lines 1838 + 2133 (
replace_all=trueoverdiscussionPeer = peer): →discussionPeer = EnginePeer(peer) - Lines 1841 + 2136 (
replace_all=trueovervar monoforumPeer: Peer?): →var monoforumPeer: EnginePeer? - Lines 1843 + 2138 (
replace_all=trueovermonoforumPeer = peerView.peers[linkedMonoforumId]): →monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init)
PeerInfoProfileItems.swift (3 edits)
- Line 1102:
discussionGroupTitle = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)→discussionGroupTitle = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - Line 1197:
if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel {→if case let .channel(monoforumPeer) = data.linkedMonoforumPeer { - Line 1409:
peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)→peerTitle = linkedDiscussionPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
Out of scope
PeerInfoScreenData.chatPeer— large blast radius. Defer.PeerInfoScreenMemberItem.enclosingPeer. Defer.
Build & verify
Standard Bazel command. Expected 1 iteration if forwarding audit holds; 2 if a displayTitle overload-resolution surprise surfaces.
Commit
Postbox -> TelegramEngine wave 49. Body: bundle + edits summary + ADD/DROP accounting.
Outcome capture
Append Wave 49 entry to docs/superpowers/postbox-refactor-log.md; update memory file.