diff --git a/CLAUDE.md b/CLAUDE.md index deb6fc4fa9..e3493b3ed0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -669,6 +669,31 @@ Net: 3 consumer files + 1 TelegramCore file + CLAUDE.md. TelegramEngineResources Plan / record: (no plan doc this wave — mechanical sweep). +### Wave 26 outcome (2026-04-21) + +`resourceRangesStatus` + `removeCachedResources` facade additions + consumer sweep. Combines two independent small sweeps into one commit. + +**Two facades added:** +- `resourceRangesStatus(resource: EngineMediaResource) -> Signal, NoError>` wraps the single `(MediaResource) -> Signal, NoError>` overload. Takes `EngineMediaResource` (not `id:`) because Postbox's overload only accepts a resource, not an id — consumers pass `.resource` already. Facade unwraps via `_asResource()`. +- `removeCachedResources(ids: [EngineMediaResource.Id], force: Bool = false, notify: Bool = false) -> Signal` wraps the `([MediaResourceId], force:, notify:) -> Signal` overload. Maps ids internally. + +**`import RangeSet` added to `TelegramEngineResources.swift`.** The `RangeSet` return type caused a name collision with Swift stdlib's `RangeSet` (iOS 18+ only) until the local `RangeSet` module is imported. `TelegramCore/BUILD` already declared the dep at line 23 (`//submodules/Utils/RangeSet:RangeSet`), so no BUILD change needed. + +**4 Shape-A consumer sites migrated (3 files):** +- `PhotoResources/Sources/PhotoResources.swift` (1) +- `TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift` (1) +- `ChatListUI/Sources/ChatListSearchContainerNode.swift` (2) + +For `ChatListSearchContainerNode.swift:1398`, the caller uses a `Set` local — wave leaves the local as-is and maps at the call site via `resourceIds.map { EngineMediaResource.Id($0) }`. Migrating the local to `Set` is out of scope (module keeps `import Postbox` for unrelated reasons). + +**Build validation.** Clean build (563 processes, 265s, 0 errors) on the second attempt after adding `import RangeSet`. + +**Lesson — Swift-stdlib-vs-third-party-module name collisions.** When a facade signature references a type name that exists both in Swift stdlib (potentially availability-restricted) and in a third-party module, the compiler picks the stdlib one by default. Fix: import the third-party module explicitly. In this codebase, `RangeSet` is provided by `submodules/Utils/RangeSet:RangeSet`, and TelegramCore already depends on it. Use `import RangeSet` at the file top. + +Net: 3 consumer files + 1 TelegramCore file + CLAUDE.md. TelegramEngineResources.swift: +9 / -0 (including `import RangeSet`). + +Plan / record: (no plan doc this wave — mechanical sweep). + ### Modules currently free of `import Postbox` (running tally) Consumer modules that no longer import Postbox, across all waves and standalone commits: diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 3cdf4f11b5..99c67bfb19 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1104,7 +1104,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo f(.default) return } - let _ = (strongSelf.context.account.postbox.mediaBox.removeCachedResources([MediaResourceId(downloadResource.id)], notify: true) + let _ = (strongSelf.context.engine.resources.removeCachedResources(ids: [EngineMediaResource.Id(downloadResource.id)], notify: true) |> deliverOnMainQueue).startStandalone(completed: { f(.dismissWithoutContent) }) @@ -1395,7 +1395,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } } - let _ = (strongSelf.context.account.postbox.mediaBox.removeCachedResources(Array(resourceIds), force: true, notify: true) + let _ = (strongSelf.context.engine.resources.removeCachedResources(ids: resourceIds.map { EngineMediaResource.Id($0) }, force: true, notify: true) |> deliverOnMainQueue).startStandalone(completed: { guard let strongSelf = self else { return diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 5d66b24aa6..8b530c5978 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2076,7 +2076,7 @@ public func chatMessagePhotoStatus(context: AccountContext, messageId: MessageId if let range = representationFetchRangeForDisplayAtSize(representation: largestRepresentation, dimension: displayAtSize) { return combineLatest( context.fetchManager.fetchStatus(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: largestRepresentation.resource), - context.account.postbox.mediaBox.resourceRangesStatus(largestRepresentation.resource) + context.engine.resources.resourceRangesStatus(resource: EngineMediaResource(largestRepresentation.resource)) ) |> map { status, rangeStatus -> MediaResourceStatus in if rangeStatus.isSuperset(of: RangeSet(range)) { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift index 98c864da92..656e37a112 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/TelegramEngineResources.swift @@ -2,6 +2,7 @@ import Foundation import SwiftSignalKit import Postbox import TelegramApi +import RangeSet public enum MediaResourceUserContentType: UInt8, Equatable { case other = 0 @@ -484,5 +485,13 @@ public extension TelegramEngine { public func copyResourceData(from: EngineMediaResource.Id, to: EngineMediaResource.Id, synchronous: Bool = false) { self.account.postbox.mediaBox.copyResourceData(from: MediaResourceId(from.stringRepresentation), to: MediaResourceId(to.stringRepresentation), synchronous: synchronous) } + + public func resourceRangesStatus(resource: EngineMediaResource) -> Signal, NoError> { + return self.account.postbox.mediaBox.resourceRangesStatus(resource._asResource()) + } + + public func removeCachedResources(ids: [EngineMediaResource.Id], force: Bool = false, notify: Bool = false) -> Signal { + return self.account.postbox.mediaBox.removeCachedResources(ids.map { MediaResourceId($0.stringRepresentation) }, force: force, notify: notify) + } } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 9bcaa14158..012e74750b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -2091,7 +2091,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine } statusSignals.append( - context.account.postbox.mediaBox.resourceRangesStatus(file.resource) + context.engine.resources.resourceRangesStatus(resource: EngineMediaResource(file.resource)) |> filter { ranges in if let fetchRange { return ranges.isSuperset(of: RangeSet(fetchRange.0))