diff --git a/CLAUDE.md b/CLAUDE.md index b9ea56518c..53415516de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -176,6 +176,40 @@ Deviation from plan: the plan capped at 3 iterations; execution needed 18 becaus Plan: `docs/superpowers/plans/2026-04-19-postbox-to-telegramengine-wave-6.md` +### Wave 7 outcome (2026-04-20) + +Closed out the seven remaining raw-Postbox leaks in `TelegramEngine.*` public facades surfaced by a post-wave-6 scouting pass. Single atomic commit, one full build, zero abandonment. + +Seven `TelegramEngine` facades migrated in place (all `_internal_*` implementations kept raw per the standing rule): + +**Messages (3):** +- `downloadMessage(messageId:)` — return `Signal` → `Signal`. Return-side wrap via `|> map { $0.flatMap(EngineMessage.init) }`. +- `topPeerActiveLiveLocationMessages(peerId:)` — return `Signal<(Peer?, [Message]), NoError>` → `Signal<(EnginePeer?, [EngineMessage]), NoError>`. Return-side tuple wrap. +- `getSynchronizeAutosaveItemOperations()` — **deleted**. Dead facade: sole caller (`StoreDownloadedMedia.swift`) already bypassed it by calling `_internal_getSynchronizeAutosaveItemOperations` directly inside its own transaction block. + +**Peers (1):** +- `updatedRemotePeer(peer:)` — return `Signal` → `Signal`. `PeerReference` param kept as-is (no `EnginePeer.Reference` alias today). The sole call site in `ChannelAdminsController.swift` uses `ignoreValues`, so no caller change was needed. + +**Resources (4):** +- `renderStorageUsageStatsMessages(…existingMessages:)` — `[EngineMessage.Id: Message]` → `[EngineMessage.Id: EngineMessage]` on both sides. Facade unwraps input via `.mapValues { $0._asMessage() }`, wraps output via `.mapValues(EngineMessage.init)`. +- `clearStorage(peerId:categories:includeMessages:excludeMessages:)` — `[Message]` → `[EngineMessage]`. Facade unwraps via `.map { $0._asMessage() }`. +- `clearStorage(peerIds:includeMessages:excludeMessages:)` — same shape. +- `clearStorage(messages:)` — same shape. No external callers; migrated for overload-set consistency. + +**Consumer call-site updates** (5 files): +- `ChatListUI/Sources/ChatListSearchListPaneNode.swift`: dropped now-redundant `.flatMap(EngineMessage.init)` wrap at the `downloadMessage` call site. +- `LocationUI/Sources/LocationViewControllerNode.swift`: dropped now-redundant `.map(EngineMessage.init)` at the `topPeerActiveLiveLocationMessages` call site. +- `LiveLocationManager/Sources/LiveLocationSummaryManager.swift`: dropped redundant `EnginePeer(author)` / `EngineMessage(message)` construction (`author`, `message` are now already `EnginePeer` / `EngineMessage`). +- `TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift`: bridged at 4 facade-call points (1 `renderStorageUsageStatsMessages`, 2 `clearStorage` overloads with message arrays; the `includeMessages: [], excludeMessages: []` site at line 3038 needed no change as empty arrays infer to `[EngineMessage]` just as well). + +**Minimal-scope bridging.** `StorageUsageScreen.swift` still has 43 raw `Message`/`MessageId` references inside its `AggregatedData` helper class and surrounding logic — not touched in this wave. A future "StorageUsageScreen full de-Postbox" wave would drop those (migrate `AggregatedData.messages: [MessageId: Message]` → `[EngineMessage.Id: EngineMessage]`, `clearIncludeMessages: [Message]` → `[EngineMessage]`, etc.) and potentially drop `import Postbox`. Out of scope here. + +**No modules became Postbox-free in this wave** — all five touched consumer files still import Postbox for reasons unrelated to the migrated facades. + +Plan / record: `docs/superpowers/plans/2026-04-20-postbox-to-telegramengine-wave-7.md`. + +After this wave, the "Known future-wave candidates" list contains only the 4 permanently-blocked classes conforming to `TelegramMediaResource`. The full public `TelegramEngine.*` facade surface is now engine-typed (modulo those four types). + ### Modules currently free of `import Postbox` (running tally) Consumer modules that no longer import Postbox, across all waves and standalone commits: @@ -198,6 +232,8 @@ Surfaced by the wave-2 final review: - Classes conforming to `TelegramMediaResource` (need `isEqual(to: MediaResource)` override) remain **permanently blocked** from consumer-side migration: `ICloudFileResource`, `InstantPageExternalMediaResource`, `VideoLibraryMediaResource`, `YoutubeEmbedStoryboardMediaResource`. Either move the class into `TelegramCore` or keep `import Postbox` in its module. +(The seven `TelegramEngine.*` facade leaks surfaced by the 2026-04-20 post-wave-6 scouting pass — `downloadMessage`, `topPeerActiveLiveLocationMessages`, `getSynchronizeAutosaveItemOperations`, `updatedRemotePeer`, `renderStorageUsageStatsMessages`, and three `clearStorage` overloads — landed in wave 7; see "Wave 7 outcome" above.) + ### Build environment quirk The build needs `TELEGRAM_CODESIGNING_GIT_PASSWORD` in the environment. It is set in `~/.zshrc` but Claude Code's bash tool does NOT source shell config by default. Prefix build commands with `source ~/.zshrc 2>/dev/null;` to pick it up. \ No newline at end of file diff --git a/docs/superpowers/plans/2026-04-20-postbox-to-telegramengine-wave-7.md b/docs/superpowers/plans/2026-04-20-postbox-to-telegramengine-wave-7.md new file mode 100644 index 0000000000..b5bf1f0f57 --- /dev/null +++ b/docs/superpowers/plans/2026-04-20-postbox-to-telegramengine-wave-7.md @@ -0,0 +1,95 @@ +# Postbox → TelegramEngine Wave 7 Implementation Plan + +> **For agentic workers:** This plan was executed in a single session; steps below are a post-hoc record of the work landed, not a to-do list. + +**Goal:** Close out the remaining raw-Postbox leaks in `TelegramEngine.*` public facades surfaced by the wave-6 post-sweep scouting pass (2026-04-20). Six facade-signature migrations + one dead-facade deletion + consumer call-site bridging, landed as a single wave commit. + +**Architecture:** Wave-2 shape scaled to seven facades at once: each facade signature changes in place from raw Postbox domain types (`Message`, `Peer`) to engine equivalents (`EngineMessage`, `EnginePeer`), with `_internal_*` implementations left raw per the standing "internal Postbox-facing stays raw" rule. Consumer call sites bridge at the facade boundary via `EngineMessage.init` / `._asMessage()` wrap/unwrap helpers or drop now-redundant wrapping. + +**Tech Stack:** Swift / Bazel. No unit tests by repo policy — verification is a full project build. + +**Build command:** + +```bash +source ~/.zshrc 2>/dev/null; PATH=/opt/homebrew/opt/ruby/bin:`gem environment gemdir`/bin:$PATH python3 build-system/Make/Make.py --overrideXcodeVersion --cacheDir ~/telegram-bazel-cache build --configurationPath build-system/appstore-configuration.json --gitCodesigningRepository git@gitlab.com:peter-iakovlev/fastlanematch.git --gitCodesigningType development --gitCodesigningUseCurrent --buildNumber 1 --configuration debug_sim_arm64 --continueOnError +``` + +--- + +## Scope — candidate list + +All seven items from the wave-6 post-sweep scouting pass: + +1. `TelegramEngine.Messages.downloadMessage(messageId: MessageId) -> Signal` → `(messageId: EngineMessage.Id) -> Signal`. Callers: 1 (`ChatListSearchListPaneNode`). +2. `TelegramEngine.Messages.topPeerActiveLiveLocationMessages(peerId: PeerId) -> Signal<(Peer?, [Message]), NoError>` → `(peerId: EnginePeer.Id) -> Signal<(EnginePeer?, [EngineMessage]), NoError>`. Callers: 2 (`LocationViewControllerNode`, `LiveLocationSummaryManager`). +3. `TelegramEngine.Messages.getSynchronizeAutosaveItemOperations()` — dead facade (sole caller `StoreDownloadedMedia.swift:298` uses `_internal_*` directly). Deleted. +4. `TelegramEngine.Peers.updatedRemotePeer(peer: PeerReference) -> Signal` → `Signal`. `PeerReference` param kept (no `EnginePeer.Reference` alias today). Callers: 1 (`ChannelAdminsController`, `ignoreValues` so no caller change needed). +5. `TelegramEngine.Resources.renderStorageUsageStatsMessages(…existingMessages: [EngineMessage.Id: Message]) -> Signal<[EngineMessage.Id: Message], NoError>` → `[EngineMessage.Id: EngineMessage]` on both sides. Callers: 1 (`StorageUsageScreen`). +6–8. `TelegramEngine.Resources.clearStorage(...)` overloads (three) — `[Message]` params → `[EngineMessage]`. Real external callers: 2 (`StorageUsageScreen`, two overloads). The third overload `clearStorage(messages:)` has no callers; migrated for overload-set consistency. + +--- + +## Tasks + +### Task 1: Migrate three `TelegramEngine.Messages` facades + +**Files:** +- Modify: `submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift` (3 facades) +- Modify: `submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift` (drop redundant `.flatMap(EngineMessage.init)`) +- Modify: `submodules/LocationUI/Sources/LocationViewControllerNode.swift` (drop redundant `.map(EngineMessage.init)`) +- Modify: `submodules/LiveLocationManager/Sources/LiveLocationSummaryManager.swift` (drop redundant `EnginePeer(...)` / `EngineMessage(...)` wrappers) + +**Changes:** + +`downloadMessage` — wrap return `Message?` → `EngineMessage?` via `|> map { $0.flatMap(EngineMessage.init) }`. `_internal_downloadMessage` still takes `messageId: MessageId`, which is typealiased to `EngineMessage.Id`, so the param change is purely a rename at the public surface. + +`topPeerActiveLiveLocationMessages` — wrap tuple return via `|> map { peer, messages -> (EnginePeer?, [EngineMessage]) in (peer.flatMap(EnginePeer.init), messages.map(EngineMessage.init)) }`. + +`getSynchronizeAutosaveItemOperations` — deleted. The sole caller `StoreDownloadedMedia.swift:298` was already calling `_internal_getSynchronizeAutosaveItemOperations` directly (inside its own transaction block), so no caller update needed. + +### Task 2: Migrate `TelegramEngine.Peers.updatedRemotePeer` + +**Files:** +- Modify: `submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift` + +Append `|> map(EnginePeer.init)` to wrap the `Peer` result. `PeerReference` param stays. Single call site in `ChannelAdminsController.swift` uses `ignoreValues`, so no caller-side change. + +### Task 3: Migrate four `TelegramEngine.Resources` facades + +**Files:** +- Modify: `submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift` (4 facades) +- Modify: `submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift` (3 call sites) + +`renderStorageUsageStatsMessages` — unwrap `[EngineMessage.Id: EngineMessage]` input via `.mapValues { $0._asMessage() }`, wrap raw result via `.mapValues(EngineMessage.init)`. Caller bridges the other direction at its single call site (`.mapValues(EngineMessage.init)` on the input `existingMessages`, `.mapValues { $0._asMessage() }` on the mapped result). + +`clearStorage(peerId:categories:includeMessages:excludeMessages:)` / `clearStorage(peerIds:includeMessages:excludeMessages:)` / `clearStorage(messages:)` — unwrap `[EngineMessage]` params via `.map { $0._asMessage() }` before forwarding to `_internal_clearStorage`. Callers bridge `[Message]` locals with `.map(EngineMessage.init)` at the facade call site. + +Call-site changes in `StorageUsageScreen` are intentionally minimal: the file's `AggregatedData` type keeps `[MessageId: Message]` / `[Message]` internally, with bridging applied only at the four facade-call points. A full-consumer-module migration to `EngineMessage` is out of scope for this wave (would require changing ~30 sites plus the item types in `StorageFileListPanelComponent`; a future "StorageUsageScreen full de-Postbox" wave could land that). + +### Task 4: Full project build + +Run the build command above with `--continueOnError`. Expected: clean build (no errors or warnings introduced). One full build covers all facades since they're in TelegramCore and rebuilding TelegramCore re-verifies every consumer. + +### Task 5: Commit + +Single wave-7 atomic commit covering the 8 modified files and the CLAUDE.md outcome update. + +--- + +## Outcome (2026-04-20) + +All seven candidates landed. Single atomic commit. Build verified green (`bazel-bin/Telegram/Telegram.ipa` produced; 5854 total actions, 1009 executed). + +- 3 `TelegramEngine.Messages` facades migrated (1 rewrite, 1 rewrite, 1 deletion) +- 1 `TelegramEngine.Peers` facade migrated +- 4 `TelegramEngine.Resources` facades migrated (1 dict, 3 overloads) +- 5 consumer files updated: `ChatListSearchListPaneNode`, `LocationViewControllerNode`, `LiveLocationSummaryManager`, `StorageUsageScreen`, CLAUDE.md + +No modules became Postbox-free in this wave (all five touched consumers still import Postbox for unrelated reasons — `StorageUsageScreen` especially, which still has 43 raw `Message` / `MessageId` references outside the facade boundary). + +**Lesson recorded:** when a facade's consumer file uses the raw Postbox type extensively outside the facade boundary (e.g. `StorageUsageScreen` with its `[MessageId: Message]` dict stored in a helper class and threaded through ~30 sites), bridging at the facade call site is the correct scope. Full-consumer-module migration is its own separate wave, not a side-effect of facade migration. + +**Next-wave candidates.** The sum of the scouting pass's 8 candidates has been closed. No new `TelegramEngine.*` public facades with raw `Postbox`/`Account`/`MediaBox`/`Peer`/`Message`/`MediaResource` leaks remain. Future-wave focus shifts to: + +1. Full-consumer-module migrations (e.g. `StorageUsageScreen` — drop `AggregatedData`'s raw-Postbox storage types, drop `import Postbox`). +2. Another speculative unused-import sweep pass like wave 6, to catch imports that became unused after waves 4–7. diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 9696097f4a..4e526a205e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -2822,9 +2822,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { |> mapToSignal { resolvedUrl -> Signal in if case let .channelMessage(_, messageId, _) = resolvedUrl { return context.engine.messages.downloadMessage(messageId: messageId) - |> map { message -> EngineMessage? in - return message.flatMap(EngineMessage.init) - } } else { return .single(nil) } diff --git a/submodules/LiveLocationManager/Sources/LiveLocationSummaryManager.swift b/submodules/LiveLocationManager/Sources/LiveLocationSummaryManager.swift index ce767dadd3..d5aff40e4f 100644 --- a/submodules/LiveLocationManager/Sources/LiveLocationSummaryManager.swift +++ b/submodules/LiveLocationManager/Sources/LiveLocationSummaryManager.swift @@ -167,7 +167,7 @@ private final class LiveLocationPeerSummaryContext { for message in messages { if let author = message.author { if author.id != strongSelf.accountPeerId && message.flags.contains(.Incoming) { - peersAndMessages.append((EnginePeer(author), EngineMessage(message))) + peersAndMessages.append((author, message)) } } } diff --git a/submodules/LocationUI/Sources/LocationViewControllerNode.swift b/submodules/LocationUI/Sources/LocationViewControllerNode.swift index f8c143d244..2dedaed1a4 100644 --- a/submodules/LocationUI/Sources/LocationViewControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationViewControllerNode.swift @@ -391,7 +391,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan let liveLocations = context.engine.messages.topPeerActiveLiveLocationMessages(peerId: subject.id.peerId) |> map { _, messages -> [EngineMessage] in - return messages.map(EngineMessage.init) + return messages } setupProximityNotificationImpl = { reset in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 3b435c2340..41812db628 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -141,8 +141,11 @@ public extension TelegramEngine { return _internal_searchHashtagPosts(account: self.account, hashtag: hashtag, state: state, limit: limit) } - public func downloadMessage(messageId: MessageId) -> Signal { + public func downloadMessage(messageId: EngineMessage.Id) -> Signal { return _internal_downloadMessage(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, messageId: messageId) + |> map { message -> EngineMessage? in + return message.flatMap(EngineMessage.init) + } } public func searchMessageIdByTimestamp(peerId: PeerId, threadId: Int64?, timestamp: Int32) -> Signal { @@ -461,8 +464,11 @@ public extension TelegramEngine { return _internal_recentlyUsedHashtags(postbox: self.account.postbox) } - public func topPeerActiveLiveLocationMessages(peerId: PeerId) -> Signal<(Peer?, [Message]), NoError> { + public func topPeerActiveLiveLocationMessages(peerId: EnginePeer.Id) -> Signal<(EnginePeer?, [EngineMessage]), NoError> { return _internal_topPeerActiveLiveLocationMessages(viewTracker: self.account.viewTracker, accountPeerId: self.account.peerId, peerId: peerId) + |> map { peer, messages -> (EnginePeer?, [EngineMessage]) in + return (peer.flatMap(EnginePeer.init), messages.map(EngineMessage.init)) + } } public func chatList(group: EngineChatList.Group, count: Int) -> Signal { @@ -931,12 +937,6 @@ public extension TelegramEngine { |> ignoreValues } - public func getSynchronizeAutosaveItemOperations() -> Signal<[(index: Int32, message: Message, mediaId: MediaId)], NoError> { - return self.account.postbox.transaction { transaction -> [(index: Int32, message: Message, mediaId: MediaId)] in - return _internal_getSynchronizeAutosaveItemOperations(transaction: transaction) - } - } - func removeSyncrhonizeAutosaveItemOperations(indices: [Int32]) { let _ = (self.account.postbox.transaction { transaction -> Void in _internal_removeSyncrhonizeAutosaveItemOperations(transaction: transaction, indices: indices) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 9d09c66765..9e64bc8b99 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -251,8 +251,9 @@ public extension TelegramEngine { } } - public func updatedRemotePeer(peer: PeerReference) -> Signal { + public func updatedRemotePeer(peer: PeerReference) -> Signal { return _internal_updatedRemotePeer(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, peer: peer) + |> map(EnginePeer.init) } public func chatOnlineMembers(peerId: PeerId) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift index 442002ffd0..94f6abae21 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift @@ -253,20 +253,24 @@ public extension TelegramEngine { return _internal_collectStorageUsageStats(account: self.account) } - public func renderStorageUsageStatsMessages(stats: StorageUsageStats, categories: [StorageUsageStats.CategoryKey], existingMessages: [EngineMessage.Id: Message]) -> Signal<[EngineMessage.Id: Message], NoError> { - return _internal_renderStorageUsageStatsMessages(account: self.account, stats: stats, categories: categories, existingMessages: existingMessages) + public func renderStorageUsageStatsMessages(stats: StorageUsageStats, categories: [StorageUsageStats.CategoryKey], existingMessages: [EngineMessage.Id: EngineMessage]) -> Signal<[EngineMessage.Id: EngineMessage], NoError> { + let rawExisting = existingMessages.mapValues { $0._asMessage() } + return _internal_renderStorageUsageStatsMessages(account: self.account, stats: stats, categories: categories, existingMessages: rawExisting) + |> map { rawResult -> [EngineMessage.Id: EngineMessage] in + return rawResult.mapValues(EngineMessage.init) + } } - - public func clearStorage(peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal { - return _internal_clearStorage(account: self.account, peerId: peerId, categories: categories, includeMessages: includeMessages, excludeMessages: excludeMessages) + + public func clearStorage(peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [EngineMessage], excludeMessages: [EngineMessage]) -> Signal { + return _internal_clearStorage(account: self.account, peerId: peerId, categories: categories, includeMessages: includeMessages.map { $0._asMessage() }, excludeMessages: excludeMessages.map { $0._asMessage() }) } - - public func clearStorage(peerIds: Set, includeMessages: [Message], excludeMessages: [Message]) -> Signal { - _internal_clearStorage(account: self.account, peerIds: peerIds, includeMessages: includeMessages, excludeMessages: excludeMessages) + + public func clearStorage(peerIds: Set, includeMessages: [EngineMessage], excludeMessages: [EngineMessage]) -> Signal { + _internal_clearStorage(account: self.account, peerIds: peerIds, includeMessages: includeMessages.map { $0._asMessage() }, excludeMessages: excludeMessages.map { $0._asMessage() }) } - - public func clearStorage(messages: [Message]) -> Signal { - _internal_clearStorage(account: self.account, messages: messages) + + public func clearStorage(messages: [EngineMessage]) -> Signal { + _internal_clearStorage(account: self.account, messages: messages.map { $0._asMessage() }) } public func clearCachedMediaResources(mediaResourceIds: Set) -> Signal { diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index a7c10155d9..3acd2a0c2a 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -2394,11 +2394,12 @@ final class StorageUsageScreenComponent: Component { var musicItems: [StorageFileListPanelComponent.Item] = [] } - self.messagesDisposable = (component.context.engine.resources.renderStorageUsageStatsMessages(stats: contextStats, categories: [.files, .photos, .videos, .music], existingMessages: self.aggregatedData?.messages ?? [:]) + self.messagesDisposable = (component.context.engine.resources.renderStorageUsageStatsMessages(stats: contextStats, categories: [.files, .photos, .videos, .music], existingMessages: (self.aggregatedData?.messages ?? [:]).mapValues(EngineMessage.init)) |> deliverOn(Queue()) - |> map { messages -> RenderResult in + |> map { engineMessages -> RenderResult in + let messages = engineMessages.mapValues { $0._asMessage() } let result = RenderResult() - + result.messages = messages var mergedMedia: [MessageId: Int64] = [:] @@ -2953,7 +2954,7 @@ final class StorageUsageScreenComponent: Component { let totalSize = aggregatedData.selectedSize - let _ = (component.context.engine.resources.clearStorage(peerId: component.peer?.id, categories: mappedCategories, includeMessages: aggregatedData.clearIncludeMessages, excludeMessages: aggregatedData.clearExcludeMessages) + let _ = (component.context.engine.resources.clearStorage(peerId: component.peer?.id, categories: mappedCategories, includeMessages: aggregatedData.clearIncludeMessages.map(EngineMessage.init), excludeMessages: aggregatedData.clearExcludeMessages.map(EngineMessage.init)) |> deliverOnMainQueue).start(next: { [weak self] progress in guard let self else { return @@ -3082,7 +3083,7 @@ final class StorageUsageScreenComponent: Component { } } - let _ = (component.context.engine.resources.clearStorage(peerIds: aggregatedData.selectionState.selectedPeers, includeMessages: includeMessages, excludeMessages: excludeMessages) + let _ = (component.context.engine.resources.clearStorage(peerIds: aggregatedData.selectionState.selectedPeers, includeMessages: includeMessages.map(EngineMessage.init), excludeMessages: excludeMessages.map(EngineMessage.init)) |> deliverOnMainQueue).start(next: { [weak self] progress in guard let self else { return