mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Postbox → TelegramEngine refactor: wave 1
Drop direct `import Postbox` from four leaf consumer submodules, routing data access through TelegramEngine/TelegramCore. Behavior-preserving. Consumers migrated: - ChatInterfaceState - ChatSendMessageActionUI - ContactListUI - DrawingUI TelegramCore additions (typealiases + one EngineData item): - EngineMemoryBuffer, EnginePostboxDecoder, EnginePostboxEncoder, EngineAdaptedPostboxDecoder (narrow utility aliases for coding support) - EngineData.Item.Configuration.ContactsSettings Six planned modules were abandoned in wave 1 with reasons recorded in the plan: ActionSheetPeerItem, ChatListSearchRecentPeersNode, DirectMediaImageCache, FetchManagerImpl, GalleryData, ICloudResources. Each either has a public API that leaks `Postbox`/`Account`/`MediaBox` (banned umbrella-type aliases) or cascades into out-of-wave modules. CLAUDE.md now records the wave rules, typealias cheat sheet, and the wave-selection guidance learned from this pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
40112d3cff
commit
ad85ef6517
12 changed files with 182 additions and 61 deletions
56
CLAUDE.md
56
CLAUDE.md
|
|
@ -17,4 +17,58 @@ The app is built using Bazel.
|
|||
- Core launch and application extensions code is in `Telegram/` directory
|
||||
- Most code is organized into libraries in `submodules/`
|
||||
- External code is located in `third-party/`
|
||||
- No tests are used at the moment
|
||||
- No tests are used at the moment
|
||||
|
||||
## Postbox → TelegramEngine refactor (in progress)
|
||||
|
||||
A gradual migration is underway to eliminate direct `import Postbox` from consumer submodules in favor of `TelegramEngine`. See:
|
||||
- Spec: `docs/superpowers/specs/2026-04-16-postbox-to-telegramengine-refactor-wave-1-design.md`
|
||||
- Plan: `docs/superpowers/plans/2026-04-16-postbox-to-telegramengine-refactor-wave-1.md`
|
||||
|
||||
### Rules that apply to every wave
|
||||
|
||||
1. `TelegramCore` does **not** `@_exported import Postbox`. Once a consumer drops `import Postbox`, every remaining Postbox-type reference must use an engine-typealiased equivalent.
|
||||
2. **Never typealias `Postbox`, `Account`, or `MediaBox`.** These umbrella types rename without encapsulating. Narrow utility typealiases (`MemoryBuffer`, `PostboxDecoder`, `PostboxEncoder`, `AdaptedPostboxDecoder`, `MediaResource`, …) remain allowed and expected.
|
||||
3. No new engine wrapper **structs** unless the wave's spec explicitly allows — only typealiases and thin forwarding methods.
|
||||
4. **Discovery first:** before adding any new engine wrapper/typealias, grep `submodules/TelegramCore/Sources/TelegramEngine/` for existing equivalents. Record the search result in the commit message.
|
||||
5. **Abandonment protocol:** if a module can only be refactored by violating rule 2 or by editing a module outside the current wave's list, mark the task Abandoned with a recorded reason. Do NOT substitute a new module mid-wave.
|
||||
6. Full project build per module. No unit tests exist in this project.
|
||||
|
||||
### Engine typealias cheat sheet (existing aliases)
|
||||
|
||||
```
|
||||
PeerId → EnginePeer.Id
|
||||
MessageId → EngineMessage.Id
|
||||
MessageIndex → EngineMessage.Index
|
||||
MessageTags → EngineMessage.Tags
|
||||
MessageAttribute → EngineMessage.Attribute
|
||||
MessageFlags → EngineMessage.Flags
|
||||
MessageForwardInfo → EngineMessage.ForwardInfo
|
||||
MediaId → EngineMedia.Id
|
||||
PreferencesEntry → EnginePreferencesEntry
|
||||
TempBox → EngineTempBox
|
||||
PinnedItemId → EngineChatList.PinnedItem.Id
|
||||
MemoryBuffer → EngineMemoryBuffer (added 2026-04)
|
||||
PostboxDecoder → EnginePostboxDecoder (added 2026-04)
|
||||
PostboxEncoder → EnginePostboxEncoder (added 2026-04)
|
||||
AdaptedPostboxDecoder → EngineAdaptedPostboxDecoder (added 2026-04)
|
||||
```
|
||||
|
||||
For the `MediaResource` Postbox protocol, prefer the TelegramCore subtype `TelegramMediaResource` when the consumer's usage allows (note: `EngineMediaResource` is a wrapper **class**, not a typealias, so it is not interchangeable with the protocol).
|
||||
|
||||
### Wave-selection guidance (learned from wave 1)
|
||||
|
||||
The "leaf module, drop Postbox in isolation" approach only works for modules whose **public API doesn't leak Postbox domain types**. Most candidate leaf modules DO leak such types (`postbox: Postbox` / `account: Account` in public inits, `Media`/`Message` in public function parameters). Those modules need paired caller-migration waves, not isolated refactors.
|
||||
|
||||
Before selecting a wave's module list, grep each candidate for:
|
||||
- `:\s*Postbox\b`, `:\s*Account\b`, `:\s*MediaBox\b` in public signatures → abandon candidate
|
||||
- `Media`/`Message` as public parameter types → likely needs paired wave with callers
|
||||
|
||||
### Wave 1 outcome (2026-04-16)
|
||||
|
||||
4 modules done: `ChatInterfaceState`, `ChatSendMessageActionUI`, `ContactListUI`, `DrawingUI`.
|
||||
6 modules abandoned with recorded reasons in the wave-1 plan: `ActionSheetPeerItem`, `ChatListSearchRecentPeersNode`, `DirectMediaImageCache`, `FetchManagerImpl`, `GalleryData`, `ICloudResources`.
|
||||
|
||||
### 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.
|
||||
|
|
@ -21,6 +21,7 @@ There are no unit tests in this project (`CLAUDE.md`: "No tests are used at the
|
|||
Run from the repo root (`/Users/ali/build/telegram/telegram-ios`):
|
||||
|
||||
```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 \
|
||||
|
|
@ -33,6 +34,8 @@ PATH=/opt/homebrew/opt/ruby/bin:`gem environment gemdir`/bin:$PATH \
|
|||
--configuration debug_sim_arm64
|
||||
```
|
||||
|
||||
(`source ~/.zshrc` picks up `TELEGRAM_CODESIGNING_GIT_PASSWORD` and other env exports that the Claude Code bash shell doesn't inherit by default.)
|
||||
|
||||
It is slow. Do not shortcut it with `bazel build //submodules/X` — the spec chose full build per module.
|
||||
|
||||
### Engine typealias cheat sheet (already in TelegramCore)
|
||||
|
|
@ -99,24 +102,26 @@ When the full build fails after a consumer edit:
|
|||
- Read the **first** compiler error in the build output.
|
||||
- If it's a name-resolution or type error in the module file being refactored, fix the mapping in that file and rebuild.
|
||||
- If it's in a **different** module that depends on the module being refactored, a public signature changed unexpectedly. Either (a) revert that signature change so the public surface stays identical, or (b) if the new surface is genuinely better, extend the fix to the downstream call site **in the same commit**.
|
||||
- If fixing would require editing a module outside the wave-1 list, revert all changes from the current task and skip to the next fallback module listed in "The 10 modules" above. Do not pull extra modules into the wave.
|
||||
- If fixing would require editing a module outside the wave-1 list — or would require aliasing an umbrella type banned by spec rule 2 (`Postbox`, `Account`, `MediaBox`) — revert all changes from the current task and mark the module **Abandoned** in its task body with a one-line reason. Do NOT substitute a different module; the wave's done-count simply goes down by one.
|
||||
|
||||
### The 10 modules (from the spec's deterministic selection rule)
|
||||
|
||||
Reverse-dep count (over the 30-candidate pool) ascending, alphabetical tiebreak. Verified by running the selection script in Task 0:
|
||||
|
||||
1. ActionSheetPeerItem — `submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift`
|
||||
2. ChatInterfaceState — `submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift`
|
||||
3. ChatListSearchRecentPeersNode — `submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift`
|
||||
1. ActionSheetPeerItem — **ABANDONED** (see Task 1 body). Public init takes `postbox: Postbox`; ShareController caller is out-of-wave.
|
||||
2. ChatInterfaceState — `submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift` — DONE
|
||||
3. ChatListSearchRecentPeersNode — **ABANDONED** (see Task 3 body). Public init takes `postbox: Postbox`; ShareController + ChatListUI callers are out-of-wave.
|
||||
4. ChatSendMessageActionUI — `submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift`
|
||||
5. ContactListUI — `submodules/ContactListUI/Sources/ContactListNode.swift`
|
||||
6. DirectMediaImageCache — `submodules/DirectMediaImageCache/Sources/DirectMediaImageCache.swift`
|
||||
6. DirectMediaImageCache — **ABANDONED** (see Task 6 body). Public init takes `account: Account`; six out-of-wave callers.
|
||||
7. DrawingUI — `submodules/DrawingUI/Sources/DrawingScreen.swift`
|
||||
8. FetchManagerImpl — `submodules/FetchManagerImpl/Sources/FetchManagerImpl.swift`
|
||||
9. GalleryData — `submodules/GalleryData/Sources/GalleryData.swift`
|
||||
10. ICloudResources — `submodules/ICloudResources/Sources/ICloudResources.swift`
|
||||
8. FetchManagerImpl — **ABANDONED** (see Task 8 body). Public init takes `postbox: Postbox`; TelegramUI caller is out-of-wave.
|
||||
9. GalleryData — **ABANDONED** (see Task 9 body). Four public functions take `Media`/`Message` as parameters; refactor cascades into many out-of-wave downstream types (`AvatarGalleryEntry`, `MessageReference`, etc.). Good candidate for a bespoke future wave that migrates the domain types together.
|
||||
10. ICloudResources — **ABANDONED** (see Task 10 body). Class conforms to `TelegramMediaResource` and inherits `isEqual(to: MediaResource)`; overriding that without aliasing the `MediaResource` protocol isn't possible.
|
||||
|
||||
If one of these hits a blocker (see spec §Risks), skip it, flag it in commit history/plan notes, and move to the next single-import candidate in the same sort order: HorizontalPeerItem, PhotoResources, PromptUI, SelectablePeerNode, TelegramIntents, ItemListAvatarAndNameInfoItem, InAppPurchaseManager, InstantPageCache, InviteLinksUI, ItemListStickerPackItem, MapResourceToAvatarSizes, PlatformRestrictionMatching, SaveToCameraRoll, ShareItems, SoftwareVideo, StickerPeekUI. The goal is 10 completed modules.
|
||||
**Wave-1 done-count: 4** (Tasks 2, 4, 5, 7 done; Tasks 1, 3, 6, 8, 9, 10 abandoned).
|
||||
|
||||
Per the spec's **abandonment protocol**, if a module hits an unresolvable blocker (requires aliasing an umbrella type such as `Postbox`/`Account`/`MediaBox`, or requires editing a module outside the wave-1 list), it is marked Abandoned in its task body and **not substituted**. The wave's done-count goes down by one; fallback modules are not pulled into the wave mid-execution. A later wave can revisit the abandoned module with tools not available in wave 1 (e.g. a real engine wrapper rather than a typealias, or a refactor that migrates the caller first).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -160,7 +165,13 @@ Task 0 produces no code changes.
|
|||
|
||||
---
|
||||
|
||||
## Task 1: Refactor `ActionSheetPeerItem`
|
||||
## Task 1: Refactor `ActionSheetPeerItem` — **ABANDONED**
|
||||
|
||||
**Status:** Abandoned for wave 1. No code changes in this repo from this task.
|
||||
|
||||
**Reason:** Refactoring this module requires either (a) typealiasing the `Postbox` class itself (banned — see spec §Guiding rules rule 2: umbrella-type typealiases rename without encapsulating) or (b) editing `submodules/ShareController/` which is not in the wave-1 list. The module's designated init takes `postbox: Postbox` as a parameter and its sole out-of-wave caller (ShareController) passes `info.account.stateManager.postbox` directly, so there is no path to drop the `import Postbox` here without crossing the wave boundary or violating rule 2. Per the spec's **abandonment protocol**, the module is skipped for this wave. Wave-1 done-count is therefore 9, not 10.
|
||||
|
||||
**Original task body (retained for audit trail, do not implement):**
|
||||
|
||||
**Files:**
|
||||
- Modify: `submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift`
|
||||
|
|
@ -168,7 +179,7 @@ Task 0 produces no code changes.
|
|||
|
||||
**Starting inventory** (computed during planning):
|
||||
|
||||
Grep for common Postbox API/type names in `ActionSheetPeerItem.swift` returned zero hits on `mediaBox`, `transaction`, `PostboxView`, `combinedView`, `PeerId`, `MessageId`, `MediaResource`, `CachedPeerData`, etc. The `import Postbox` line appears unused. Confirm this during inventory — it's the most likely case, but other Postbox symbols (e.g. types referenced inside a parameter type) may still be present.
|
||||
Grep for common Postbox API/type names in `ActionSheetPeerItem.swift` returned zero hits on `mediaBox`, `transaction`, `PostboxView`, `combinedView`, `PeerId`, `MessageId`, `MediaResource`, `CachedPeerData`, etc. The `import Postbox` line appears unused. Confirm this during inventory — it's the most likely case, but other Postbox symbols (e.g. types referenced inside a parameter type) may still be present. (Subsequent inventory discovered the module does take `postbox: Postbox` as a parameter type — this is what makes the module unrefactorable under the wave-1 rules.)
|
||||
|
||||
- [ ] **Step 1: Inventory**
|
||||
|
||||
|
|
@ -328,7 +339,13 @@ EOF
|
|||
|
||||
---
|
||||
|
||||
## Task 3: Refactor `ChatListSearchRecentPeersNode`
|
||||
## Task 3: Refactor `ChatListSearchRecentPeersNode` — **ABANDONED**
|
||||
|
||||
**Status:** Abandoned for wave 1. No code changes in this repo from this task.
|
||||
|
||||
**Reason:** The module's public `init` at line 207 takes `postbox: Postbox` as a parameter. Two out-of-wave callers (`submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift`, `submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift`) use this init. Refactoring requires either typealiasing the `Postbox` class (banned by spec rule 2) or editing those two out-of-wave modules (banned by wave boundary). Per the abandonment protocol, the module is skipped.
|
||||
|
||||
**Original task body (retained for audit trail, do not implement):**
|
||||
|
||||
**Files:**
|
||||
- Modify: `submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift`
|
||||
|
|
@ -565,7 +582,13 @@ EOF
|
|||
|
||||
---
|
||||
|
||||
## Task 6: Refactor `DirectMediaImageCache`
|
||||
## Task 6: Refactor `DirectMediaImageCache` — **ABANDONED**
|
||||
|
||||
**Status:** Abandoned for wave 1. No code changes in this repo from this task.
|
||||
|
||||
**Reason:** The module's public `init(account: Account)` at line 241 takes `account: Account` (an umbrella type banned by spec rule 2). Out-of-wave callers include `submodules/CalendarMessageScreen/`, four TelegramUI components (`StoryContainerScreen`, `ShareWithPeersScreen`, `PeerInfoVisualMediaPaneNode` × 2), and `submodules/TelegramUI/Sources/AccountContext.swift`. Refactoring requires either aliasing `Account` (banned) or editing all those out-of-wave callers (banned). Per the abandonment protocol, the module is skipped.
|
||||
|
||||
**Original task body (retained for audit trail, do not implement):**
|
||||
|
||||
**Files:**
|
||||
- Modify: `submodules/DirectMediaImageCache/Sources/DirectMediaImageCache.swift`
|
||||
|
|
@ -697,7 +720,13 @@ EOF
|
|||
|
||||
---
|
||||
|
||||
## Task 8: Refactor `FetchManagerImpl`
|
||||
## Task 8: Refactor `FetchManagerImpl` — **ABANDONED**
|
||||
|
||||
**Status:** Abandoned for wave 1. No code changes in this repo from this task.
|
||||
|
||||
**Reason:** The module's public `init(postbox: Postbox, storeManager: DownloadedMediaStoreManager?)` at line 708 takes `postbox: Postbox`. Out-of-wave caller: `submodules/TelegramUI/Sources/AccountContext.swift:296`. Refactoring requires either aliasing the `Postbox` class (banned by spec rule 2) or editing TelegramUI (banned by wave boundary). Per the abandonment protocol, the module is skipped.
|
||||
|
||||
**Original task body (retained for audit trail, do not implement):**
|
||||
|
||||
**Files:**
|
||||
- Modify: `submodules/FetchManagerImpl/Sources/FetchManagerImpl.swift`
|
||||
|
|
@ -765,7 +794,13 @@ EOF
|
|||
|
||||
---
|
||||
|
||||
## Task 9: Refactor `GalleryData`
|
||||
## Task 9: Refactor `GalleryData` — **ABANDONED**
|
||||
|
||||
**Status:** Abandoned for wave 1. No code changes in this repo from this task.
|
||||
|
||||
**Reason:** Four public functions take `Media` (Postbox protocol) and/or `Message` (Postbox class) as parameters, called from TelegramUI and ChatListUI (out-of-wave). Refactoring to `EngineMedia` / `EngineMessage` requires `.init(_:)` / `._asMedia()` / `._asMessage()` coercions threaded through many local variables (e.g. `var galleryMedia: Media?` in `chatMessageGalleryControllerData` is reassigned from various `TelegramMedia*` casts and passed to `MessageReference(...)` chains and enum cases), which would cascade into `AvatarGalleryEntry`, `MessageReference`, and other out-of-wave types. The narrow-utility alias path is ruled out because `Media` and especially `Message` are domain types, not utilities. Per the abandonment protocol, the module is skipped.
|
||||
|
||||
**Original task body (retained for audit trail, do not implement):**
|
||||
|
||||
**Files:**
|
||||
- Modify: `submodules/GalleryData/Sources/GalleryData.swift`
|
||||
|
|
@ -827,7 +862,15 @@ EOF
|
|||
|
||||
---
|
||||
|
||||
## Task 10: Refactor `ICloudResources`
|
||||
## Task 10: Refactor `ICloudResources` — **ABANDONED**
|
||||
|
||||
**Status:** Abandoned for wave 1. No code changes in this repo from this task.
|
||||
|
||||
**Reason:** The module declares `public class ICloudFileResource: TelegramMediaResource` and thus must implement `func isEqual(to: MediaResource) -> Bool` (protocol requirement inherited from `MediaResource`). That override's parameter type is fixed at `MediaResource`, which can only be named by importing Postbox or adding a typealias for the raw `MediaResource` protocol. The protocol-alias would be borderline per rule 2; user directed to skip. Per the abandonment protocol, the module is skipped.
|
||||
|
||||
**Original task body (retained for audit trail, do not implement):**
|
||||
|
||||
### Original Task 10
|
||||
|
||||
**Files:**
|
||||
- Modify: `submodules/ICloudResources/Sources/ICloudResources.swift`
|
||||
|
|
|
|||
|
|
@ -18,11 +18,16 @@ This spec covers **wave 1**: the first 10 single-import leaf modules, refactored
|
|||
## Guiding rules
|
||||
|
||||
1. Consumers only. `TelegramCore` does **not** `@_exported import Postbox`, so once a module drops its Postbox import every remaining Postbox-type reference must be switched to the engine-typealiased equivalent (`PeerId` → `EnginePeer.Id`, `MessageId` → `EngineMessage.Id`, `MessageIndex` → `EngineMessage.Index`, `MessageTags` → `EngineMessage.Tags`, `MediaId` → `EngineMedia.Id`, etc.). These aliases are identical to their Postbox originals, so the swap is behavior-preserving.
|
||||
2. Prefer existing `Engine*` wrapper types (`EnginePeer`, `EngineMessage`, `EngineMediaResource`) and engine methods; add new engine wrappers only when a call site clearly needs one.
|
||||
3. Before adding any new engine wrapper, search `submodules/TelegramCore/Sources/TelegramEngine/` for an equivalent by name and shape. Record the search result in the commit that adds the wrapper.
|
||||
4. Bottom-up dependency order across modules.
|
||||
5. Full project build after each module, using the command from the global `CLAUDE.md`.
|
||||
6. A module is done when: no `import Postbox` in its `.swift` files, no `//submodules/Postbox:Postbox` entry in its `BUILD`, full build green, commits landed.
|
||||
2. **Never typealias the `Postbox` class itself** (or other large umbrella API surfaces such as `Account` or `MediaBox`). Aliasing an umbrella type with something like `EnginePostbox = Postbox` renames without encapsulating — the consumer still has access to the full Postbox API through the alias. It defeats the purpose of the refactor. Narrow utility typealiases (`MemoryBuffer`, `PostboxDecoder`, `PostboxEncoder`, `AdaptedPostboxDecoder`, `MediaResource`, etc.) remain allowed and expected; the ban is specifically on aliasing the large facade types.
|
||||
3. Prefer existing `Engine*` wrapper types (`EnginePeer`, `EngineMessage`, `EngineMediaResource`) and engine methods; add new engine wrappers only when a call site clearly needs one.
|
||||
4. Before adding any new engine wrapper, search `submodules/TelegramCore/Sources/TelegramEngine/` for an equivalent by name and shape. Record the search result in the commit that adds the wrapper.
|
||||
5. Bottom-up dependency order across modules.
|
||||
6. Full project build after each module, using the command from the global `CLAUDE.md`.
|
||||
7. A module is done when: no `import Postbox` in its `.swift` files, no `//submodules/Postbox:Postbox` entry in its `BUILD`, full build green, commits landed.
|
||||
|
||||
### Abandonment protocol
|
||||
|
||||
If a module can only be refactored by either (a) typealiasing an umbrella type banned by rule 2, or (b) editing a module outside the wave-1 list, the module is **abandoned for this wave**. Record it in the plan (mark the task Abandoned with the reason) and reduce the wave's done-count accordingly. Do not substitute a new module mid-wave; the wave's scope is fixed at plan time.
|
||||
|
||||
## Wave-1 scope: selecting the 10 modules
|
||||
|
||||
|
|
@ -160,6 +165,6 @@ Before writing any new wrapper, search `submodules/TelegramCore/Sources/Telegram
|
|||
|
||||
## Done definition for this spec
|
||||
|
||||
- 10 leaf modules have zero `import Postbox` in their sources and no `//submodules/Postbox:Postbox` in their `BUILD`.
|
||||
- Every module in the wave-1 list is either **done** (zero `import Postbox` in its sources, no `//submodules/Postbox:Postbox` in its `BUILD`) or explicitly marked **abandoned** with a recorded reason in the plan.
|
||||
- Full project build is green at wave end.
|
||||
- Any new engine wrappers added along the way are documented in their commits.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ swift_library(
|
|||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
|
|
@ -306,8 +305,8 @@ public enum ChatInterfaceMediaDraftState: Codable, Equatable {
|
|||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
let resourceData = try container.decode(AdaptedPostboxDecoder.RawObjectData.self, forKey: "r")
|
||||
self.resource = LocalFileMediaResource(decoder: PostboxDecoder(buffer: MemoryBuffer(data: resourceData.data)))
|
||||
let resourceData = try container.decode(EngineAdaptedPostboxDecoder.RawObjectData.self, forKey: "r")
|
||||
self.resource = LocalFileMediaResource(decoder: EnginePostboxDecoder(buffer: EngineMemoryBuffer(data: resourceData.data)))
|
||||
|
||||
self.fileSize = try container.decode(Int32.self, forKey: "s")
|
||||
|
||||
|
|
@ -333,7 +332,7 @@ public enum ChatInterfaceMediaDraftState: Codable, Equatable {
|
|||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
try container.encode(PostboxEncoder().encodeObjectToRawData(self.resource), forKey: "r")
|
||||
try container.encode(EnginePostboxEncoder().encodeObjectToRawData(self.resource), forKey: "r")
|
||||
try container.encode(self.fileSize, forKey: "s")
|
||||
try container.encode(self.duration, forKey: "dd")
|
||||
try container.encode(self.waveform.samples, forKey: "wd")
|
||||
|
|
@ -499,11 +498,11 @@ public final class ChatInterfaceState: Codable, Equatable {
|
|||
}
|
||||
|
||||
public struct PostSuggestionState: Codable, Equatable {
|
||||
public var editingOriginalMessageId: MessageId?
|
||||
public var editingOriginalMessageId: EngineMessage.Id?
|
||||
public var price: CurrencyAmount?
|
||||
public var timestamp: Int32?
|
||||
|
||||
public init(editingOriginalMessageId: MessageId?, price: CurrencyAmount?, timestamp: Int32?) {
|
||||
public init(editingOriginalMessageId: EngineMessage.Id?, price: CurrencyAmount?, timestamp: Int32?) {
|
||||
self.editingOriginalMessageId = editingOriginalMessageId
|
||||
self.price = price
|
||||
self.timestamp = timestamp
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ swift_library(
|
|||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import TelegramPresentationData
|
|||
import AccountContext
|
||||
import ContextUI
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import TextFormat
|
||||
import ReactionSelectionNode
|
||||
import ViewControllerComponent
|
||||
|
|
@ -970,7 +969,7 @@ final class ChatSendMessageContextScreenComponent: Component {
|
|||
})
|
||||
}
|
||||
|
||||
var customEffectResource: (FileMediaReference, MediaResource)?
|
||||
var customEffectResource: (FileMediaReference, TelegramMediaResource)?
|
||||
if let effectAnimation = messageEffect.effectAnimation?._parse() {
|
||||
customEffectResource = (FileMediaReference.standalone(media: effectAnimation), effectAnimation.resource)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import Display
|
|||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import DeviceAccess
|
||||
|
|
@ -436,7 +435,7 @@ private func contactListNodeEntries(
|
|||
displayOrder: PresentationPersonNameOrder,
|
||||
disabledPeerIds: Set<EnginePeer.Id>,
|
||||
peerRequiresPremiumForMessaging: [EnginePeer.Id: Bool],
|
||||
peersWithStories: [EnginePeer.Id: PeerStoryStats],
|
||||
peersWithStories: [EnginePeer.Id: EngineChatList.StoryStats],
|
||||
authorizationStatus: AccessType,
|
||||
warningSuppressed: (Bool, Bool),
|
||||
displaySortOptions: Bool,
|
||||
|
|
@ -1032,7 +1031,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||
}
|
||||
private var didSetReady = false
|
||||
|
||||
private let contactPeersViewPromise = Promise<(EngineContactList, EnginePeer?, [EnginePeer.Id: Bool], [EnginePeer.Id: PeerStoryStats])>()
|
||||
private let contactPeersViewPromise = Promise<(EngineContactList, EnginePeer?, [EnginePeer.Id: Bool], [EnginePeer.Id: EngineChatList.StoryStats])>()
|
||||
let storySubscriptions = Promise<EngineStorySubscriptions?>(nil)
|
||||
|
||||
private let selectionStatePromise = Promise<ContactListNodeGroupSelectionState?>(nil)
|
||||
|
|
@ -1115,7 +1114,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||
contactsWithPremiumRequired = .single([:])
|
||||
}
|
||||
|
||||
let contactsWithStories: Signal<[EnginePeer.Id: PeerStoryStats], NoError> = self.context.engine.data.subscribe(
|
||||
let contactsWithStories: Signal<[EnginePeer.Id: EngineChatList.StoryStats], NoError> = self.context.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Contacts.List(includePresences: false)
|
||||
)
|
||||
|> map { contacts -> Set<EnginePeer.Id> in
|
||||
|
|
@ -1126,14 +1125,14 @@ public final class ContactListNode: ASDisplayNode {
|
|||
return result
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { peerIds -> Signal<[EnginePeer.Id: PeerStoryStats], NoError> in
|
||||
|> mapToSignal { peerIds -> Signal<[EnginePeer.Id: EngineChatList.StoryStats], NoError> in
|
||||
return context.engine.data.subscribe(
|
||||
EngineDataMap(
|
||||
peerIds.map(TelegramEngine.EngineData.Item.Peer.StoryStats.init(id:))
|
||||
)
|
||||
)
|
||||
|> map { result -> [EnginePeer.Id: PeerStoryStats] in
|
||||
var filtered: [EnginePeer.Id: PeerStoryStats] = [:]
|
||||
|> map { result -> [EnginePeer.Id: EngineChatList.StoryStats] in
|
||||
var filtered: [EnginePeer.Id: EngineChatList.StoryStats] = [:]
|
||||
for (id, value) in result {
|
||||
if let value {
|
||||
filtered[id] = value
|
||||
|
|
@ -1152,7 +1151,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||
contactsWithPremiumRequired,
|
||||
contactsWithStories
|
||||
)
|
||||
|> mapToThrottled { next, contactsWithPremiumRequired, contactsWithStories -> Signal<(EngineContactList, EnginePeer?, [EnginePeer.Id: Bool], [EnginePeer.Id: PeerStoryStats]), NoError> in
|
||||
|> mapToThrottled { next, contactsWithPremiumRequired, contactsWithStories -> Signal<(EngineContactList, EnginePeer?, [EnginePeer.Id: Bool], [EnginePeer.Id: EngineChatList.StoryStats]), NoError> in
|
||||
return .single((next.0, next.1, contactsWithPremiumRequired, contactsWithStories))
|
||||
|> then(
|
||||
.complete()
|
||||
|
|
@ -1165,7 +1164,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||
TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.engine.account.peerId)
|
||||
),
|
||||
contactsWithPremiumRequired, contactsWithStories)
|
||||
|> map { next, contactsWithPremiumRequired, contactsWithStories -> (EngineContactList, EnginePeer?, [EnginePeer.Id: Bool], [EnginePeer.Id: PeerStoryStats]) in
|
||||
|> map { next, contactsWithPremiumRequired, contactsWithStories -> (EngineContactList, EnginePeer?, [EnginePeer.Id: Bool], [EnginePeer.Id: EngineChatList.StoryStats]) in
|
||||
return (next.0, next.1, contactsWithPremiumRequired, contactsWithStories)
|
||||
}
|
||||
|> take(1))
|
||||
|
|
@ -1249,9 +1248,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||
let contactsWarningSuppressed = Promise<(Bool, Bool)>()
|
||||
contactsWarningSuppressed.set(.single((false, false))
|
||||
|> then(
|
||||
combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings]))
|
||||
|> map { noticeView, preferences -> (Bool, Bool) in
|
||||
let settings: ContactsSettings = preferences.values[PreferencesKeys.contactsSettings]?.get(ContactsSettings.self) ?? ContactsSettings.defaultSettings
|
||||
combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.ContactsSettings()))
|
||||
|> map { noticeView, settings -> (Bool, Bool) in
|
||||
let synchronizeDeviceContacts: Bool = settings.synchronizeContacts
|
||||
let suppressed: Bool
|
||||
let timestamp = noticeView.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
|
||||
|
|
@ -1448,23 +1446,23 @@ public final class ContactListNode: ASDisplayNode {
|
|||
|> mapToSignal { query in
|
||||
let foundLocalContacts: Signal<([FoundPeer], [EnginePeer.Id: EnginePeer.Presence]), NoError>
|
||||
if searchChatList {
|
||||
let foundChatListPeers = context.account.postbox.searchPeers(query: query.lowercased())
|
||||
let foundChatListPeers = context.engine.contacts.searchLocalPeers(query: query.lowercased())
|
||||
foundLocalContacts = foundChatListPeers
|
||||
|> mapToSignal { peers -> Signal<([FoundPeer], [EnginePeer.Id: EnginePeer.Presence]), NoError> in
|
||||
var resultPeers: [FoundPeer] = []
|
||||
|
||||
|
||||
for peer in peers {
|
||||
if !displaySavedMessages {
|
||||
if peer.peerId == context.account.peerId {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if searchGroups || searchChannels {
|
||||
let mainPeer = peer.chatMainPeer
|
||||
if let _ = mainPeer as? TelegramUser {
|
||||
} else if let _ = mainPeer as? TelegramGroup {
|
||||
} else if let channel = mainPeer as? TelegramChannel {
|
||||
if case .user = mainPeer {
|
||||
} else if case .legacyGroup = mainPeer {
|
||||
} else if case let .channel(channel) = mainPeer {
|
||||
if case .broadcast = channel.info {
|
||||
if !searchChannels {
|
||||
continue
|
||||
|
|
@ -1481,10 +1479,10 @@ public final class ContactListNode: ASDisplayNode {
|
|||
if let mainPeer = peer.chatMainPeer {
|
||||
var matches = true
|
||||
if let isPeerEnabled = isPeerEnabled {
|
||||
matches = isPeerEnabled(EnginePeer(mainPeer))
|
||||
matches = isPeerEnabled(mainPeer)
|
||||
}
|
||||
if matches {
|
||||
resultPeers.append(FoundPeer(peer: mainPeer, subscribers: nil))
|
||||
resultPeers.append(FoundPeer(peer: mainPeer._asPeer(), subscribers: nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1535,7 +1533,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||
foundDeviceContacts = .single([:])
|
||||
}
|
||||
|
||||
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId)
|
||||
let accountPeer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> take(1)
|
||||
|
||||
struct FoundPeers {
|
||||
|
|
@ -1642,11 +1640,11 @@ public final class ContactListNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
if !excludeSelf && !existingPeerIds.contains(accountPeer.id) {
|
||||
if let accountPeer = accountPeer, !excludeSelf && !existingPeerIds.contains(accountPeer.id) {
|
||||
let lowercasedQuery = query.lowercased()
|
||||
if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
|
||||
existingPeerIds.insert(accountPeer.id)
|
||||
peers.append(.peer(peer: accountPeer, isGlobal: false, participantCount: nil))
|
||||
peers.append(.peer(peer: accountPeer._asPeer(), isGlobal: false, participantCount: nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ swift_library(
|
|||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import Display
|
|||
import ComponentFlow
|
||||
import LegacyComponents
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
|
@ -825,7 +824,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
|||
let tools = self.drawingState.tools
|
||||
let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.drawingSettings, { _ in
|
||||
return PreferencesEntry(DrawingSettings(tools: tools, colors: []))
|
||||
return EnginePreferencesEntry(DrawingSettings(tools: tools, colors: []))
|
||||
})
|
||||
}).start()
|
||||
}
|
||||
|
|
@ -2893,13 +2892,13 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U
|
|||
var stickers: [Any] = []
|
||||
for entity in self.entitiesView.entities {
|
||||
if let sticker = entity as? DrawingStickerEntity, case let .file(file, _) = sticker.content {
|
||||
let coder = PostboxEncoder()
|
||||
let coder = EnginePostboxEncoder()
|
||||
coder.encodeRootObject(file.media)
|
||||
stickers.append(coder.makeData())
|
||||
} else if let text = entity as? DrawingTextEntity, let subEntities = text.renderSubEntities {
|
||||
for sticker in subEntities {
|
||||
if let sticker = sticker as? DrawingStickerEntity, case let .file(file, _) = sticker.content {
|
||||
let coder = PostboxEncoder()
|
||||
let coder = EnginePostboxEncoder()
|
||||
coder.encodeRootObject(file.media)
|
||||
stickers.append(coder.makeData())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -576,6 +576,27 @@ public extension TelegramEngine.EngineData.Item {
|
|||
}
|
||||
}
|
||||
|
||||
public struct ContactsSettings: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public typealias Result = TelegramCore.ContactsSettings
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
var key: PostboxViewKey {
|
||||
return .preferences(keys: Set([PreferencesKeys.contactsSettings]))
|
||||
}
|
||||
|
||||
func extract(view: PostboxView) -> Result {
|
||||
guard let view = view as? PreferencesView else {
|
||||
preconditionFailure()
|
||||
}
|
||||
guard let value = view.values[PreferencesKeys.contactsSettings]?.get(TelegramCore.ContactsSettings.self) else {
|
||||
return TelegramCore.ContactsSettings.defaultSettings
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
public struct EmojiGame: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public typealias Result = EmojiGameInfo
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import Postbox
|
||||
|
||||
public typealias EngineMemoryBuffer = MemoryBuffer
|
||||
public typealias EnginePostboxDecoder = PostboxDecoder
|
||||
public typealias EnginePostboxEncoder = PostboxEncoder
|
||||
public typealias EngineAdaptedPostboxDecoder = AdaptedPostboxDecoder
|
||||
Loading…
Add table
Add a link
Reference in a new issue