WinterGram/docs/superpowers/plans/2026-04-25-peerinfoscreendata-savedmessagespeer-engine-peer.md
isaac d1aa0db537 Postbox -> TelegramEngine waves 46-93 (squashed)
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>
2026-04-25 20:48:15 +04:00

4.3 KiB
Raw Blame History

Wave 48 — PeerInfoScreenData.savedMessagesPeer Peer? → EnginePeer?

Date: 2026-04-25 Predecessor: Wave 47 (commit d7b7536440) — stored PHN.peer single-file private migration. Shape: Cross-file struct-field migration. Storage class is internal to PeerInfoScreen module; no external consumer references PSD.savedMessagesPeer.

Target

submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift, PeerInfoScreenData.savedMessagesPeer: Peer? at line 388.

Pre-flight inventory

grep -rEn "(\w+\??)\.savedMessagesPeer\b" submodules/ Telegram/ → matches only inside submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/. No external consumer. The same field name appears in unrelated places (TelegramEngineMessages.swift, ChatListUI, etc.) but those are different declarations on different types.

Within PeerInfoScreen module:

Site Code Action
PeerInfoData.swift:388 let savedMessagesPeer: Peer? (struct field decl) Type change → EnginePeer?
PeerInfoData.swift:444 savedMessagesPeer: Peer?, (init param) Type change → EnginePeer?
PeerInfoData.swift:489 self.savedMessagesPeer = savedMessagesPeer (assignment) No change (passthrough)
PeerInfoData.swift:1029 savedMessagesPeer: nil, (init kwarg) No change (nil works for either)
PeerInfoData.swift:1102 savedMessagesPeer: nil, No change
PeerInfoData.swift:13131317 let savedMessagesPeer: Signal<EnginePeer?, NoError> (local) No change — already EnginePeer?
PeerInfoData.swift:1622 savedMessagesPeer: savedMessagesPeer?._asPeer(), Drop bridgesavedMessagesPeer: savedMessagesPeer,
PeerInfoData.swift:1869 savedMessagesPeer: nil, No change
PeerInfoData.swift:2207 savedMessagesPeer: nil, No change
PeerInfoScreen.swift:5399 peer: self.data?.savedMessagesPeer.flatMap(EnginePeer.init) ?? self.data?.peer, Drop bridgepeer: self.data?.savedMessagesPeer ?? self.data?.peer,
PeerInfoScreen.swift:5805 same as :5399 Same drop

Total edits: 5 (3 in PID, 2 in PIS).

EnginePeer / read-site audit

The local signal at PeerInfoData.swift:1313 already produces EnginePeer? from engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(...)). The ?._asPeer() at line 1622 was an artificial demotion. Migrating the field type to EnginePeer? removes both the demotion at the storage site and the flatMap(EnginePeer.init) re-promotions at the read sites — a clean ratchet.

PIS:5399 and :5805 use the field as input to headerNode.update(... peer: ...), whose peer parameter has been EnginePeer? since wave 45. The ?? coalescing operand is self.data?.peer (already EnginePeer?). Result: drop the .flatMap(EnginePeer.init) and the expression compiles.

Edit list

PeerInfoData.swift (3 edits)

  1. Line 388: let savedMessagesPeer: Peer?let savedMessagesPeer: EnginePeer?
  2. Line 444: savedMessagesPeer: Peer?,savedMessagesPeer: EnginePeer?,
  3. Line 1622: savedMessagesPeer: savedMessagesPeer?._asPeer(),savedMessagesPeer: savedMessagesPeer,

PeerInfoScreen.swift (2 edits, identical text)

  1. Line 5399: peer: self.data?.savedMessagesPeer.flatMap(EnginePeer.init) ?? self.data?.peer,peer: self.data?.savedMessagesPeer ?? self.data?.peer,
  2. Line 5805: same

Use replace_all=true for the PIS edit since the matched text appears at both call sites verbatim.

Out of scope

  • PeerInfoScreenData.chatPeer — large blast radius (5 as? TelegramX checks downstream + ClearPeerHistory init parameter), defer.
  • PeerInfoScreenData.linkedDiscussionPeer, linkedMonoforumPeer — both have as? TelegramChannel consumer sites in PeerInfoProfileItems.swift. Defer.
  • PeerInfoScreenMemberItem.enclosingPeer — defer (separate target).

Build & verify

Same Bazel command as wave 47. Expected 1-iteration first-pass-clean (single-pattern bridge removal, no enum-case rewrites, no Peer-only property access).

Commit

Postbox -> TelegramEngine wave 48. Body lists the 5-edit summary and notes 3 internal bridges (1 PID + 2 PIS, identical PIS text appears twice).

Outcome capture

Append a Wave 48 entry to docs/superpowers/postbox-refactor-log.md and update memory file project_postbox_refactor_next_wave.md.