Add Message/EngineMessage.effectiveMedia (= message.media when non-empty, else
richText.instantPage.allMedia()) and route the media-consuming sites through it
so a rich message's instant-page media participates in the same pipelines as
normal message.media: shared-media grids/file-rows, search media grid, gallery
open + item nodes + footer, the peer audio/voice playlist, secret-media preview,
resource-by-id resolution, recent downloads, downloaded-media store, delete-time
resource cleanup, cache-usage stats, the in-chat download manager, and the
context-menu / share actions (Save to Camera Roll, copy image, save audio/music
to files). For normal messages effectiveMedia == message.media, so each swap is
behavior-preserving; rich messages render their own bubble via
ChatMessageRichDataBubbleContentNode (not the text/file bubbles), so those paths
are deliberately untouched, as are the forward path (the attribute travels with
the forward) and the markdown-based rich-edit path. First-media scope for now.
See docs/instantpage-richtext.md for the full architecture + invariants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Original wave 106 pure import-drop sweep abandoned: 576 candidate files
all genuinely reference Postbox-tier tokens; wave 93's pattern exhausted
at file granularity (no single-file orphans remain).
Pivot: extend engine.resources.data(resource:) facade with
`incremental: Bool = false` parameter (wires to ResourceDataRequestOption).
Drain the 1 live consumer site (ChatInterfaceStateContextMenus:1327)
plus consumer-side `data.complete` -> `data.isComplete` rename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two TelegramEngine.Resources facades and migrates 4 Shape-A sites across 3 consumer files.
Also imports RangeSet in TelegramEngineResources.swift to disambiguate the RangeSet type from
Swift stdlib's iOS-18-only type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two TelegramEngine.Resources.copyResourceData overloads and migrates 4 Shape-A sites
across 3 consumer files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two TelegramEngine.Resources.moveResourceData overloads and migrates 6 Shape-A sites
across 4 consumer files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds TelegramEngine.Resources.cancelInteractiveResourceFetch(id:) and migrates 5 of 7 Shape-A
sites. Two sites in ChatMessageInteractiveMediaNode.swift skipped to avoid mixing with
pre-existing uncommitted WIP in that file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same shape as wave 21. Adds TelegramEngine.Resources.storeResourceData(id:, data:, synchronous:)
and sweeps 46 context.account.postbox.mediaBox.storeResourceData sites across 17 files.
The range-store overload and accountManager.mediaBox sites are explicitly out of scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Combined wave-19+wave-20 shape. Adds TelegramEngine.Resources.completedResourcePath(id:, pathExtension:)
facade and sweeps 29 consumer sites across 14 files in one atomic commit.
Shape A/B migrated. Shape C (5 sites with raw account: Account) and Shape D
(3 sites with local postbox: Postbox field) intentionally skipped — need
module-scoped init-signature rework rather than per-site sweep.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Additive-only. Adds TelegramEngine.Resources.shortLivedResourceCachePathPrefix(id: EngineMediaResource.Id) -> String as a thin forwarder over MediaBox.shortLivedResourceCachePathPrefix.
Unblocks consumer migration across 25+ call sites in ~15 modules; consumers migrate in follow-up waves.
Closes the seven remaining raw-Postbox leaks in TelegramEngine public
facades surfaced by a post-wave-6 scouting pass (all non-permanently-
blocked candidates).
Facades migrated in place (6 rewrites + 1 deletion; all _internal_*
implementations unchanged per the "internal Postbox-facing stays raw"
rule):
Messages:
downloadMessage Signal<Message?> -> Signal<EngineMessage?>
topPeerActiveLiveLocationMessages Signal<(Peer?, [Message])> -> Signal<(EnginePeer?, [EngineMessage])>
getSynchronizeAutosaveItemOperations deleted (dead facade; sole caller uses _internal_ directly)
Peers:
updatedRemotePeer Signal<Peer> -> Signal<EnginePeer>
(PeerReference param kept; no EnginePeer.Reference alias today)
Resources:
renderStorageUsageStatsMessages [EngineMessage.Id: Message] -> [EngineMessage.Id: EngineMessage]
clearStorage(peerId: ...) [Message] -> [EngineMessage]
clearStorage(peerIds: ...) [Message] -> [EngineMessage]
clearStorage(messages:) [Message] -> [EngineMessage]
(no external callers; migrated for overload-set consistency)
Consumer call-site updates (5 files):
- ChatListSearchListPaneNode drop redundant .flatMap(EngineMessage.init) wrap
- LocationViewControllerNode drop redundant .map(EngineMessage.init) wrap
- LiveLocationSummaryManager drop redundant EnginePeer(...) / EngineMessage(...) ctors
- StorageUsageScreen bridge [Message] <-> [EngineMessage] at the 4 facade-call points
(internal [MessageId: Message] / [Message] storage kept;
full-consumer-module migration is out of scope)
Discovery: grep of TelegramEngine/*/TelegramEngine*.swift public signatures
for `: Postbox|: Account|: MediaBox|: MediaResource|: Peer\b|: Message\b|
-> Signal<.*(Peer|Message)` turned up these seven candidates and no others.
After this wave, the full TelegramEngine.* facade surface is engine-typed
modulo the four permanently-blocked TelegramMediaResource-conforming
classes recorded in CLAUDE.md (ICloudFileResource, InstantPageExternal-
MediaResource, VideoLibraryMediaResource, YoutubeEmbedStoryboardMedia-
Resource).
No modules became Postbox-free in this wave. Plan: docs/superpowers/
plans/2026-04-20-postbox-to-telegramengine-wave-7.md.
Full project build verified green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds three thin forwarding methods on TelegramEngine.Resources
(fetch, status, data) over MediaBox, then migrates SaveToCameraRoll's
three public functions to use them, drops import Postbox from the
module (source + Bazel dep), and updates all 23 call sites across 14
caller files atomically.
Bundled: spec + fix + plan + C1 facades + C2 SaveToCameraRoll rewrite
+ BUILD dep drop + CLAUDE.md outcome.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>